{{-- M3's tabs: the sections of one thing, with the chosen one marked by an active indicator. The bar is rendered on the server from `tabs`, a list of `['name' => …, 'label' => …]` with an optional `icon`, `badge` and `disabled` — so it arrives with the page, never a moment after it. The panels are `` in the slot; every panel is rendered, so switching never waits on the server, and each arrives with its id and with every panel but the chosen one already hidden, so nothing flashes and no screen reader meets four panels at once (N-05). The chosen tab is the Livewire property in `wire:model` (entangled; `wire:model.live` tells the server at once), or, without it, `selected` (the first tab by default) and `x-model`. The bar is a real tablist (resources/js/tabs.js). `variant` is `primary` (the default: the indicator under the label, in primary) or `secondary` (under the whole tab, for tabs inside a section); `stacked` puts a primary tab's icon over its label (64px); `scrollable` lets tabs keep their own width and scroll sideways instead of sharing the width equally, offset 52dp from the leading edge so it reads as scrollable (N-10, resources/css/components/tabs.css). A fixed tab's label too long for its share wraps to a second line and truncates there, the whole bar growing to hold it; a scrollable tab keeps its label on one line (M3: "may wrap to a max second line if needed with truncation, or use scrollable tabs to give longer titles more room"). The root renders `data-md-tabs`; the bar is `data-md-tabs-bar` with `data-md-variant`, `data-md-stacked` and `data-md-scrollable`, holding `data-md-tab` buttons (each `data-md-tab-content`, `data-md-tab-label` around the label's `data-md-tab-text` and its badge, and `data-md-tab-indicator`). Imports icon.css and badge.css for what the view renders. --}} @props([ 'tabs' => [], 'variant' => 'primary', 'selected' => null, 'stacked' => false, 'scrollable' => false, ]) @php $model = $attributes->wire('model')->value() ?: null; $variant = $variant === 'secondary' ? 'secondary' : 'primary'; // tab.blade.php repeats these two lines off the component stack, because a slot renders before // the component around it: change them together, or a panel's id stops matching aria-controls. $id = $attributes->get('id') ?? 'tabs-'.substr(md5($model.'|'.implode('|', array_column($tabs, 'name'))), 0, 8); $initial = $selected ?? (collect($tabs)->first(fn (array $tab): bool => ! ($tab['disabled'] ?? false))['name'] ?? null); if ($model !== null && ($component = \Livewire\Livewire::current()) !== null && filled(data_get($component, $model))) { $initial = data_get($component, $model); } @endphp
whereDoesntStartWith('wire:model')->except(['id']) }} >
@foreach ($tabs as $tab) @php($isInitial = (string) $tab['name'] === (string) $initial) @endforeach
{{ $slot }}