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>