Rename the app shell to the scaffold and give it a FAB slot
Plan step 35: <x-app-shell> becomes <x-scaffold>, M3's and Compose's name for the structure of bars, rails and panes, with no alias; every package view, the showcase, the tests, the skills, README and UPGRADE follow. Its markup and Tailwind classes stay for the navigation rewrite. The new fab slot places the FAB as Compose's Scaffold does - bottom-end, 16px from the edges below medium and 24px from it, clear of the navigation bar and of a snackbar on screen (resources/css/layout/scaffold.css) - and the content region tells a pane inside that the margin is already drawn. 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
acd6ba21ae
commit
0013e0154d
@@ -407,3 +407,59 @@ it('lays a feed out in one column below medium and in columns of its minimum wid
|
||||
->assertScript(layoutColumns('#feed').' === 4')
|
||||
->assertScript(layoutRect('#item-5', 'left').' === '.layoutRect('#item-1', 'left'));
|
||||
});
|
||||
|
||||
function scaffoldFabPage(int $width): mixed
|
||||
{
|
||||
$body = <<<'BLADE'
|
||||
<x-scaffold :destinations="[
|
||||
['title' => 'Inbox', 'icon' => 'inbox', 'url' => '/inbox', 'active' => true],
|
||||
['title' => 'Sent', 'icon' => 'send', 'url' => '/sent'],
|
||||
['title' => 'Drafts', 'icon' => 'draft', 'url' => '/drafts'],
|
||||
]">
|
||||
<x-slot:fab><x-fab icon="edit" tooltip="Compose" /></x-slot:fab>
|
||||
<p style="block-size: 2000px">The page</p>
|
||||
</x-scaffold>
|
||||
BLADE;
|
||||
|
||||
return layoutPage($body, $width, 800);
|
||||
}
|
||||
|
||||
const SCAFFOLD_FAB = '[data-md-scaffold-fab]';
|
||||
|
||||
/** The navigation bar the scaffold pins to the bottom: the one navigation landmark at the window's foot. */
|
||||
const SCAFFOLD_BAR = "[...document.querySelectorAll('[data-md-scaffold] nav')].find((nav) => nav.checkVisibility() && Math.round(nav.getBoundingClientRect().bottom) >= window.innerHeight - 1)";
|
||||
|
||||
it('places the scaffold\'s FAB at the bottom-end corner, clear of the navigation bar and of a snackbar', function () {
|
||||
$page = scaffoldFabPage(599)
|
||||
->assertScript(layoutStyle(SCAFFOLD_FAB, 'position')." === 'fixed'")
|
||||
->assertScript(SCAFFOLD_BAR.' !== undefined')
|
||||
->assertScript('window.innerWidth - '.layoutRect(SCAFFOLD_FAB, 'right').' === 16')
|
||||
// 16px above the bar's top edge.
|
||||
->assertScript('Math.round('.SCAFFOLD_BAR.'.getBoundingClientRect().top) - '.layoutRect(SCAFFOLD_FAB, 'bottom').' === 16');
|
||||
|
||||
$page->script("window.eval(\"materialToast('Draft saved', { sticky: true })\")");
|
||||
|
||||
$page->wait(1)
|
||||
->assertScript("document.documentElement.style.getPropertyValue('--material-snackbar-height') !== ''")
|
||||
// Lifted above the snackbar, which sits 16px above the bar, by M3's 16px margin.
|
||||
->assertScript("Math.round(document.querySelector('[x-data=\"materialSnackbar\"] > div').getBoundingClientRect().top) - ".layoutRect(SCAFFOLD_FAB, 'bottom').' === 16')
|
||||
->assertScript(layoutRect(SCAFFOLD_FAB, 'bottom')." <= Math.round(document.querySelector('[x-data=\"materialSnackbar\"] > div').getBoundingClientRect().top)");
|
||||
|
||||
scaffoldFabPage(600)
|
||||
->assertScript('window.innerWidth - '.layoutRect(SCAFFOLD_FAB, 'right').' === 24')
|
||||
->assertScript('window.innerHeight - '.layoutRect(SCAFFOLD_FAB, 'bottom').' === 24')
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('draws no second margin for a pane or a canonical layout inside the scaffold\'s content region', function () {
|
||||
$body = <<<'BLADE'
|
||||
<x-scaffold :destinations="[['title' => 'Inbox', 'icon' => 'inbox', 'url' => '/inbox', 'active' => true]]">
|
||||
<x-pane id="pane"><p id="text">Body</p></x-pane>
|
||||
</x-scaffold>
|
||||
BLADE;
|
||||
|
||||
// The content region's own 24px beside the 96px collapsed rail, and nothing more.
|
||||
layoutPage($body, 700)
|
||||
->assertScript(layoutRect('#text', 'left').' - '.layoutRect('main', 'left').' === 24')
|
||||
->assertScript(layoutStyle('#pane [data-md-pane-body]', 'paddingLeft')." === '0px'");
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ function firstPaintProbe(): void
|
||||
@livewireStyles
|
||||
</head>
|
||||
<body>
|
||||
<x-app-shell :destinations="[
|
||||
<x-scaffold :destinations="[
|
||||
['title' => 'Inbox', 'icon' => 'inbox', 'url' => '/rail-probe', 'active' => true],
|
||||
['title' => 'Sent', 'icon' => 'send', 'url' => '/rail-probe'],
|
||||
]">
|
||||
@@ -51,7 +51,7 @@ function firstPaintProbe(): void
|
||||
};
|
||||
</script>
|
||||
<p>The page</p>
|
||||
</x-app-shell>
|
||||
</x-scaffold>
|
||||
@livewireScripts
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user