Skip to main content

AI & workflows

Tree code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/tree.blade.php

@props([
    'items' => null,
])

@php
    /*
    | Tree view — a nested, expandable hierarchy for file trees, nav or any
    | parent/child structure. Drive it with a nested `items` array, or compose
    | <x-ui.tree.item> children by hand.
    |
    | Each node is an array with keys:
    |   - label    (string, required)
    |   - icon     (?string) Phosphor icon; defaults to folder / file
    |   - href     (?string) render the leaf row as a link
    |   - expanded (?bool)   start the branch open
    |   - children (array)   child nodes, same shape
    |
    | Usage (array-driven), where $tree is the nested array described above:
    |   <x-ui.tree :items="$tree" />
    |
    | Usage (composed by hand):
    |   <x-ui.tree>
    |       <x-ui.tree.item label="src" :expanded="true">
    |           <x-ui.tree.item label="index.js" icon="file-js" />
    |       </x-ui.tree.item>
    |   </x-ui.tree>
    */
@endphp

<ul role="tree" {{ $attributes->merge(['class' => 'text-sm text-gray-700 dark:text-gray-200 select-none']) }}>
    @if (is_array($items))
        @foreach ($items as $node)
            <x-ui.tree.item
                :label="$node['label'] ?? ''"
                :icon="$node['icon'] ?? null"
                :href="$node['href'] ?? null"
                :expanded="$node['expanded'] ?? false"
                :items="$node['children'] ?? null"
            />
        @endforeach
    @else
        {{ $slot }}
    @endif
</ul>

resources/views/components/ui/tree/item.blade.php

@props([
    'label' => '',
    'icon' => null,
    'href' => null,
    'expanded' => false,
    'items' => null,
])

@php
    /*
    | A node inside <x-ui.tree>. A node is a "branch" when it has children
    | (either an `items` array or slot content) and a "leaf" otherwise. Branches
    | get a chevron + folder icon and toggle their children; leaves get a file
    | icon and may link via `href`.
    |
    | Usage:
    |   <x-ui.tree.item label="resources" :expanded="true">
    |       <x-ui.tree.item label="app.css" icon="file-css" href="#" />
    |   </x-ui.tree.item>
    */
    $hasArrayChildren = is_array($items) && count($items) > 0;
    $isBranch = $hasArrayChildren || trim($slot) !== '';

    $defaultIcon = $isBranch ? 'folder' : 'file';
    $iconName = $icon ?? $defaultIcon;

    $rowClasses = 'group flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left transition hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer';
@endphp

<li role="treeitem" @if ($isBranch) x-data="{ expanded: @js((bool) $expanded), toggle() { this.expanded = ! this.expanded } }" ::aria-expanded="expanded" @endif>
    @if ($isBranch)
        <button type="button" @click="toggle()" class="{{ $rowClasses }}">
            <x-ui.icon
                name="caret-right"
                weight="bold"
                size="sm"
                class="shrink-0 text-gray-400 transition-transform duration-150 dark:text-gray-500"
                ::class="expanded ? 'rotate-90' : ''"
            />
            <x-ui.icon
                :name="$iconName"
                weight="fill"
                size="base"
                class="shrink-0 text-brand-500 dark:text-brand-400"
            />
            <span class="truncate font-medium text-gray-800 dark:text-gray-100">{{ $label }}</span>
        </button>

        <ul role="group" x-show="expanded" x-cloak class="ms-3.5 border-s border-gray-200 dark:border-gray-800 ps-2.5">
            @if ($hasArrayChildren)
                @foreach ($items as $child)
                    <x-ui.tree.item
                        :label="$child['label'] ?? ''"
                        :icon="$child['icon'] ?? null"
                        :href="$child['href'] ?? null"
                        :expanded="$child['expanded'] ?? false"
                        :items="$child['children'] ?? null"
                    />
                @endforeach
            @else
                {{ $slot }}
            @endif
        </ul>
    @else
        @if ($href)
            <a href="{{ $href }}" class="{{ $rowClasses }}">
                <span class="w-3.5 shrink-0"></span>
                <x-ui.icon :name="$iconName" size="base" class="shrink-0 text-gray-400 dark:text-gray-500" />
                <span class="truncate text-gray-700 dark:text-gray-200">{{ $label }}</span>
            </a>
        @else
            <div class="{{ $rowClasses }}">
                <span class="w-3.5 shrink-0"></span>
                <x-ui.icon :name="$iconName" size="base" class="shrink-0 text-gray-400 dark:text-gray-500" />
                <span class="truncate text-gray-700 dark:text-gray-200">{{ $label }}</span>
            </div>
        @endif
    @endif
</li>