Draw a standard side sheet's first state without motion
Giving a closing standard sheet its room back made the root's inline size a transition, with an `@starting-style` for the entry, so a sheet that starts open grew in on every page load: its open state arrives through Alpine, after the first paint of the closed root. Nothing should move while a page loads. The view now marks the root `data-md-drawer-settled` two frames after the first `settle()` — once the state Alpine starts with has been painted — and drawer.css gives the standard sheet's root and sheet no transitions until then. The browser test loads a page with slowed motion tokens and a sheet that starts open, and finds it standing at its full width, shown, with nothing animating; closing it afterwards still animates. It fails without the change in Chrome and Firefox and passes in all three engines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
99c18e01d8
commit
1ccf0b639e
@@ -195,6 +195,13 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Until the view settles (`data-md-drawer-settled`, two frames after Alpine starts), the
|
||||
state the page loads with is drawn at once: a sheet that starts open does not grow in. */
|
||||
[data-md-drawer][data-md-standard]:not([data-md-drawer-settled]),
|
||||
[data-md-drawer][data-md-standard]:not([data-md-drawer-settled]) > [data-md-drawer-sheet] {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
[data-md-drawer][data-md-standard] > [data-md-drawer-scrim] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
waits for that exit (`closing`, `settle()`) instead of cutting it off on its first frame. It
|
||||
binds `collapsed`, which only `settle()` and the window's width write: a binding that read
|
||||
`open` itself would run before `settle()` in the same flush and, already queued, not run again
|
||||
once `closing` changed.
|
||||
once `closing` changed. Nothing moves while the page loads: `data-md-drawer-settled` comes two
|
||||
frames after the state Alpine starts with, and the standard sheet has no transitions until
|
||||
then, so a sheet that starts open stands open rather than growing in.
|
||||
|
||||
For the second pane of a list-detail layout use `<x-list-detail>` instead (step 35's canonical
|
||||
layout); the two are not the same thing — a pane shows what the list beside it selected, a
|
||||
@@ -85,6 +87,7 @@
|
||||
x-data="{
|
||||
@if ($model !== null) open: @entangle($attributes->wire('model')).live, @endif
|
||||
wide: false,
|
||||
settled: false,
|
||||
collapsed: false,
|
||||
closing: false,
|
||||
closings: 0,
|
||||
@@ -93,9 +96,17 @@
|
||||
settle(open) {
|
||||
open = Boolean(open);
|
||||
|
||||
if (this.wasOpen === null || open === this.wasOpen) {
|
||||
if (this.wasOpen === null) {
|
||||
this.wasOpen = open;
|
||||
|
||||
// The state Alpine starts with is drawn, not animated: two frames on, once it has
|
||||
// been painted, the sheet may move.
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => this.settled = true));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (open === this.wasOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,6 +152,7 @@
|
||||
@if ($closeOnEscape) x-on:keydown.window.escape="if (open && ! wide) close()" @endif
|
||||
x-bind:data-md-open="open ? '' : null"
|
||||
x-bind:data-md-drawer-collapsed="collapsed ? '' : null"
|
||||
x-bind:data-md-drawer-settled="settled ? '' : null"
|
||||
x-effect="settle(open)"
|
||||
data-md-drawer
|
||||
@if ($standard) data-md-standard @endif
|
||||
|
||||
@@ -515,8 +515,8 @@ it('gives a closing standard side sheet\'s room back to the content beside it as
|
||||
$column = "{$root}.previousElementSibling";
|
||||
$toggle = "Array.from(document.querySelectorAll('#containment button')).find((button) => button.textContent.trim() === 'Show or hide the filters')";
|
||||
|
||||
// The example starts open, and grows in once Alpine opens it: its entry has run when the sheet
|
||||
// stands at its full 400px and fully shown, with nothing on it still animating.
|
||||
// The example starts open, and stands open from the first frame Alpine draws: full 400px, fully
|
||||
// shown, nothing on it animating.
|
||||
$page = containment()->resize(1000, 800)
|
||||
->assertScript("{$root}.hasAttribute('data-md-open') && Math.round({$root}.getBoundingClientRect().width) === 400 && getComputedStyle({$sheet}).opacity === '1' && {$root}.getAnimations({ subtree: true }).length === 0");
|
||||
|
||||
@@ -565,6 +565,42 @@ it('gives a closing standard side sheet\'s room back to the content beside it as
|
||||
->assertScript("{$root}.hasAttribute('data-md-open') && ! {$root}.hasAttribute('data-md-drawer-collapsed') && Math.abs({$root}.getBoundingClientRect().width - 400) < 1 && getComputedStyle({$sheet}).opacity === '1'");
|
||||
});
|
||||
|
||||
it('draws a standard side sheet that starts open standing open, without growing it in on load', function () {
|
||||
// Slow tokens, so a load-time entry would still be running when the page is first read.
|
||||
Route::middleware('web')->get('/standard-sheet-load-probe', fn () => Blade::render(<<<'BLADE'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<x-theme-script />
|
||||
@vite(config('livewire-material.showcase.vite'))
|
||||
@livewireStyles
|
||||
<style>:root { --md-sys-motion-spatial-default-duration: 3000ms; --md-sys-motion-effects-default-duration: 3000ms; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<x-row align="stretch" gap="space300" x-data="{ open: true }">
|
||||
<x-stack style="flex: 1 1 0%; min-width: 0;">
|
||||
<button type="button" id="toggle-sheet" x-on:click="open = ! open">Show or hide the filters</button>
|
||||
</x-stack>
|
||||
<x-drawer standard title="Filters">Filter the list</x-drawer>
|
||||
</x-row>
|
||||
@livewireScripts
|
||||
</body>
|
||||
</html>
|
||||
BLADE));
|
||||
|
||||
$root = "document.querySelector('[data-md-drawer]')";
|
||||
|
||||
$page = visit('/standard-sheet-load-probe')->resize(1000, 800)->waitForEvent('networkidle')
|
||||
->assertScript("typeof window.Alpine !== 'undefined' && {$root}.hasAttribute('data-md-drawer-settled')")
|
||||
->assertScript("{$root}.hasAttribute('data-md-open') && Math.abs({$root}.getBoundingClientRect().width - 400) < 1")
|
||||
->assertScript("{$root}.getAnimations({ subtree: true }).length === 0")
|
||||
->assertScript("getComputedStyle({$root}.querySelector('[data-md-drawer-sheet]')).opacity === '1'");
|
||||
|
||||
// Settled, it still moves when someone closes it.
|
||||
$page->script("document.getElementById('toggle-sheet').click()");
|
||||
$page->assertScript("{$root}.getAnimations({ subtree: true }).length > 0");
|
||||
});
|
||||
|
||||
it('opens a row\'s opener from a press anywhere on the row, but not from its own buttons', function () {
|
||||
$page = containment();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user