Overflow an app bar's trailing actions below medium
Plan step 25, item 7 (navigation Missing: "App bar, trailing-action overflow at small widths"). `<x-app-bar :actions="[...]">` takes the trailing actions as a list whose entries take <x-menu-item>'s props plus any action attribute. M3 allows up to two trailing icon buttons, and up to four on larger screens, and lets trailing actions collapse into an overflow menu at smaller breakpoints: below medium the bar keeps at most two icon buttons, from medium four, the "More options" button counted among them, the rest in its menu. Both forms render and media queries pick one, so there is no script and no flash; each width has its own menu so the arrow keys never reach a hidden row. The actions slot still takes markup and never overflows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
12cdeaaf67
commit
092100c6ae
@@ -6,6 +6,25 @@
|
||||
title is an `<h1>` unless `heading` names another element. Slots: `navigation` (the leading
|
||||
icon button: back, or the menu) and `actions` (trailing icon buttons, an avatar).
|
||||
|
||||
`actions` can instead be a list, `:actions="[['label' => 'Share', 'icon' => 'share',
|
||||
'wire:click' => 'share'], …]"`, in the order of use — M3 puts the most used nearest the
|
||||
headline. Each entry takes what `<x-menu-item>` takes: `label` and `icon`, `link` (with
|
||||
`external` or `no-wire-navigate`), `disabled`, `selected`, and any other key — `wire:click`,
|
||||
`x-on:click` — as an attribute. Every entry is rendered twice, as an icon button named and
|
||||
tooltipped by its label and as a menu item, and the window's width decides which one shows.
|
||||
M3 allows "up to two icon buttons" after the headline, gives a larger screen "up to four
|
||||
trailing icons" (said of the search app bar, the only count it gives for a wider window), and
|
||||
lets actions at the trailing edge "collapse into an overflow menu at smaller breakpoints" and
|
||||
"become visible again at larger sizes" (docs/reference/m3/components-navigation-selection-inputs.md
|
||||
§ Top app bar → Behaviour and guidelines; app-bars/guidelines § Trailing icon buttons, § Large
|
||||
screens, § Resizing). So below `medium` (600px) the bar shows at most two icon buttons and from
|
||||
there at most four, the overflow button — `more_vert`, "More options" — counted among them:
|
||||
with more actions than that, the first one (or three) stay and the rest go into its menu. Each
|
||||
width has a menu of its own holding only what overflows there, so the arrow keys never land on
|
||||
a hidden row, and it is all CSS (resources/css/components/app-bar.css): nothing moves after the
|
||||
page has drawn. Only the list overflows; markup in the slot is drawn as written. A slot and a
|
||||
list are alternatives — a slot of the same name replaces the attribute.
|
||||
|
||||
Sticky at the top by default (`:sticky="false"` for one that scrolls away), under the top safe
|
||||
area; the surface turns surface-container once content scrolls under it
|
||||
(resources/css/components/app-bar.css, resources/js/app-bar.js). A collapsing bar needs the
|
||||
@@ -18,12 +37,36 @@
|
||||
'variant' => 'small',
|
||||
'sticky' => true,
|
||||
'heading' => 'h1',
|
||||
'actions' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$variant = in_array($variant, ['small', 'center', 'medium', 'large', 'search'], true) ? $variant : 'small';
|
||||
$flexible = in_array($variant, ['medium', 'large'], true);
|
||||
$heading = in_array($heading, ['h1', 'h2', 'h3', 'div', 'p'], true) ? $heading : 'h1';
|
||||
|
||||
// The list form of `actions`, each entry split into the props an icon button and a menu item
|
||||
// share and the attributes (its action) both of them carry.
|
||||
$list = is_iterable($actions) ? collect($actions)->values()->map(fn (array $action): array => [
|
||||
'label' => $action['label'] ?? null,
|
||||
'icon' => $action['icon'] ?? null,
|
||||
'link' => $action['link'] ?? null,
|
||||
'external' => (bool) ($action['external'] ?? false),
|
||||
'noWireNavigate' => (bool) ($action['noWireNavigate'] ?? $action['no-wire-navigate'] ?? false),
|
||||
'disabled' => (bool) ($action['disabled'] ?? false),
|
||||
'selected' => $action['selected'] ?? null,
|
||||
'attributes' => new \Illuminate\View\ComponentAttributeBag(\Illuminate\Support\Arr::except($action, [
|
||||
'label', 'icon', 'link', 'external', 'noWireNavigate', 'no-wire-navigate', 'disabled', 'selected',
|
||||
])),
|
||||
])->all() : null;
|
||||
|
||||
// How many stay icon buttons: two trailing icon buttons below `medium`, four from it, the
|
||||
// overflow button one of them whenever it is there.
|
||||
$count = count($list ?? []);
|
||||
$shown = [
|
||||
'compact' => $count > 2 ? 1 : $count,
|
||||
'medium' => $count > 4 ? 3 : $count,
|
||||
];
|
||||
@endphp
|
||||
|
||||
<header
|
||||
@@ -57,9 +100,55 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@isset($actions)
|
||||
@if ($list !== null)
|
||||
@if ($count > 0)
|
||||
<div data-app-bar-trailing>
|
||||
@foreach ($list as $index => $action)
|
||||
@if ($index < $shown['medium'])
|
||||
<span data-app-bar-action @if ($index >= $shown['compact']) data-overflows-compact @endif>
|
||||
<x-livewire-material::button
|
||||
:icon="$action['icon']"
|
||||
:tooltip-bottom="$action['label']"
|
||||
:link="$action['link']"
|
||||
:external="$action['external']"
|
||||
:no-wire-navigate="$action['noWireNavigate']"
|
||||
:disabled="$action['disabled']"
|
||||
:selected="$action['selected']"
|
||||
:attributes="$action['attributes']"
|
||||
/>
|
||||
</span>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@foreach ($shown as $width => $kept)
|
||||
@if ($count > $kept)
|
||||
<span data-app-bar-overflow="{{ $width }}">
|
||||
<x-livewire-material::menu :label="__('More options')" position="bottom-end">
|
||||
<x-slot:trigger>
|
||||
<x-livewire-material::button icon="more_vert" :tooltip-bottom="__('More options')" />
|
||||
</x-slot:trigger>
|
||||
|
||||
@foreach (array_slice($list, $kept) as $action)
|
||||
<x-livewire-material::menu-item
|
||||
:label="$action['label']"
|
||||
:icon="$action['icon']"
|
||||
:link="$action['link']"
|
||||
:external="$action['external']"
|
||||
:no-wire-navigate="$action['noWireNavigate']"
|
||||
:disabled="$action['disabled']"
|
||||
:selected="$action['selected']"
|
||||
:attributes="$action['attributes']"
|
||||
/>
|
||||
@endforeach
|
||||
</x-livewire-material::menu>
|
||||
</span>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@elseif (isset($actions))
|
||||
<div data-app-bar-trailing>{{ $actions }}</div>
|
||||
@endisset
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($flexible && filled($title))
|
||||
|
||||
Reference in New Issue
Block a user