Let a docked toolbar carry a FAB
Plan step 25 (navigation Missing): M3's toolbar configuration table lists "With FAB" for both toolbars, but the `fab` slot was gated to the floating one. A docked toolbar now takes it at its end, inside the toolbar where the arrow keys reach it; its controls gather at the start, and the FAB rests flat on the bar — elevation 0 for a FAB nested in another component, as Compose's bottomAppBarFabElevation is. 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
9c047a8fb1
commit
c975c5c593
@@ -787,7 +787,7 @@ A collapsing bar needs the window to scroll: no ancestor with `overflow-hidden`/
|
||||
|
||||
### `<x-toolbar>`
|
||||
|
||||
M3 Expressive toolbar, `role="toolbar"` (arrow keys move between controls). `variant`: `floating` (default pill at elevation 3; `vibrant`, `vertical`) or `docked` (full-width surface-container bar). `place`: `bottom` or `end` to fix it over the page; `fab` slot sets a FAB beside a floating toolbar; `label` names it.
|
||||
M3 Expressive toolbar, `role="toolbar"` (arrow keys move between controls). `variant`: `floating` (default pill at elevation 3; `vibrant`, `vertical`) or `docked` (full-width surface-container bar). `place`: `bottom` or `end` to fix it over the page; `fab` slot takes an `<x-fab>` — beside a floating toolbar, or at the end of a docked one, where the controls gather at the start and the FAB rests flat on the bar (M3's elevation 0 for a nested FAB) and the arrow keys reach it; `label` names it.
|
||||
|
||||
```blade
|
||||
<x-toolbar label="Selection" place="bottom" vibrant>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Apache-2.0).
|
||||
*
|
||||
* docked a 64px bar across the bottom of the screen in surface-container, square, its
|
||||
* controls spread out 4 to 32px apart between 16px ends
|
||||
* controls spread out 4 to 32px apart between 16px ends; with a FAB, the controls
|
||||
* at the start and the FAB flat at the end
|
||||
* floating a 64px pill in surface-container (or primary-container, `vibrant`) at elevation 3,
|
||||
* 8px at its ends and 4px between controls; `vertical` stands it on end
|
||||
*
|
||||
@@ -71,6 +72,24 @@
|
||||
color: var(--md-sys-color-primary);
|
||||
}
|
||||
|
||||
/* A docked toolbar with a FAB: the controls at the start, the FAB at the end, resting on the bar
|
||||
at elevation 0 — M3 puts a FAB nested in another component there, and Compose's
|
||||
FloatingActionButtonDefaults.bottomAppBarFabElevation() is 0 in every state. `<x-fab>` draws its
|
||||
shadow with utilities, which is one more reason this file is unlayered. */
|
||||
[data-toolbar][data-variant="docked"]:has(> [data-toolbar-fab]) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[data-toolbar-fab] {
|
||||
display: flex;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
[data-toolbar-fab] [data-fab],
|
||||
[data-toolbar-fab] [data-fab]:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* A floating toolbar and its FAB, side by side (or stacked, vertical). */
|
||||
[data-toolbar-group] {
|
||||
display: inline-flex;
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
`vertical` stands it on end) or `docked` (a full-width bar in surface-container, for the bottom
|
||||
of a screen). `place` puts it over the page: `bottom` (centred above the bottom edge; a docked
|
||||
toolbar spans it) or `end` (centred against the end edge, for a vertical one); without it the
|
||||
toolbar sits where it is written. A `fab` slot sets an `<x-fab>` beside a floating toolbar.
|
||||
`label` names it for screen readers.
|
||||
toolbar sits where it is written. A `fab` slot sets an `<x-fab>` beside a floating toolbar,
|
||||
and at the end of a docked one — M3's "With FAB" configuration for both. On a docked toolbar the
|
||||
controls then gather at the start and the FAB rests on the bar, flat: M3 gives a FAB nested in
|
||||
another component elevation 0 (Compose's bottomAppBarFabElevation is 0 at every state), where
|
||||
one beside a floating toolbar keeps its own. It is one of the toolbar's controls then, so the
|
||||
arrow keys reach it. `label` names it for screen readers.
|
||||
|
||||
A docked toolbar and a navigation bar occupy the same region of the screen and M3 says never to
|
||||
show both at once: the bar belongs on a primary page, the toolbar on a secondary or contextual
|
||||
@@ -29,6 +33,7 @@
|
||||
$vertical = $vertical && $variant === 'floating';
|
||||
$place = in_array($place, ['bottom', 'end'], true) ? $place : null;
|
||||
$grouped = isset($fab) && $variant === 'floating';
|
||||
$docksFab = isset($fab) && $variant === 'docked';
|
||||
@endphp
|
||||
|
||||
@if ($grouped)
|
||||
@@ -48,6 +53,10 @@
|
||||
{{ $attributes }}
|
||||
>
|
||||
{{ $slot }}
|
||||
|
||||
@if ($docksFab)
|
||||
<div data-toolbar-fab>{{ $fab }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($grouped)
|
||||
|
||||
@@ -73,6 +73,16 @@
|
||||
<x-button icon="delete" tooltip="Delete" />
|
||||
</x-toolbar>
|
||||
</div>
|
||||
|
||||
<div class="w-full overflow-hidden rounded-corner-lg border border-outline-variant">
|
||||
<x-toolbar variant="docked" label="Editing">
|
||||
<x-button icon="check_box" tooltip="Select" />
|
||||
<x-button icon="brush" tooltip="Draw" />
|
||||
<x-button icon="mic" tooltip="Record" />
|
||||
<x-button icon="image" tooltip="Add image" />
|
||||
<x-slot:fab><x-fab icon="add" tooltip="New note" color="secondary" /></x-slot:fab>
|
||||
</x-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
BLADE,
|
||||
'Primary tabs' => <<<'BLADE'
|
||||
|
||||
@@ -69,6 +69,27 @@ it('draws floating and docked toolbars', function () {
|
||||
->not->toContain('data-toolbar-group');
|
||||
});
|
||||
|
||||
it('sets a FAB at the end of a docked toolbar, resting flat on it', function () {
|
||||
$html = (string) $this->blade(<<<'BLADE'
|
||||
<x-toolbar variant="docked" label="Editing">
|
||||
<button>Draw</button>
|
||||
<x-slot:fab><button>+</button></x-slot:fab>
|
||||
</x-toolbar>
|
||||
BLADE);
|
||||
|
||||
expect($html)
|
||||
// Inside the toolbar, after its controls, where the arrow keys reach it too.
|
||||
->toMatch('/role="toolbar".*<button>Draw<\/button>\s*<div data-toolbar-fab><button>\+<\/button><\/div>\s*<\/div>/s')
|
||||
->not->toContain('data-toolbar-group')
|
||||
->and((string) $this->blade('<x-toolbar variant="docked"><button>Draw</button></x-toolbar>'))
|
||||
->not->toContain('data-toolbar-fab');
|
||||
|
||||
expect(file_get_contents(__DIR__.'/../../../resources/css/components/toolbar.css'))
|
||||
->toMatch('/\[data-toolbar-fab\] \{\s+display: flex;\s+margin-inline-start: auto;/')
|
||||
// A nested FAB rests at elevation 0.
|
||||
->toMatch('/\[data-toolbar-fab\] \[data-fab\],\s+\[data-toolbar-fab\] \[data-fab\]:hover \{\s+box-shadow: none;/');
|
||||
});
|
||||
|
||||
it('centres a headline on a three-column row and curves the search container', function () {
|
||||
$css = file_get_contents(__DIR__.'/../../../resources/css/components/app-bar.css');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user