Morph the rail's FAB, and offer a divider, no fill and a width floor
M3 asks the rail to animate its FAB into an extended FAB as it expands; the documented pattern was two FABs swapped by `display`, so the collapsed one appeared at once while the rail's width was still springing. The header now takes one <x-fab label icon> whose label springs open and shut on the rail's own spring — and names it at both widths, where the collapsed FAB had needed a tooltip to have a name at all. M3 also lists two container treatments as optional that the library had neither of: `divider` draws the vertical divider on the page's edge, which is its own answer to a page scrolling under a fixed rail, and `fill="false"` drops the container colour. Neither reaches a rail open over a scrim. And a `collapsible` rail, whose width is the visitor's choice rather than the window's, is held to the collapsed 96px below `medium`, where M3 asks for a navigation bar instead. Plan: docs/plans/material-3-alignment.md, step 21 (navigation N-22, N-23, N-24). 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
1218371568
commit
7d14f2bd03
@@ -696,8 +696,7 @@ A visitor who has pressed the menu button keeps that choice in both standard ban
|
|||||||
]">
|
]">
|
||||||
<x-slot:brand><a href="{{ route('home') }}" wire:navigate class="type-title-lg">SealShare</a></x-slot:brand>
|
<x-slot:brand><a href="{{ route('home') }}" wire:navigate class="type-title-lg">SealShare</a></x-slot:brand>
|
||||||
<x-slot:rail-header>
|
<x-slot:rail-header>
|
||||||
<span class="rail-collapsed:hidden"><x-fab label="New share" icon="add" link="{{ route('upload') }}" /></span>
|
<x-fab label="New share" icon="add" link="{{ route('upload') }}" />
|
||||||
<span class="hidden rail-collapsed:inline-flex"><x-fab icon="add" tooltip-right="New share" link="{{ route('upload') }}" /></span>
|
|
||||||
</x-slot:rail-header>
|
</x-slot:rail-header>
|
||||||
<x-slot:rail-footer>
|
<x-slot:rail-footer>
|
||||||
<x-navigation-rail-item label="Settings" icon="settings" link="{{ route('settings') }}" :active="request()->routeIs('settings')" />
|
<x-navigation-rail-item label="Settings" icon="settings" link="{{ route('settings') }}" :active="request()->routeIs('settings')" />
|
||||||
@@ -743,7 +742,7 @@ M3 Expressive's navigation rail: collapsed (96px, icon over label) or expanded (
|
|||||||
<div class="flex min-h-dvh">
|
<div class="flex min-h-dvh">
|
||||||
<x-navigation-rail mode="collapsible">
|
<x-navigation-rail mode="collapsible">
|
||||||
<x-slot:brand><span class="type-title-lg">SealShare</span></x-slot:brand>
|
<x-slot:brand><span class="type-title-lg">SealShare</span></x-slot:brand>
|
||||||
<x-slot:header><x-fab icon="add" tooltip-right="New share" /></x-slot:header>
|
<x-slot:header><x-fab label="New share" icon="add" /></x-slot:header>
|
||||||
|
|
||||||
<x-navigation-rail-item label="Shares" icon="folder_shared" link="{{ route('shares.index') }}" active badge="3" />
|
<x-navigation-rail-item label="Shares" icon="folder_shared" link="{{ route('shares.index') }}" active badge="3" />
|
||||||
<x-navigation-rail-section label="Admin">
|
<x-navigation-rail-section label="Admin">
|
||||||
@@ -760,8 +759,9 @@ M3 Expressive's navigation rail: collapsed (96px, icon over label) or expanded (
|
|||||||
```
|
```
|
||||||
|
|
||||||
- `mode`: `collapsed`, `expanded`, `collapsible` (default: expanded until its menu button collapses it; the choice is `$store.rail`, remembered and applied before the first paint), `modal` (collapsed in the layout; the menu button or `$store.rail.show()` opens it expanded over a scrim, focus held until Escape, the scrim or leaving the page), `adaptive` (`<x-app-shell>`'s, one rail per window size class: hidden and opened as a modal on a compact window, collapsed and opened as a modal at `medium`, a standard rail from `expanded` — collapsed there, expanded from `large`).
|
- `mode`: `collapsed`, `expanded`, `collapsible` (default: expanded until its menu button collapses it; the choice is `$store.rail`, remembered and applied before the first paint), `modal` (collapsed in the layout; the menu button or `$store.rail.show()` opens it expanded over a scrim, focus held until Escape, the scrim or leaving the page), `adaptive` (`<x-app-shell>`'s, one rail per window size class: hidden and opened as a modal on a compact window, collapsed and opened as a modal at `medium`, a standard rail from `expanded` — collapsed there, expanded from `large`).
|
||||||
- Props: `label` ("Main"), `width` (expanded width, `16rem`, held between 220 and 360px), `menu` (the menu button; on by default for `collapsible`, `modal`, `adaptive`). Slots: `brand` (beside the menu button, expanded only), `header` (a FAB), the destinations (the only part that scrolls), `footer`. In a flex row the rail sticks to the top of the viewport.
|
- Props: `label` ("Main"), `width` (expanded width, `16rem`, held between 220 and 360px), `menu` (the menu button; on by default for `collapsible`, `modal`, `adaptive`), `divider` (M3's optional vertical divider on the page's side — use it when the page scrolls under a fixed rail), `fill` (`false` for a transparent container, which M3 allows while the items keep 3:1 contrast). Slots: `brand` (beside the menu button, expanded only), `header` (one `<x-fab label icon>`, which the rail morphs into an extended FAB and back as it expands — it also rests at elevation 0, as M3 asks of a nested FAB), the destinations (the only part that scrolls), `footer`. In a flex row the rail sticks to the top of the viewport.
|
||||||
- Anything inside a rail takes both shapes with the `rail-collapsed:` variant, true while that rail is drawn collapsed for whatever reason: `<span class="rail-collapsed:hidden">…expanded only…</span>`, `<span class="hidden rail-collapsed:inline-flex">…collapsed only…</span>`. Put the variant on a wrapper, never on a component. Nothing that shows while collapsed may be wider than 96px.
|
- Anything else inside a rail takes both shapes with the `rail-collapsed:` variant, true while that rail is drawn collapsed for whatever reason: `<span class="rail-collapsed:hidden">…expanded only…</span>`, `<span class="hidden rail-collapsed:inline-flex">…collapsed only…</span>`. Put the variant on a wrapper, never on a component. Nothing that shows while collapsed may be wider than 96px.
|
||||||
|
- A `collapsible` rail is held to the collapsed 96px below `medium` (600px), where M3 says to use a navigation bar rather than a standard rail. `collapsed` and `expanded` are fixed-width by design: wrap one in a `medium:` element if it must not show on a phone.
|
||||||
- `<x-navigation-rail-item>`: the same props as `<x-navigation-bar-item>`. `<x-navigation-rail-section label="…">`: a group with a heading that shows only while the rail is expanded; it names the group for screen readers either way.
|
- `<x-navigation-rail-item>`: the same props as `<x-navigation-bar-item>`. `<x-navigation-rail-section label="…">`: a group with a heading that shows only while the rail is expanded; it names the group for screen readers either way.
|
||||||
- `$store.rail`: `collapsed`, `toggle()`, `collapse()`, `expand()` (the remembered choice; `auto` is true while nothing is stored, so an adaptive rail takes its window size class's default instead, and the first choice clears it), `open`, `show()`, `hide()` (the modal rail; closed on every `wire:navigate`). `config/livewire-material.php` → `rail.default` (`expanded` or `collapsed`) and `rail.storage_key` (`material-rail`).
|
- `$store.rail`: `collapsed`, `toggle()`, `collapse()`, `expand()` (the remembered choice; `auto` is true while nothing is stored, so an adaptive rail takes its window size class's default instead, and the first choice clears it), `open`, `show()`, `hide()` (the modal rail; closed on every `wire:navigate`). `config/livewire-material.php` → `rail.default` (`expanded` or `collapsed`) and `rail.storage_key` (`material-rail`).
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,14 @@
|
|||||||
@slot;
|
@slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Below `medium` a 220–360px rail would be most of the screen, so a rail whose width is the
|
||||||
|
visitor's choice is held collapsed there whatever they chose. */
|
||||||
|
@media (width < 37.5rem) {
|
||||||
|
&:where([data-navigation-rail='collapsible'], [data-navigation-rail='collapsible'] *) {
|
||||||
|
@slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (width < 52.5rem) {
|
@media (width < 52.5rem) {
|
||||||
&:where([data-navigation-rail='adaptive']:not([data-open]), [data-navigation-rail='adaptive']:not([data-open]) *) {
|
&:where([data-navigation-rail='adaptive']:not([data-open]), [data-navigation-rail='adaptive']:not([data-open]) *) {
|
||||||
@slot;
|
@slot;
|
||||||
@@ -215,6 +223,14 @@
|
|||||||
[data-navigation-rail='adaptive'] {
|
[data-navigation-rail='adaptive'] {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Compact → don't use a standard rail, use a navigation bar." A `collapsible` rail takes
|
||||||
|
the width the visitor chose, which on a 360px screen would be two-thirds of it, so it
|
||||||
|
is floored at the collapsed 96px. `collapsed` and `expanded` mean what they say and are
|
||||||
|
left alone: wrap one in a `medium:` element if it must not show on a phone. */
|
||||||
|
[data-navigation-rail='collapsible'] {
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* `--material-banner` is the height of a bar pinned across the top of the window — what
|
/* `--material-banner` is the height of a bar pinned across the top of the window — what
|
||||||
@@ -241,6 +257,18 @@
|
|||||||
background-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);
|
background-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* M3's two optional container treatments: "optional vertical divider separates rail from
|
||||||
|
content, placed on the content-adjacent edge" — also its answer to a page scrolling under a
|
||||||
|
fixed rail — and "container fill can be turned off (transparent) as long as items keep ≥3:1
|
||||||
|
contrast". Neither applies to a rail open over a scrim, which is a surface over the page. */
|
||||||
|
[data-navigation-rail][data-divider]:not([data-open]) > [data-navigation-rail-panel] {
|
||||||
|
border-inline-end: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-navigation-rail][data-fill='false']:not([data-open]) > [data-navigation-rail-panel] {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
/* Open: expanded over a scrim, in surface-container with a large corner at its inner edge. */
|
/* Open: expanded over a scrim, in surface-container with a large corner at its inner edge. */
|
||||||
[data-navigation-rail][data-open] > [data-navigation-rail-panel] {
|
[data-navigation-rail][data-open] > [data-navigation-rail-panel] {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -627,3 +655,31 @@
|
|||||||
[data-navigation-rail-header] [data-fab]:hover {
|
[data-navigation-rail-header] [data-fab]:hover {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Rail transitions from collapsed to expanded should animate the FAB into and out of an extended
|
||||||
|
* FAB" — so the header takes one <x-fab label icon> and morphs it, rather than swapping two by
|
||||||
|
* `display`, which showed the collapsed one at once while the rail's width was still springing.
|
||||||
|
* The label's width springs on the rail's own spatial spring; with the gap closed, what is left is
|
||||||
|
* 16px of padding, the 24px icon and 16px again — the 56px square a collapsed FAB is. The label
|
||||||
|
* stays in the accessibility tree at both widths, so the FAB keeps its name. Unlayered for the
|
||||||
|
* same reason as the rule above: `gap-2` is a utility, and a layer would lose to it. The cap is a
|
||||||
|
* transition needs a length to travel to; a rail FAB's label is a word or two. */
|
||||||
|
[data-navigation-rail-header] [data-fab] > span {
|
||||||
|
max-width: 16rem;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition:
|
||||||
|
max-width var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default),
|
||||||
|
opacity var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default);
|
||||||
|
|
||||||
|
@variant rail-collapsed {
|
||||||
|
max-width: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-navigation-rail-header] [data-fab] {
|
||||||
|
@variant rail-collapsed {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,8 +47,9 @@
|
|||||||
|
|
||||||
Slots, each rendered once: `banner` (a bar across the whole window, above the rail and the
|
Slots, each rendered once: `banner` (a bar across the whole window, above the rail and the
|
||||||
page — M3's scaffold is bars, then rails, then panes), `brand` (beside the rail's menu button
|
page — M3's scaffold is bars, then rails, then panes), `brand` (beside the rail's menu button
|
||||||
while it is expanded), `rail-header` (under it: a FAB — see `<x-navigation-rail>` for its two
|
while it is expanded), `rail-header` (under it: one `<x-fab label="…" icon="…">`, which the
|
||||||
shapes), `rail-footer` (at the foot of the rail: footer destinations, an account), `actions`
|
rail morphs between a FAB and an extended FAB as it opens), `rail-footer` (at the foot of the
|
||||||
|
rail: footer destinations, an account), `actions`
|
||||||
(a row of icon buttons at the very foot, stacked when the rail is collapsed: a theme toggle,
|
(a row of icon buttons at the very foot, stacked when the rail is collapsed: a theme toggle,
|
||||||
sign out), `top` (the page's own bar, above the page and *beside* the rail) and the page
|
sign out), `top` (the page's own bar, above the page and *beside* the rail) and the page
|
||||||
itself. The rail is one element at every width, so what is in it is also in the modal rail a
|
itself. The rail is one element at every width, so what is in it is also in the modal rail a
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<x-navigation-rail mode="collapsible">
|
<x-navigation-rail mode="collapsible">
|
||||||
<x-slot:brand><span class="type-title-lg">Mail</span></x-slot:brand>
|
<x-slot:brand><span class="type-title-lg">Mail</span></x-slot:brand>
|
||||||
<x-slot:header>
|
<x-slot:header>
|
||||||
<x-fab icon="edit" tooltip-right="Compose" />
|
<x-fab label="Compose" icon="edit" />
|
||||||
</x-slot:header>
|
</x-slot:header>
|
||||||
|
|
||||||
<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" active badge="12" />
|
<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" active badge="12" />
|
||||||
@@ -36,22 +36,30 @@
|
|||||||
the same standard rail, expanded to begin with. A visitor who has used the menu button keeps
|
the same standard rail, expanded to begin with. A visitor who has used the menu button keeps
|
||||||
that choice in both standard bands.
|
that choice in both standard bands.
|
||||||
|
|
||||||
Slots: `brand` beside the menu button, only while expanded; `header` under it — a FAB, drawn
|
Slots: `brand` beside the menu button, only while expanded; `header` under it — one
|
||||||
as an extended FAB when expanded (`rail-collapsed:` below); the destinations in the default
|
`<x-fab label="…" icon="…">`, which the rail morphs: the label's width springs open and shut
|
||||||
slot, which alone scroll when the window is too short; `footer`, pinned to the foot. Header and
|
with the rail, so the FAB becomes an extended FAB and back rather than one being swapped for
|
||||||
footer never scroll, so nothing in them is cut off by the scroller's edge. A FAB in the header
|
the other, and its label names it at both widths. It also rests flat, because M3 puts a FAB
|
||||||
rests flat: M3 puts a nested FAB at elevation 0, not the 3 a standalone one has.
|
nested in another component at elevation 0, not the 3 a standalone one has. Then the
|
||||||
|
destinations in the default slot, which alone scroll when the window is too short, and
|
||||||
|
`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
|
Anything else inside can take both shapes with the `rail-collapsed:` variant, which applies
|
||||||
the rail is drawn collapsed for whatever reason:
|
while the rail is drawn collapsed for whatever reason:
|
||||||
`<span class="rail-collapsed:hidden"><x-fab label="Compose" icon="edit" /></span>`
|
`<span class="rail-collapsed:hidden">…only while expanded…</span>`
|
||||||
`<span class="hidden rail-collapsed:inline-flex"><x-fab icon="edit" tooltip-right="Compose" /></span>`.
|
`<span class="hidden rail-collapsed:inline-flex">…only while collapsed…</span>`.
|
||||||
Nothing that shows while collapsed may be wider than 96px.
|
Nothing that shows while collapsed may be wider than 96px.
|
||||||
|
|
||||||
Props: `label` names the landmark ("Main"); `width` is the expanded width (`16rem`, held
|
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`,
|
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
|
`modal` and `adaptive`); `divider` draws M3's optional vertical divider on the edge the page
|
||||||
the top of the viewport, as tall as the viewport at most.
|
is on — which is also what M3 asks for when a page scrolls underneath a fixed rail; `fill`
|
||||||
|
(`false`) drops the container colour for a transparent rail over the page's own background,
|
||||||
|
which M3 allows as long as the items keep a 3:1 contrast against what is behind them. A rail
|
||||||
|
open over a scrim keeps its fill and drops the divider whatever those say: it is a surface
|
||||||
|
over the page then. 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
|
Values from androidx Compose Material 3 (Apache-2.0), androidx-main
|
||||||
27cf9a7d5788aa0f5f2d8b6699ce279560daf326: NavigationRailCollapsedTokens.kt,
|
27cf9a7d5788aa0f5f2d8b6699ce279560daf326: NavigationRailCollapsedTokens.kt,
|
||||||
@@ -67,6 +75,8 @@
|
|||||||
'label' => null,
|
'label' => null,
|
||||||
'width' => '16rem',
|
'width' => '16rem',
|
||||||
'menu' => null,
|
'menu' => null,
|
||||||
|
'divider' => false,
|
||||||
|
'fill' => true,
|
||||||
])
|
])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
@@ -79,6 +89,8 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
data-navigation-rail="{{ $mode }}"
|
data-navigation-rail="{{ $mode }}"
|
||||||
|
@if ($divider) data-divider @endif
|
||||||
|
@unless ($fill) data-fill="false" @endunless
|
||||||
@if ($interactive)
|
@if ($interactive)
|
||||||
x-data="materialNavigationRail('{{ $mode }}')"
|
x-data="materialNavigationRail('{{ $mode }}')"
|
||||||
x-bind:data-open="open"
|
x-bind:data-open="open"
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
'Collapsed and expanded rails' => <<<'BLADE'
|
'Collapsed and expanded rails' => <<<'BLADE'
|
||||||
<div class="flex w-full flex-wrap items-start gap-6">
|
<div class="flex w-full flex-wrap items-start gap-6">
|
||||||
<div class="flex h-[36rem] overflow-hidden rounded-corner-lg border border-outline-variant">
|
<div class="flex h-[36rem] overflow-hidden rounded-corner-lg border border-outline-variant">
|
||||||
<x-navigation-rail mode="collapsed" label="Collapsed example">
|
<x-navigation-rail mode="collapsed" label="Collapsed example" divider>
|
||||||
<x-slot:header>
|
<x-slot:header>
|
||||||
<x-fab icon="edit" tooltip-right="Compose" />
|
<x-fab label="Compose" icon="edit" />
|
||||||
</x-slot:header>
|
</x-slot:header>
|
||||||
|
|
||||||
<x-navigation-rail-item label="Inbox" icon="inbox" link="#navigation" no-wire-navigate active badge="12" />
|
<x-navigation-rail-item label="Inbox" icon="inbox" link="#navigation" no-wire-navigate active badge="12" />
|
||||||
@@ -82,8 +82,7 @@
|
|||||||
</x-slot:brand>
|
</x-slot:brand>
|
||||||
|
|
||||||
<x-slot:header>
|
<x-slot:header>
|
||||||
<span class="rail-collapsed:hidden"><x-fab label="Compose" icon="edit" /></span>
|
<x-fab label="Compose" icon="edit" />
|
||||||
<span class="hidden rail-collapsed:inline-flex"><x-fab icon="edit" tooltip-right="Compose" /></span>
|
|
||||||
</x-slot:header>
|
</x-slot:header>
|
||||||
|
|
||||||
<x-navigation-rail-item label="Inbox" icon="inbox" link="#navigation" no-wire-navigate active badge="12" />
|
<x-navigation-rail-item label="Inbox" icon="inbox" link="#navigation" no-wire-navigate active badge="12" />
|
||||||
@@ -92,7 +91,7 @@
|
|||||||
</x-navigation-rail>
|
</x-navigation-rail>
|
||||||
|
|
||||||
<div class="min-w-0 flex-1 bg-surface-container-low p-6 type-body-md text-on-surface-variant">
|
<div class="min-w-0 flex-1 bg-surface-container-low p-6 type-body-md text-on-surface-variant">
|
||||||
The menu button collapses and expands the rail. The choice is remembered and applied before the next page paints — every collapsible rail follows it, <code><x-app-shell></code>'s from <code>lg</code> too.
|
The menu button collapses and expands the rail, and the FAB morphs into an extended FAB with it. The choice is remembered and applied before the next page paints — every collapsible rail follows it, <code><x-app-shell></code>'s from <code>expanded</code> (840px) too.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
BLADE,
|
BLADE,
|
||||||
@@ -104,7 +103,7 @@
|
|||||||
|
|
||||||
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
||||||
<code><x-navigation-bar></code> and <code><x-navigation-bar-item></code>, <code><x-navigation-rail></code>, <code><x-navigation-rail-item></code> and <code><x-navigation-rail-section></code>,
|
<code><x-navigation-bar></code> and <code><x-navigation-bar-item></code>, <code><x-navigation-rail></code>, <code><x-navigation-rail-item></code> and <code><x-navigation-rail-section></code>,
|
||||||
and <code><x-app-shell></code>, which puts them together: a bar below <code>sm</code>, a collapsed rail that opens as a modal to <code>lg</code>, a collapsible rail from there.
|
and <code><x-app-shell></code>, which puts them together on M3's window size classes: a bar below <code>medium</code> (600px), a collapsed rail that opens as a modal through <code>medium</code>, a standard rail from <code>expanded</code> (840px) and an expanded one from <code>large</code> (1200px).
|
||||||
<a href="{{ route('livewire-material.shell') }}" class="link text-primary">Open the app shell</a> and change the window's width.
|
<a href="{{ route('livewire-material.shell') }}" class="link text-primary">Open the app shell</a> and change the window's width.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,7 @@
|
|||||||
</x-slot:brand>
|
</x-slot:brand>
|
||||||
|
|
||||||
<x-slot:rail-header>
|
<x-slot:rail-header>
|
||||||
<span class="rail-collapsed:hidden"><x-livewire-material::fab label="Compose" icon="edit" x-on:click="materialToast('Compose opens here')" /></span>
|
<x-livewire-material::fab label="Compose" icon="edit" x-on:click="materialToast('Compose opens here')" />
|
||||||
<span class="hidden rail-collapsed:inline-flex"><x-livewire-material::fab icon="edit" tooltip-right="Compose" x-on:click="materialToast('Compose opens here')" /></span>
|
|
||||||
</x-slot:rail-header>
|
</x-slot:rail-header>
|
||||||
|
|
||||||
<x-slot:rail-footer>
|
<x-slot:rail-footer>
|
||||||
|
|||||||
@@ -51,6 +51,22 @@ it('keeps the header and footer out of the scrolling destinations', function ()
|
|||||||
->toMatch('/data-navigation-rail-header.*Brand.*FAB.*data-navigation-rail-destinations.*Inbox.*data-navigation-rail-footer.*Account/s');
|
->toMatch('/data-navigation-rail-header.*Brand.*FAB.*data-navigation-rail-destinations.*Inbox.*data-navigation-rail-footer.*Account/s');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('takes M3\'s optional divider and turns the container fill off', function () {
|
||||||
|
expect((string) $this->blade('<x-navigation-rail mode="expanded" divider :fill="false" />'))
|
||||||
|
->toContain('data-divider')
|
||||||
|
->toContain('data-fill="false"')
|
||||||
|
->and((string) $this->blade('<x-navigation-rail mode="expanded" />'))
|
||||||
|
->not->toContain('data-divider')
|
||||||
|
->not->toContain('data-fill');
|
||||||
|
|
||||||
|
expect(file_get_contents(__DIR__.'/../../../resources/css/components/navigation.css'))
|
||||||
|
// Neither reaches a rail open over a scrim, which is a surface over the page (N-22).
|
||||||
|
->toContain('[data-navigation-rail][data-divider]:not([data-open]) > [data-navigation-rail-panel] {')
|
||||||
|
->toContain("[data-navigation-rail][data-fill='false']:not([data-open]) > [data-navigation-rail-panel] {")
|
||||||
|
// A collapsible rail is held to 96px where M3 asks for a navigation bar instead (N-24).
|
||||||
|
->toMatch("/\\[data-navigation-rail='collapsible'\\] \\{\\s+width: 6rem;/");
|
||||||
|
});
|
||||||
|
|
||||||
it('flattens a FAB nested in the rail header to elevation 0', function () {
|
it('flattens a FAB nested in the rail header to elevation 0', function () {
|
||||||
$html = (string) $this->blade(<<<'BLADE'
|
$html = (string) $this->blade(<<<'BLADE'
|
||||||
<x-navigation-rail mode="expanded">
|
<x-navigation-rail mode="expanded">
|
||||||
@@ -58,12 +74,14 @@ it('flattens a FAB nested in the rail header to elevation 0', function () {
|
|||||||
</x-navigation-rail>
|
</x-navigation-rail>
|
||||||
BLADE);
|
BLADE);
|
||||||
|
|
||||||
// The hook the unlayered rule in navigation.css needs (N-03).
|
// The hook the unlayered rules in navigation.css need (N-03).
|
||||||
expect($html)->toContain('data-fab');
|
expect($html)->toContain('data-fab');
|
||||||
|
|
||||||
expect(file_get_contents(__DIR__.'/../../../resources/css/components/navigation.css'))
|
expect(file_get_contents(__DIR__.'/../../../resources/css/components/navigation.css'))
|
||||||
->toContain('[data-navigation-rail-header] [data-fab],')
|
->toContain('[data-navigation-rail-header] [data-fab],')
|
||||||
->toContain('[data-navigation-rail-header] [data-fab]:hover {');
|
->toContain('[data-navigation-rail-header] [data-fab]:hover {')
|
||||||
|
// One FAB whose label springs shut, not two swapped by display (N-23).
|
||||||
|
->toContain('[data-navigation-rail-header] [data-fab] > span {');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws a destination in both shapes, with its count on the icon and at the end', function () {
|
it('draws a destination in both shapes, with its count on the icon and at the end', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user