Package views now write <x-livewire-material::button>, so a configured prefix (which Blade spells <x-m::button>) and an application component of the same name can no longer break or shadow them. The showcase rewrites its examples to the configured prefix. Adds the README, and waits for the adaptive rail to hear a resize before the navigation tests press its menu button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
136 lines
7.0 KiB
PHP
136 lines
7.0 KiB
PHP
{{-- M3 Expressive's navigation rail: destinations down the start edge of a medium or wider
|
|
window, collapsed (96px, icon over label) or expanded (icon beside label in a full-width pill).
|
|
|
|
<div class="flex min-h-dvh">
|
|
<x-navigation-rail mode="collapsible">
|
|
<x-slot:brand><span class="type-title-lg">Mail</span></x-slot:brand>
|
|
<x-slot:header>
|
|
<x-fab icon="edit" tooltip-right="Compose" />
|
|
</x-slot:header>
|
|
|
|
<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" active badge="12" />
|
|
<x-navigation-rail-section label="Labels">
|
|
<x-navigation-rail-item label="Travel" icon="label" link="/labels/travel" />
|
|
</x-navigation-rail-section>
|
|
|
|
<x-slot:footer>
|
|
<x-navigation-rail-item label="Settings" icon="settings" link="/settings" />
|
|
</x-slot:footer>
|
|
</x-navigation-rail>
|
|
|
|
<main class="min-w-0 flex-1">…</main>
|
|
</div>
|
|
|
|
`mode` says what decides its width:
|
|
- `collapsed` — always collapsed; `expanded` — always expanded.
|
|
- `collapsible` (the default) — the visitor's choice: expanded until the menu button collapses
|
|
it. The choice is `$store.rail`, remembered in localStorage and applied by <x-theme-script>
|
|
before the first paint (<html data-rail>), so the rail never paints wide and snaps shut.
|
|
- `modal` — collapsed in the layout; the menu button (or `$store.rail.show()` from anywhere)
|
|
opens it expanded over a scrim, holding focus until Escape, the scrim, the menu button or
|
|
leaving the page closes it (Compose's ModalWideNavigationRail).
|
|
- `adaptive` — what `<x-app-shell>` uses: below `sm` nothing until `$store.rail.show()` slides
|
|
it in as a modal; from `sm` collapsed, opening as a modal; from `lg` collapsible.
|
|
|
|
Slots: `brand` beside the menu button, only while expanded; `header` under it — a FAB, drawn
|
|
as an extended FAB when expanded (`rail-collapsed:` below); the destinations in the default
|
|
slot, which alone scroll when the window is too short; `footer`, pinned to the foot. Header and
|
|
footer never scroll, so nothing in them is cut off by the scroller's edge.
|
|
|
|
Anything inside can take both shapes with the `rail-collapsed:` variant, which applies while
|
|
the rail is drawn collapsed for whatever reason:
|
|
`<span class="rail-collapsed:hidden"><x-fab label="Compose" icon="edit" /></span>`
|
|
`<span class="hidden rail-collapsed:inline-flex"><x-fab icon="edit" tooltip-right="Compose" /></span>`.
|
|
Nothing that shows while collapsed may be wider than 96px.
|
|
|
|
Props: `label` names the landmark ("Main"); `width` is the expanded width (`16rem`, held
|
|
between M3's 220 and 360dp); `menu` shows the menu button (by default for `collapsible`,
|
|
`modal` and `adaptive`). The rail does not scroll with the page: in a flex row it sticks to
|
|
the top of the viewport, as tall as the viewport at most.
|
|
|
|
Values from androidx Compose Material 3 (Apache-2.0), androidx-main
|
|
27cf9a7d5788aa0f5f2d8b6699ce279560daf326: NavigationRailCollapsedTokens.kt,
|
|
NavigationRailExpandedTokens.kt, NavigationRailBaselineItemTokens.kt and
|
|
WideNavigationRail.kt under
|
|
https://github.com/androidx/androidx/tree/androidx-main/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3
|
|
— surface (surface-container and elevation 2 with a large inner corner when modal), 44px above
|
|
the header and 40px under it, 4px between collapsed items. The styles are
|
|
resources/css/components/navigation.css; the behaviour resources/js/navigation.js. --}}
|
|
|
|
@props([
|
|
'mode' => 'collapsible',
|
|
'label' => null,
|
|
'width' => '16rem',
|
|
'menu' => null,
|
|
])
|
|
|
|
@php
|
|
$mode = in_array($mode, ['collapsed', 'expanded', 'collapsible', 'modal', 'adaptive'], true) ? $mode : 'collapsible';
|
|
$interactive = in_array($mode, ['collapsible', 'modal', 'adaptive'], true);
|
|
$canOpen = in_array($mode, ['modal', 'adaptive'], true);
|
|
$menu ??= $interactive;
|
|
$collapsedAtFirst = in_array($mode, ['collapsed', 'modal'], true);
|
|
@endphp
|
|
|
|
<div
|
|
data-navigation-rail="{{ $mode }}"
|
|
@if ($interactive)
|
|
x-data="materialNavigationRail('{{ $mode }}')"
|
|
x-bind:data-open="open"
|
|
@endif
|
|
{{ $attributes->merge(['style' => "--navigation-rail-width: {$width}"]) }}
|
|
>
|
|
@if ($canOpen)
|
|
<div data-navigation-rail-scrim aria-hidden="true" x-on:click="$store.rail.hide()"></div>
|
|
@endif
|
|
|
|
<nav
|
|
data-navigation-rail-panel
|
|
aria-label="{{ $label ?? __('Main') }}"
|
|
@if ($canOpen)
|
|
x-trap.inert.noscroll="open"
|
|
x-on:keydown.escape.window="open && $store.rail.hide()"
|
|
@endif
|
|
>
|
|
@if ($menu || isset($brand) || isset($header))
|
|
<div data-navigation-rail-header>
|
|
@if ($menu || isset($brand))
|
|
<div @class(['flex w-full min-w-0 items-center gap-3 pe-5', 'ps-7' => $menu, 'ps-5' => ! $menu])>
|
|
@if ($menu)
|
|
<button
|
|
type="button"
|
|
data-navigation-rail-menu
|
|
aria-label="{{ $collapsedAtFirst ? __('Expand navigation') : __('Collapse navigation') }}"
|
|
aria-expanded="{{ $collapsedAtFirst ? 'false' : 'true' }}"
|
|
x-on:click="menu()"
|
|
x-bind:aria-label="expanded ? @js(__('Collapse navigation')) : @js(__('Expand navigation'))"
|
|
x-bind:aria-expanded="expanded.toString()"
|
|
class="state-layer focus-ring inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded-corner-full text-on-surface-variant after:absolute after:top-1/2 after:left-1/2 after:size-12 after:-translate-x-1/2 after:-translate-y-1/2"
|
|
>
|
|
<span class="contents rail-collapsed:hidden"><x-livewire-material::icon name="menu_open" class="size-6" /></span>
|
|
<span class="hidden rail-collapsed:contents"><x-livewire-material::icon name="menu" class="size-6" /></span>
|
|
</button>
|
|
@endif
|
|
|
|
@isset($brand)
|
|
<div class="min-w-0 flex-1 rail-collapsed:hidden">{{ $brand }}</div>
|
|
@endisset
|
|
</div>
|
|
@endif
|
|
|
|
@isset($header)
|
|
<div {{ $header->attributes->class(['flex w-full flex-col items-start gap-2 px-5']) }}>{{ $header }}</div>
|
|
@endisset
|
|
</div>
|
|
@endif
|
|
|
|
<div data-navigation-rail-destinations>
|
|
{{ $slot }}
|
|
</div>
|
|
|
|
@isset($footer)
|
|
<div data-navigation-rail-footer {{ $footer->attributes }}>{{ $footer }}</div>
|
|
@endisset
|
|
</nav>
|
|
</div>
|