M3 Expressive's flexible navigation bar, the collapsed, expanded and modal navigation rail with its state applied before the first paint, and an adaptive app shell composing them. The head script now restores the theme and rail attributes that wire:navigate strips from <html>. Completes Phase 8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
104 lines
4.4 KiB
PHP
104 lines
4.4 KiB
PHP
{{-- The theme and the navigation rail's width, decided before the first paint. Include it in
|
|
<head>, ahead of @vite.
|
|
|
|
It reads the visitor's choice from localStorage (`livewire-material.theme.storage_key`):
|
|
`light`, `dark` or `system`, falling back to `theme.default`. `system` follows the
|
|
operating system, now and whenever it changes. The result is written to
|
|
<html data-theme>, which is the only thing the stylesheet keys on; the choice itself is
|
|
<html data-theme-choice>, where `$store.theme` picks it up.
|
|
|
|
A value under one of `theme.legacy_keys` — an earlier toggle's key, which may be
|
|
JSON-encoded (maryUI's `"dark"`) — is adopted once and removed. Nothing is written for a
|
|
visitor who never chose, so changing `theme.default` later reaches them too.
|
|
|
|
The rail rides along for the theme's reason: <html data-rail> is `expanded` or `collapsed`
|
|
(`livewire-material.rail.storage_key`, falling back to `rail.default`), and a collapsible
|
|
rail's width is CSS keyed on it (the `rail-collapsed:` variant). Set any later, a collapsed
|
|
rail would paint wide and snap shut on every load. `$store.rail` (resources/js/navigation.js)
|
|
changes it.
|
|
|
|
wire:navigate swaps the body, merges the head without running this again, and gives <html>
|
|
the next page's attributes — which the server rendered without any of these, so Livewire
|
|
removes them. They are put back as the new page is swapped in (`onSwap`, in the same task,
|
|
before anything paints), so this only has to run on a full load. --}}
|
|
|
|
@php
|
|
$theme = config('livewire-material.theme');
|
|
$rail = config('livewire-material.rail');
|
|
|
|
$settings = [
|
|
'default' => in_array($theme['default'] ?? null, ['light', 'dark', 'system'], true) ? $theme['default'] : 'system',
|
|
'key' => $theme['storage_key'] ?? 'material-theme',
|
|
'legacy' => array_values($theme['legacy_keys'] ?? []),
|
|
'rail' => [
|
|
'default' => ($rail['default'] ?? null) === 'collapsed' ? 'collapsed' : 'expanded',
|
|
'key' => $rail['storage_key'] ?? 'material-rail',
|
|
],
|
|
];
|
|
@endphp
|
|
|
|
<script>
|
|
(function (settings) {
|
|
var root = document.documentElement;
|
|
var media = window.matchMedia('(prefers-color-scheme: dark)');
|
|
var valid = function (value) { return value === 'light' || value === 'dark' || value === 'system'; };
|
|
var choice = settings.default;
|
|
var rail = settings.rail.default;
|
|
|
|
try {
|
|
var storedRail = localStorage.getItem(settings.rail.key);
|
|
|
|
if (storedRail === 'collapsed' || storedRail === 'expanded') {
|
|
rail = storedRail;
|
|
}
|
|
|
|
var stored = localStorage.getItem(settings.key);
|
|
|
|
if (valid(stored)) {
|
|
choice = stored;
|
|
} else {
|
|
settings.legacy.forEach(function (key) {
|
|
var legacy = (localStorage.getItem(key) || '').replace(/"/g, '');
|
|
|
|
if (valid(legacy)) {
|
|
choice = legacy;
|
|
localStorage.setItem(settings.key, legacy);
|
|
}
|
|
|
|
localStorage.removeItem(key);
|
|
});
|
|
}
|
|
} catch (e) {
|
|
// Private windows and blocked site data throw on access; the default stands.
|
|
}
|
|
|
|
var apply = function () {
|
|
var current = root.getAttribute('data-theme-choice');
|
|
|
|
root.setAttribute('data-theme', current === 'system' ? (media.matches ? 'dark' : 'light') : current);
|
|
};
|
|
|
|
root.setAttribute('data-theme-key', settings.key);
|
|
root.setAttribute('data-theme-choice', choice);
|
|
root.setAttribute('data-rail-key', settings.rail.key);
|
|
root.setAttribute('data-rail', rail);
|
|
apply();
|
|
|
|
media.addEventListener('change', apply);
|
|
|
|
document.addEventListener('livewire:navigating', function (event) {
|
|
var kept = ['data-theme', 'data-theme-choice', 'data-theme-key', 'data-rail', 'data-rail-key'].map(function (name) {
|
|
return [name, root.getAttribute(name)];
|
|
});
|
|
|
|
event.detail.onSwap(function () {
|
|
kept.forEach(function (attribute) {
|
|
if (attribute[1] !== null) {
|
|
root.setAttribute(attribute[0], attribute[1]);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
})(@json($settings));
|
|
</script>
|