@props([
'name' => 'menu', // input name for JSON
'value' => '[]', // initial JSON (string or array)
'id' => null, // optional DOM id
'maxDepth' => null, // optional nesting limit (null = unlimited)
])

<div {{ $attributes->merge(['class' => 'menu-builder ']) }} x-data="menuBuilder({ initial: @js($value), maxDepth: @js($maxDepth) })" x-init="init()" :id="$id">
    <form action="{{ route('admin.option.menu') }}" method="POST">
        @csrf
        <div class="container py-4">
            <div class="row g-3">
                <!-- Add item panel -->
                <div class="col-md-4">
                    <div class="card h-100">
                        <div class="card-header">{{ __('Add Menu Item') }}</div>
                        <div class="card-body">
                            <div class="mb-3">
                                <label class="form-label">{{ __('Label') }} <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" placeholder="e.g. Home" x-model.trim="newItem.title">
                            </div>
                            <div class="mb-3">
                                <label class="form-label">{{ __('URL') }}</label>
                                <input dir="ltr" type="url" class="form-control" placeholder="https://example.com" x-model.trim="newItem.url">
                            </div>
                            <div class="form-check mb-3">
                                <input class="form-check-input" type="checkbox" id="new-blank" x-model="newItem.blank">
                                <label class="form-check-label" for="new-blank">{{ __('Open in new tab') }} </label>
                            </div>
                            <button type="button" class="btn btn-primary w-100" @click="addItem()" :disabled="!newItem.title">{{ __('Add to menu') }}</button>
                            <hr>
                            <div class="d-flex gap-2">
                                <button type="button" class="btn btn-outline-secondary btn-sm" @click="expandAll()">{{ __('Expand all') }}</button>
                                <button type="button" class="btn btn-outline-secondary btn-sm" @click="collapseAll()">{{ __('Collapse all') }}</button>
                                <button type="button" class="btn btn-outline-danger btn-sm ms-auto" @click="clearAll()" x-show="items.length">{{ __('Clear') }}</button>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Builder panel -->
                <div class="col-md-8">
                    <div class="card">
                        <div class="card-header d-flex align-items-center justify-content-between">
                            <div>
                                <strong>{{ __('Menu Structure') }}</strong>
                                <small class="text-muted ms-2">{{ __('Drag to reorder or nest items') }}</small>
                            </div>
                            <div class="d-flex gap-2 align-items-center">
                                <button type="submit" class="btn btn-success btn-sm">{{ __('Save the menu') }}</button>
                                <button type="button" class="btn btn-success btn-sm" @click="save()">{{ __('Save JSON') }}</button>
                            </div>
                        </div>
                        <div class="card-body">
                            <!-- Root list -->
                            <ol class="" x-ref="rootList" data-sortable>
                                <template x-for="item in items" :key="item.id">
                                    <li class="menu-li" :data-id="item.id" data-type="item">
                                        <div class="menu-item-card d-flex align-items-center gap-2">
                                            <span class="drag-handle" title="Drag to move">⋮⋮</span>
                                            <button type="button" class="btn btn-sm btn-light border" @click="item._open = !item._open" :aria-expanded="item._open">
                                                <span x-text="item._open ? '▾' : '▸'"></span>
                                            </button>
                                            <div class="flex-grow-1">
                                                <div><strong x-text="item.title"></strong> <small class="text-muted" x-text="item.url"></small></div>
                                            </div>
                                            <div class="btn-group btn-group-sm">
                                                <button type="button" class="btn btn-outline-secondary" @click="item._open = true">{{ __('Edit') }}</button>
                                                <button type="button" class="btn btn-outline-secondary" @click="addChild(item.id)">{{ __('Add child') }}</button>
                                                <button type="button" class="btn btn-outline-danger" @click="removeItem(item.id)">{{ __('Delete') }}</button>
                                            </div>
                                        </div>

                                        <!-- Inline editor -->
                                        <div class="border rounded p-2 mt-2 bg-light" x-show="item._open" x-transition>
                                            <div class="row g-2 align-items-center">
                                                <div class="col-md-5">
                                                    <label class="form-label">{{__('Label')}}</label>
                                                    <input type="text" class="form-control form-control-sm" x-model.trim="item.title" @input="save()">
                                                </div>
                                                <div class="col-md-5">
                                                    <label class="form-label">{{__('URL')}}</label>
                                                    <input type="url" class="form-control form-control-sm" x-model.trim="item.url" @input="save()">
                                                </div>
                                                <div class="col-md-2 mt-4">
                                                    <div class="form-check">
                                                        <input class="form-check-input" type="checkbox" :id="'b'+item.id" x-model="item.blank" @change="save()">
                                                        <label class="form-check-label" :for="'b'+item.id">{{ __('Open in new tab') }} </label>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>

                                        <!-- Children list -->
                                        <ol class="ms-4 mt-2" data-sortable>
                                            <template x-for="child in item.children" :key="child.id">
                                                <li class="menu-li" :data-id="child.id" data-type="item">
                                                    <div class="menu-item-card d-flex align-items-center gap-2">
                                                        <span class="drag-handle" title="Drag to move">⋮⋮</span>
                                                        <button type="button" class="btn btn-sm btn-light border" @click="child._open = !child._open" :aria-expanded="child._open">
                                                            <span x-text="child._open ? '▾' : '▸'"></span>
                                                        </button>
                                                        <div class="flex-grow-1">
                                                            <div><strong x-text="child.title"></strong> <small class="text-muted" x-text="child.url"></small></div>
                                                        </div>
                                                        <div class="btn-group btn-group-sm">
                                                            <button type="button" class="btn btn-outline-secondary" @click="child._open = true">{{ __('Edit') }}</button>
                                                            <button type="button" class="btn btn-outline-secondary" @click="addChild(child.id)">{{ __('Add child') }}</button>
                                                            <button type="button" class="btn btn-outline-danger" @click="removeItem(child.id)">{{ __('Delete') }}</button>
                                                        </div>
                                                    </div>

                                                    <!-- Inline editor for child -->
                                                    <div class="border rounded p-2 mt-2 bg-light" x-show="child._open" x-transition>
                                                        <div class="row g-2 align-items-center">
                                                            <div class="col-md-5">
                                                                <label class="form-label">{{ __('Label') }}</label>
                                                                <input type="text" class="form-control form-control-sm" x-model.trim="child.title" @input="save()">
                                                            </div>
                                                            <div class="col-md-5">
                                                                <label class="form-label">{{ __('URL') }}</label>
                                                                <input type="url" class="form-control form-control-sm" x-model.trim="child.url" @input="save()">
                                                            </div>
                                                            <div class="col-md-2 mt-4">
                                                                <div class="form-check">
                                                                    <input class="form-check-input" type="checkbox" :id="'bc'+child.id" x-model="child.blank" @change="save()">
                                                                    <label class="form-check-label" :for="'bc'+child.id">{{ __('Open in new tab') }}</label>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </div>

                                                    <!-- Grand-children list (recursive via template nesting) -->
                                                    <ol class="ms-4 mt-2" data-sortable>
                                                        <template x-for="g in child.children" :key="g.id">
                                                            <li class="menu-li" :data-id="g.id" data-type="item">
                                                                <div class="menu-item-card d-flex align-items-center gap-2">
                                                                    <span class="drag-handle" title="Drag to move">⋮⋮</span>
                                                                    <button type="button" class="btn btn-sm btn-light border" @click="g._open = !g._open" :aria-expanded="g._open">
                                                                        <span x-text="g._open ? '▾' : '▸'"></span>
                                                                    </button>
                                                                    <div class="flex-grow-1">
                                                                        <div><strong x-text="g.title"></strong> <small class="text-muted" x-text="g.url"></small></div>
                                                                    </div>
                                                                    <div class="btn-group btn-group-sm">
                                                                        <button type="button" class="btn btn-outline-secondary" @click="g._open = true">{{ __('Edit') }}</button>
                                                                        <button type="button" class="btn btn-outline-secondary" @click="addChild(g.id)">{{ __('Add child') }}</button>
                                                                        <button type="button" class="btn btn-outline-danger" @click="removeItem(g.id)">{{ __('Delete') }}</button>
                                                                    </div>
                                                                </div>
                                                                <div class="border rounded p-2 mt-2 bg-light" x-show="g._open" x-transition>
                                                                    <div class="row g-2 align-items-center">
                                                                        <div class="col-md-5">
                                                                            <label class="form-label">{{ __('Label') }}</label>
                                                                            <input type="text" class="form-control form-control-sm" x-model.trim="g.title" @input="save()">
                                                                        </div>
                                                                        <div class="col-md-5">
                                                                            <label class="form-label">{{ __('URL') }}</label>
                                                                            <input type="url" class="form-control form-control-sm" x-model.trim="g.url" @input="save()">
                                                                        </div>
                                                                        <div class="col-md-2 mt-4">
                                                                            <div class="form-check">
                                                                                <input class="form-check-input" type="checkbox" :id="'bg'+g.id" x-model="g.blank" @change="save()">
                                                                                <label class="form-check-label" :for="'bg'+g.id">{{ __('Open in new tab') }}</label>
                                                                            </div>
                                                                        </div>
                                                                    </div>
                                                                </div>
                                                                <!-- deeper levels will continue to work via DOM serialization even if not pre-rendered here -->
                                                                <ol class="ms-4 mt-2" data-sortable></ol>
                                                            </li>
                                                        </template>
                                                    </ol>
                                                </li>
                                            </template>
                                        </ol>
                                    </li>
                                </template>
                            </ol>

                            <!-- Hidden field for form submit to Laravel -->
                            <input type="text" hidden name="{{ $name }}" x-model="outputJson">

                            <!-- Preview -->
                            <label class="form-label mt-3 d-none">JSON output</label>
                            <textarea class="form-control d-none" rows="8" x-text="outputJson" readonly></textarea>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

@once
<!-- Optional: Bootstrap 5 CSS for admin (comment out if already loaded globally) -->
{{-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> --}}
<style>
    .drag-handle {
        cursor: grab;
        user-select: none;
        font-size: 1.25rem;
        line-height: 1;
    }

    .sortable-ghost {
        opacity: .6;
    }

    .sortable-chosen {
        background: var(--bs-light);
    }

    .placeholder {
        height: 44px;
        border: 2px dashed var(--bs-secondary);
        border-radius: .5rem;
        margin: .25rem 0;
    }

    ol[data-sortable] {
        list-style: none;
        padding-left: 0;
    }

    ol[data-sortable]>li {
        margin-bottom: .25rem;
    }

    .menu-item-card {
        border: 1px solid var(--bs-border-color, #dee2e6);
        border-radius: .5rem;
        padding: .5rem .75rem;
        background: #fff;
    }
</style>
<!-- SortableJS -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
@endonce

<script>
    function menuBuilder(opts = {}) {
        return {
            // configuration
            maxDepth: opts.maxDepth ?? null,

            // state
            items: [],
            outputJson: '[]',
            newItem: {
                title: '',
                url: '',
                blank: false
            },
            nextId: 1,
            registry: new Map(),

            init() {
                // load initial value if provided
                if (opts.initial) {
                    try {
                        this.loadFromJson(opts.initial);
                    } catch (e) {
                        /* ignore */
                    }
                }
                this.rebuildRegistry();
                this.$nextTick(() => {
                    this.initSortables();
                    this.save();
                });
            },

            addItem(parentId = null) {
                const item = {
                    id: this.nextId++,
                    title: this.newItem.title || 'Untitled',
                    url: this.newItem.url || '',
                    blank: !!this.newItem.blank,
                    children: [],
                    _open: true
                };
                if (parentId === null) {
                    this.items.push(item);
                } else {
                    const t = this.findById(parentId);
                    if (t && t.item) t.item.children.push(item);
                }
                this.newItem = {
                    title: '',
                    url: '',
                    blank: false
                };
                this.rebuildRegistry();
                this.$nextTick(() => {
                    this.initSortables();
                    this.save();
                });
            },
            addChild(parentId) {
                this.addItem(parentId);
            },

            removeItem(id) {
                const removeRec = (arr) => {
                    const idx = arr.findIndex(x => x.id === id);
                    if (idx !== -1) {
                        arr.splice(idx, 1);
                        return true;
                    }
                    for (const it of arr)
                        if (removeRec(it.children)) return true;
                    return false;
                };
                removeRec(this.items);
                this.rebuildRegistry();
                this.$nextTick(() => {
                    this.save();
                });
            },

            expandAll() {
                this.walk(it => it._open = true);
            },
            collapseAll() {
                this.walk(it => it._open = false);
            },
            clearAll() {
                this.items = [];
                this.rebuildRegistry();
                this.save();
            },

            save() {
                this.outputJson = JSON.stringify(this.serialize(), null, 2);
            },

            // helpers
            walk(fn, arr = null) {
                (arr || this.items).forEach(it => {
                    fn(it);
                    this.walk(fn, it.children);
                });
            },
            findById(id, arr = this.items, parent = null) {
                for (let i = 0; i < arr.length; i++) {
                    const it = arr[i];
                    if (it.id === id) return {
                        item: it,
                        parent,
                        index: i
                    };
                    const deeper = this.findById(id, it.children, it);
                    if (deeper) return deeper;
                }
                return null;
            },

            rebuildRegistry() {
                this.registry.clear();
                this.walk(it => {
                    this.registry.set(it.id, {
                        id: it.id,
                        title: it.title,
                        url: it.url,
                        blank: !!it.blank,
                        _open: !!it._open
                    });
                });
            },

            // SortableJS integration
            initSortables() {
                this.$root.querySelectorAll('ol[data-sortable]').forEach((ol) => {
                    if (ol._sortableInited) return;
                    ol._sortableInited = true;
                    new Sortable(ol, {
                        group: {
                            name: 'nested',
                            pull: true,
                            put: true
                        },
                        animation: 150,
                        handle: '.drag-handle',
                        ghostClass: 'sortable-ghost',
                        chosenClass: 'sortable-chosen',
                        dragoverBubble: true,
                        swapThreshold: 0.65,
                        onMove: (evt) => {
                            if (this.maxDepth == null) return true;
                            const el = evt.dragged; // LI being dragged
                            const to = evt.to; // target OL
                            const depth = this.computeDepth(to);
                            // depth counts from 1 at root items
                            return depth <= this.maxDepth;
                        },
                        onEnd: () => {
                            this.applyDomToData();
                        },
                        onAdd: () => {
                            this.applyDomToData();
                        },
                        onUpdate: () => {
                            this.applyDomToData();
                        },
                    });
                });
            },

            computeDepth(ol) {
                let d = 1; // root depth
                let cur = ol;
                while (cur && cur.closest && (cur = cur.closest('li[data-type="item"]'))) {
                    d++;
                    cur = cur.parentElement; // next OL
                    if (!cur) break;
                    cur = cur.closest('ol[data-sortable]');
                }
                return d;
            },

            applyDomToData() {
                const oldRegistry = this.registry;
                const buildFromList = (ol) => {
                    const arr = [];
                    ol.querySelectorAll(':scope > li[data-type="item"]').forEach(li => {
                        const id = parseInt(li.getAttribute('data-id'));
                        const snapshot = oldRegistry.get(id) || {
                            id,
                            title: 'Untitled',
                            url: '',
                            blank: false,
                            _open: false
                        };
                        const childOl = li.querySelector(':scope > ol[data-sortable]');
                        const children = childOl ? buildFromList(childOl) : [];
                        arr.push({
                            id,
                            title: snapshot.title,
                            url: snapshot.url,
                            blank: !!snapshot.blank,
                            children,
                            _open: !!snapshot._open
                        });
                    });
                    return arr;
                };
                const root = this.$refs.rootList;
                this.items = buildFromList(root);
                this.rebuildRegistry();
                this.$nextTick(() => {
                    this.initSortables();
                    this.save();
                });
            },

            serialize() {
                const clone = (arr) => arr.map(it => ({
                    id: it.id,
                    title: it.title,
                    url: it.url,
                    blank: !!it.blank,
                    children: clone(it.children)
                }));
                return clone(this.items);
            },

            loadFromJson(json) {
                const parsed = typeof json === 'string' ? JSON.parse(json) : json;
                const attach = (arr) => arr.map(it => ({
                    id: it.id ?? this.nextId++,
                    title: it.title || 'Untitled',
                    url: it.url || '',
                    blank: !!it.blank,
                    children: attach(it.children || []),
                    _open: false
                }));
                this.items = attach(parsed);
                const collect = (i) => [i.id, ...i.children.flatMap(collect)];
                const maxId = Math.max(0, ...this.items.flatMap(collect));
                this.nextId = maxId + 1;
                this.rebuildRegistry();
                this.$nextTick(() => {
                    this.initSortables();
                    this.save();
                });
            }
        };
    }
</script>

{{--
USAGE

<x-menu-builder name="menu" :value="$menuJson ?? '[]'" :max-depth="3" class="mb-4" />

3) In your controller:
$validated = $request->validate([
  'menu' => ['required','json'],
]);

$tree = json_decode($validated['menu'], true);
// store $tree as JSON or flatten to menu_items table (parent_id, order, title, url, blank)

4) To edit (update action), pass existing JSON to :value. Component loads and serializes automatically.

NOTE:
- Set :max-depth to limit nesting (e.g., 3). Null = unlimited.
--}}
