Flip what the rail on the page draws from $store.rail.toggle()
toggle() was set(!collapsed), and while nothing is stored `collapsed` is only `rail.default`: an adaptive rail between 840 and 1199px is drawn collapsed whatever that says, so an application's shortcut collapsed a collapsed rail and the first press changed nothing. Each interactive rail now registers its root, and toggle() asks the first one on the page, the way its menu button asks itself: an open rail is hidden, a rail with no room to expand (modal, or adaptive below expanded) opens over its scrim without storing anything, and any other rail stores the opposite of what it draws, so a rail that hid itself docks back into the layout as before. With no rail on the page it still flips the choice. collapse() and expand() stay the choice itself. The browser tests toggle the scaffold's rail at 960 and 1512px (in place) and at 768 and 393px (the modal), and a hiding rail back into the layout; the first two fail without the change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
252c649c45
commit
394c9326ac
+10
@@ -1,5 +1,15 @@
|
||||
# Upgrading
|
||||
|
||||
## From 2.1.0 to 2.1.1
|
||||
|
||||
- **`$store.rail.toggle()`** flips what the first rail on the page draws, as its menu button does.
|
||||
With nothing stored, `<x-scaffold>`'s rail is drawn collapsed from 840 to 1199px whatever
|
||||
`rail.default` says, and `toggle()` collapsed it again, so the first press of an application's
|
||||
shortcut changed nothing; it now expands the rail. Below 840px, where there is no room to expand
|
||||
it in the layout, `toggle()` opens and closes the modal rail (`show()`, `hide()`) instead of
|
||||
changing a choice nothing draws there. A shortcut that pressed the rail's menu button to get this
|
||||
can call `$store.rail.toggle()` again.
|
||||
|
||||
## From 2.0.0 to 2.1.0
|
||||
|
||||
- **Browsers:** Firefox 151 or later (was 147), for container style queries on a custom property;
|
||||
|
||||
@@ -1046,7 +1046,7 @@ M3 Expressive's navigation rail: collapsed (96px, icon over label) or expanded (
|
||||
|
||||
- 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 `<x-stack hide-below="medium">` 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.
|
||||
- `$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`, `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`), and `toggle()` — for a keyboard shortcut — which flips what the first rail on the page draws, as its menu button does: the choice where the rail stands in the layout (from `expanded` for `<x-scaffold>`'s, where a collapsed rail with nothing stored expands), the modal where the window leaves it no room. `config/livewire-material.php` → `rail.default` (`expanded` or `collapsed`) and `rail.storage_key` (`material-rail`).
|
||||
|
||||
### `<x-app-bar>`
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* `$store.rail.auto` is true while nothing is stored — the value is `rail.default`, not a choice —
|
||||
* and the adaptive rail then takes its window size class's default instead: collapsed in the
|
||||
* expanded class (840–1199), expanded from `large` (1200), as M3 asks. The first `set()` drops it.
|
||||
* So `collapsed` is not always what is drawn, and `toggle()` asks the rail on the page instead.
|
||||
*
|
||||
* `$store.rail.open` is the modal rail: on a window too narrow for an expanded rail, a menu
|
||||
* button opens it over a scrim (`show()`), and Escape, the scrim or leaving the page closes it
|
||||
@@ -22,6 +23,9 @@
|
||||
*/
|
||||
import { from, upTo } from './breakpoints.js'
|
||||
|
||||
/** The root of every interactive rail on the page once its view is initialised: what `toggle()` asks. */
|
||||
const rails = new Set()
|
||||
|
||||
/*
|
||||
* The active indicator grows out of its centre when a page arrives through wire:navigate. The
|
||||
* new page's indicator is new markup, so the only way to animate it is a starting style — and
|
||||
@@ -61,8 +65,27 @@ document.addEventListener('alpine:init', () => {
|
||||
auto: root.hasAttribute('data-rail-auto'),
|
||||
open: false,
|
||||
|
||||
/**
|
||||
* Flips what the first rail on the page draws: shut when it is open over the page, open
|
||||
* over a scrim where the mode or the window leaves it no room to expand (`show()`,
|
||||
* remembering nothing), and otherwise the choice, from what is drawn — a rail that hides
|
||||
* when collapsed comes back into the layout. Not `set(!collapsed)`: with nothing stored,
|
||||
* an adaptive rail in the expanded class is drawn collapsed whatever `rail.default` says.
|
||||
* With no rail on the page, the choice itself.
|
||||
*/
|
||||
toggle() {
|
||||
this.set(!this.collapsed)
|
||||
const root = [...document.querySelectorAll('[data-md-navigation-rail]')].find((element) => rails.has(element))
|
||||
const rail = root && window.Alpine.$data(root)
|
||||
|
||||
if (!rail) {
|
||||
this.set(!this.collapsed)
|
||||
} else if (rail.open) {
|
||||
this.hide()
|
||||
} else if (rail.cramped) {
|
||||
this.show()
|
||||
} else {
|
||||
this.set(rail.expanded)
|
||||
}
|
||||
},
|
||||
|
||||
collapse() {
|
||||
@@ -120,6 +143,8 @@ document.addEventListener('alpine:init', () => {
|
||||
wasOpen: false,
|
||||
|
||||
init() {
|
||||
rails.add(this.$root)
|
||||
|
||||
if (mode !== 'adaptive') {
|
||||
// Below `medium` a collapsible rail is held at its collapsed width whatever the
|
||||
// visitor chose, so `hide-when-collapsed` does not reach it there.
|
||||
@@ -164,6 +189,7 @@ document.addEventListener('alpine:init', () => {
|
||||
},
|
||||
|
||||
destroy() {
|
||||
rails.delete(this.$root)
|
||||
this.queries.forEach((query, index) => query.removeEventListener('change', this.listeners[index]))
|
||||
},
|
||||
|
||||
@@ -272,8 +298,8 @@ document.addEventListener('alpine:init', () => {
|
||||
} else if (this.modal) {
|
||||
this.$store.rail.open ? this.$store.rail.hide() : this.$store.rail.show()
|
||||
} else {
|
||||
// `set`, not `toggle`: with nothing stored the store's `collapsed` is only
|
||||
// `rail.default`, so the button has to flip what is actually drawn.
|
||||
// `set`, not `toggle`: the button flips what this rail draws, and `toggle()` asks
|
||||
// the first rail on the page.
|
||||
this.$store.rail.set(this.expanded)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -117,6 +117,72 @@ it('starts the rail collapsed across the expanded class and expands it in the la
|
||||
navigationReady($page->refresh())->assertScript(railWidth(256));
|
||||
});
|
||||
|
||||
const TOGGLE_RAIL = "window.eval(\"Alpine.store('rail').toggle()\")";
|
||||
|
||||
it('flips the standing rail from $store.rail.toggle() as drawn, whatever is stored', function () {
|
||||
// The expanded class with nothing stored: `data-rail` says `expanded` (`rail.default`), and
|
||||
// the rail is drawn collapsed all the same — so an application's own shortcut expands it on
|
||||
// the first press, as the rail's menu button does, rather than collapsing a collapsed rail.
|
||||
$page = shellPage(960)
|
||||
->assertScript(railWidth(96))
|
||||
->assertScript("document.documentElement.getAttribute('data-rail') === 'expanded'")
|
||||
->assertScript("document.documentElement.hasAttribute('data-rail-auto')");
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript(railWidth(256))
|
||||
->assertScript('! '.RAIL.".hasAttribute('data-md-open')")
|
||||
->assertScript("localStorage.getItem('material-rail') === 'expanded'")
|
||||
->assertAttribute('[data-md-navigation-rail-menu]', 'aria-expanded', 'true');
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript(railWidth(96))
|
||||
->assertScript("localStorage.getItem('material-rail') === 'collapsed'")
|
||||
->assertNoJavaScriptErrors();
|
||||
|
||||
// Large: expanded to begin with, so the first press collapses it.
|
||||
$page = shellPage(1512)->assertScript(railWidth(256));
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript(railWidth(96))
|
||||
->assertScript("localStorage.getItem('material-rail') === 'collapsed'")
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('opens and closes the modal rail from $store.rail.toggle() below expanded, storing nothing', function () {
|
||||
// Medium: collapsed in the layout, and expanding it means the scrim, as its menu button does.
|
||||
$page = shellPage(768)->assertScript(railWidth(96));
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript(RAIL.".hasAttribute('data-md-open')")
|
||||
->assertScript('Math.round('.RAIL_PANEL.'.getBoundingClientRect().width) === 256')
|
||||
->assertScript(railWidth(96));
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript('! '.RAIL.".hasAttribute('data-md-open')")
|
||||
->assertScript("localStorage.getItem('material-rail') === null")
|
||||
->assertNoJavaScriptErrors();
|
||||
|
||||
// Compact: nothing is drawn until the modal rail slides in, as `$store.rail.show()` brings it.
|
||||
$page = shellPage(393, 852)->assertScript('getComputedStyle('.RAIL_PANEL.").display === 'none'");
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript(RAIL.".hasAttribute('data-md-open')")
|
||||
->assertScript('getComputedStyle('.RAIL_PANEL.").display === 'flex'");
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript('! '.RAIL.".hasAttribute('data-md-open')")
|
||||
->assertScript('getComputedStyle('.RAIL_PANEL.").display === 'none'")
|
||||
->assertScript("localStorage.getItem('material-rail') === null")
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('morphs the rail header FAB between collapsed (icon only) and extended (with its label)', function () {
|
||||
$fab = "document.querySelector('[data-md-navigation-rail-fab-row] [data-md-fab]')";
|
||||
$label = "document.querySelector('[data-md-navigation-rail-fab-row] [data-md-fab] > span')";
|
||||
@@ -399,6 +465,28 @@ it('takes a rail out of the layout entirely when it hides collapsed, and back ov
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('docks a rail that hid itself back into the layout from $store.rail.toggle(), not over the page', function () {
|
||||
$page = navigationExtrasProbe();
|
||||
$rail = "document.querySelector('#hiding-rail [data-md-navigation-rail]')";
|
||||
$railWidth = "Math.round({$rail}.getBoundingClientRect().width)";
|
||||
|
||||
$page->click('#hiding-rail [data-md-navigation-rail-menu]')
|
||||
->assertScript("{$railWidth} === 0");
|
||||
|
||||
// The first interactive rail on the page is this one: the narrow rail above it is fixed.
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript("{$railWidth} === 256")
|
||||
->assertScript("! {$rail}.hasAttribute('data-md-open')")
|
||||
->assertScript("localStorage.getItem('material-rail') === 'expanded'");
|
||||
|
||||
$page->script(TOGGLE_RAIL);
|
||||
|
||||
$page->assertScript("{$railWidth} === 0")
|
||||
->assertScript("! {$rail}.hasAttribute('data-md-open')")
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('takes an adaptive rail out of the layout across the expanded class when it hides collapsed, standing again from large', function () {
|
||||
$page = navigationExtrasProbe();
|
||||
$rail = "document.querySelector('#adaptive-hiding-rail [data-md-navigation-rail]')";
|
||||
|
||||
Reference in New Issue
Block a user