Give each canonical layout a page of its own in the Layout section
Plan step 38: the Layout section's Overview keeps its breakpoint
table and live "This window" readout (now a shared
<x-showcase::breakpoint-readout> partial, and the five breakpoint
cards a real range-queried data-md-showcase-breakpoint-card instead
of Tailwind's max-medium:/medium:max-expanded: variants), but the
three canonical layouts move off it onto a page of their own:
/material/layout/{list-detail,supporting-pane,feed}
(ShowcaseController::layout(), routes/showcase.php), each built on
the real component, sharing one <x-section-nav> Sections::
layoutPages() feeds. The search index gains an entry per page with
its own URL, and the component-homes scan now also reads the three
pages' own source so <x-list-detail>, <x-supporting-pane> and
<x-feed> keep a home.
The whole section moves onto the layout components and md-* classes:
no bespoke Tailwind grid or table styling remains.
LayoutTest's browser test now visits the three dedicated pages
instead of one shared /material/layout; a new Feature test asserts
each page renders and is linked from the Layout section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
44a29a8ea2
commit
487176d345
@@ -276,4 +276,47 @@
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Layout: the five breakpoint cards, each lit by the same range query the layout components'
|
||||||
|
own stylesheets use, so the page proves the breakpoints rather than only describing them. */
|
||||||
|
[data-md-showcase-breakpoint-card] {
|
||||||
|
border-radius: var(--md-sys-shape-corner-md);
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
padding: var(--md-sys-measurement-space200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width < 600px) {
|
||||||
|
[data-md-showcase-breakpoint-card='compact'] {
|
||||||
|
background-color: var(--md-sys-color-primary-container);
|
||||||
|
color: var(--md-sys-color-on-primary-container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (600px <= width < 840px) {
|
||||||
|
[data-md-showcase-breakpoint-card='medium'] {
|
||||||
|
background-color: var(--md-sys-color-primary-container);
|
||||||
|
color: var(--md-sys-color-on-primary-container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (840px <= width < 1200px) {
|
||||||
|
[data-md-showcase-breakpoint-card='expanded'] {
|
||||||
|
background-color: var(--md-sys-color-primary-container);
|
||||||
|
color: var(--md-sys-color-on-primary-container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (1200px <= width < 1600px) {
|
||||||
|
[data-md-showcase-breakpoint-card='large'] {
|
||||||
|
background-color: var(--md-sys-color-primary-container);
|
||||||
|
color: var(--md-sys-color-on-primary-container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width >= 1600px) {
|
||||||
|
[data-md-showcase-breakpoint-card='extra-large'] {
|
||||||
|
background-color: var(--md-sys-color-primary-container);
|
||||||
|
color: var(--md-sys-color-on-primary-container);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{{-- The live breakpoint readout: this window's width, and the M3 breakpoint it falls in, read
|
||||||
|
from the same numbers resources/js/breakpoints.js compares against. Shared by the Layout
|
||||||
|
section's Overview and its three canonical layout pages (plan step 38). --}}
|
||||||
|
|
||||||
|
<x-livewire-material::surface
|
||||||
|
level="surface-container-low"
|
||||||
|
padding="space300"
|
||||||
|
corner="lg"
|
||||||
|
data-test="window-class"
|
||||||
|
x-data="{ width: window.innerWidth }"
|
||||||
|
x-on:resize.window="width = window.innerWidth"
|
||||||
|
>
|
||||||
|
<x-livewire-material::row gap="space200" wrap align="baseline">
|
||||||
|
<span class="md-type-label-lg md-ink-variant">This window</span>
|
||||||
|
<span class="md-type-emphasized-headline-sm md-tabular" x-text="width + 'px'"></span>
|
||||||
|
<span class="md-type-title-md" x-text="width < 600 ? 'compact' : width < 840 ? 'medium' : width < 1200 ? 'expanded' : width < 1600 ? 'large' : 'extra-large'"></span>
|
||||||
|
</x-livewire-material::row>
|
||||||
|
</x-livewire-material::surface>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{{-- The feed canonical layout on a page of its own (plan step 38): the same working demo the
|
||||||
|
Layout section's Overview linked from its own navigation, built on the real <x-feed>, with
|
||||||
|
the live breakpoint readout the Overview page has. --}}
|
||||||
|
|
||||||
|
@extends('livewire-material::showcase.layout')
|
||||||
|
|
||||||
|
@php
|
||||||
|
$code = <<<'BLADE'
|
||||||
|
<x-feed min-item="220px">
|
||||||
|
<x-card title="Harbour at dawn" subtitle="12 photos" variant="outlined">The boats before the ferry.</x-card>
|
||||||
|
<x-card title="Old town" subtitle="31 photos" variant="outlined">Every lane, twice.</x-card>
|
||||||
|
<x-card title="Lake swim" subtitle="4 videos" variant="outlined">Colder than it looks.</x-card>
|
||||||
|
<x-card title="Market" subtitle="18 photos" variant="outlined">Cheese, mostly.</x-card>
|
||||||
|
<x-card title="The long walk" subtitle="9 photos" variant="outlined">Up to the ridge and back.</x-card>
|
||||||
|
<x-card title="Train home" subtitle="3 photos" variant="outlined">Asleep by the second stop.</x-card>
|
||||||
|
</x-feed>
|
||||||
|
BLADE;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-livewire-material::pane width="wide" data-md-showcase-page>
|
||||||
|
<x-livewire-material::stack gap="space300">
|
||||||
|
<x-livewire-material::stack as="header" gap="space50">
|
||||||
|
<p class="md-type-label-lg md-ink-variant">Foundations</p>
|
||||||
|
<h1 class="md-type-headline-lg">Layout</h1>
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
|
||||||
|
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
|
||||||
|
|
||||||
|
<x-showcase::breakpoint-readout />
|
||||||
|
|
||||||
|
<p class="md-type-body-md md-ink-variant">
|
||||||
|
M3's feed: cards or items to browse, in columns that multiply as the room grows — news, photos,
|
||||||
|
posts. One column below 600px, then as many equal columns of at least <code>min-item</code> as the
|
||||||
|
feed has room for.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<x-showcase::example title="Feed" :code="$code" stack />
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
</x-livewire-material::pane>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{{-- The list-detail canonical layout on a page of its own (plan step 38): the same working demo
|
||||||
|
the Layout section's Overview linked from its own navigation, built on the real
|
||||||
|
<x-list-detail>, with the live breakpoint readout the Overview page has. --}}
|
||||||
|
|
||||||
|
@extends('livewire-material::showcase.layout')
|
||||||
|
|
||||||
|
@php
|
||||||
|
$code = <<<'BLADE'
|
||||||
|
<x-surface level="surface" corner="lg" outlined x-data="{ message: null }" style="overflow: clip">
|
||||||
|
<x-list-detail x-model="message">
|
||||||
|
<x-slot:list>
|
||||||
|
<x-pane title="Inbox" heading="h3" :sticky="false">
|
||||||
|
<x-list label="Messages">
|
||||||
|
<x-list-item title="Anna Müller" description="The photos from Saturday" avatar="AM" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 1" x-bind:aria-current="selected === 1 ? 'true' : null" />
|
||||||
|
<x-list-item title="Ben Keller" description="Contract, second draft" avatar="BK" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 2" x-bind:aria-current="selected === 2 ? 'true' : null" />
|
||||||
|
<x-list-item title="Chiara Rossi" description="Train times for Friday" avatar="CR" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 3" x-bind:aria-current="selected === 3 ? 'true' : null" />
|
||||||
|
</x-list>
|
||||||
|
</x-pane>
|
||||||
|
</x-slot:list>
|
||||||
|
|
||||||
|
<x-slot:detail>
|
||||||
|
<x-pane heading="h3" back :sticky="false">
|
||||||
|
<x-stack gap="space200">
|
||||||
|
<p class="md-type-body-lg md-ink-variant" x-show="! selected">Select a message to read it.</p>
|
||||||
|
<p class="md-type-headline-sm" x-show="selected" x-text="{ 1: 'The photos from Saturday', 2: 'Contract, second draft', 3: 'Train times for Friday' }[selected]"></p>
|
||||||
|
<p class="md-type-body-lg" x-show="selected">Below 840px the list gives way to this message, with a back button that returns you — and your focus — to the list. From 840px both panes show, the list 360px wide and 412px from 1200px.</p>
|
||||||
|
</x-stack>
|
||||||
|
</x-pane>
|
||||||
|
</x-slot:detail>
|
||||||
|
</x-list-detail>
|
||||||
|
</x-surface>
|
||||||
|
BLADE;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-livewire-material::pane width="wide" data-md-showcase-page>
|
||||||
|
<x-livewire-material::stack gap="space300">
|
||||||
|
<x-livewire-material::stack as="header" gap="space50">
|
||||||
|
<p class="md-type-label-lg md-ink-variant">Foundations</p>
|
||||||
|
<h1 class="md-type-headline-lg">Layout</h1>
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
|
||||||
|
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
|
||||||
|
|
||||||
|
<x-showcase::breakpoint-readout />
|
||||||
|
|
||||||
|
<p class="md-type-body-md md-ink-variant">
|
||||||
|
M3's list-detail: a list, and the detail of what is selected in it — an inbox and a message, folders
|
||||||
|
and a file, settings and a category. Below 840px the list gives way to the detail with a back button;
|
||||||
|
from 840px both panes show, the list a fixed 360px (412px from 1200px) and the detail flexible.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<x-showcase::example title="List-detail" :code="$code" stack />
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
</x-livewire-material::pane>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{{-- The supporting-pane canonical layout on a page of its own (plan step 38): the same working
|
||||||
|
demo the Layout section's Overview linked from its own navigation, built on the real
|
||||||
|
<x-supporting-pane>, with the live breakpoint readout the Overview page has. --}}
|
||||||
|
|
||||||
|
@extends('livewire-material::showcase.layout')
|
||||||
|
|
||||||
|
@php
|
||||||
|
$code = <<<'BLADE'
|
||||||
|
<x-supporting-pane label="Comments" compact="sheet">
|
||||||
|
<x-slot:main>
|
||||||
|
<x-surface level="surface" corner="lg" style="overflow: clip">
|
||||||
|
<x-pane title="Trip proposal" subtitle="Draft · edited today" heading="h3" :sticky="false">
|
||||||
|
<x-stack gap="space200" style="padding-block-end: var(--md-sys-measurement-space300)">
|
||||||
|
<p class="md-type-body-lg">Three days by the lake in June: the train on Friday morning, two nights at the harbour, and a walk round the old town on Sunday before the train home.</p>
|
||||||
|
<p class="md-type-body-lg">From 840px the comments sit beside the proposal, 360px wide (412px from 1200px). Below it they are a bottom sheet docked to the window: press its handle to read them.</p>
|
||||||
|
</x-stack>
|
||||||
|
</x-pane>
|
||||||
|
</x-surface>
|
||||||
|
</x-slot:main>
|
||||||
|
|
||||||
|
<x-slot:supporting>
|
||||||
|
<x-surface level="surface-container-low" corner="lg" padding="space200">
|
||||||
|
<x-list label="Comments">
|
||||||
|
<x-list-item title="Anna Müller" description="Friday works for me." avatar="AM" />
|
||||||
|
<x-list-item title="Ben Keller" description="Can we stay a third night?" avatar="BK" />
|
||||||
|
</x-list>
|
||||||
|
</x-surface>
|
||||||
|
</x-slot:supporting>
|
||||||
|
</x-supporting-pane>
|
||||||
|
BLADE;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-livewire-material::pane width="wide" data-md-showcase-page>
|
||||||
|
<x-livewire-material::stack gap="space300">
|
||||||
|
<x-livewire-material::stack as="header" gap="space50">
|
||||||
|
<p class="md-type-label-lg md-ink-variant">Foundations</p>
|
||||||
|
<h1 class="md-type-headline-lg">Layout</h1>
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
|
||||||
|
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
|
||||||
|
|
||||||
|
<x-showcase::breakpoint-readout />
|
||||||
|
|
||||||
|
<p class="md-type-body-md md-ink-variant">
|
||||||
|
M3's supporting pane: a focus pane, and beside it what only means something in relation to it — the
|
||||||
|
comments on a document, the details of a video. Below 840px it moves under the focus pane or into a
|
||||||
|
bottom sheet; from 840px it sits beside it, a fixed 360px (412px from 1200px).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<x-showcase::example title="Supporting pane" :code="$code" stack />
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
</x-livewire-material::pane>
|
||||||
|
@endsection
|
||||||
@@ -1,71 +1,13 @@
|
|||||||
{{-- M3's window size classes, live: the one this window is in, what each one changes, and the
|
{{-- M3's breakpoints, live: the one this window is in, what each one changes, and the cards that
|
||||||
variants that key on them. The five cards light up through the variants themselves, so the
|
light up through real media queries, so the page proves the breakpoints rather than only
|
||||||
page proves the compiled breakpoints rather than describing them.
|
describing them.
|
||||||
|
|
||||||
Then the layout components, and an example of each canonical layout — list-detail, supporting
|
Then the layout components, and the way on to the three canonical layouts — list-detail,
|
||||||
pane, feed — written as an application writes them, with the layout components and the md-*
|
supporting pane, feed — each on a page of its own (plan step 38), reached through the
|
||||||
text classes and no Tailwind. Resize the window to watch each one change at its breakpoints. --}}
|
section navigation below. --}}
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$examples = [
|
$examples = [
|
||||||
'List-detail' => <<<'BLADE'
|
|
||||||
<x-surface level="surface" corner="lg" outlined x-data="{ message: null }" style="overflow: clip">
|
|
||||||
<x-list-detail x-model="message">
|
|
||||||
<x-slot:list>
|
|
||||||
<x-pane title="Inbox" heading="h3" :sticky="false">
|
|
||||||
<x-list label="Messages">
|
|
||||||
<x-list-item title="Anna Müller" description="The photos from Saturday" avatar="AM" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 1" x-bind:aria-current="selected === 1 ? 'true' : null" />
|
|
||||||
<x-list-item title="Ben Keller" description="Contract, second draft" avatar="BK" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 2" x-bind:aria-current="selected === 2 ? 'true' : null" />
|
|
||||||
<x-list-item title="Chiara Rossi" description="Train times for Friday" avatar="CR" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 3" x-bind:aria-current="selected === 3 ? 'true' : null" />
|
|
||||||
</x-list>
|
|
||||||
</x-pane>
|
|
||||||
</x-slot:list>
|
|
||||||
|
|
||||||
<x-slot:detail>
|
|
||||||
<x-pane heading="h3" back :sticky="false">
|
|
||||||
<x-stack gap="space200">
|
|
||||||
<p class="md-type-body-lg md-ink-variant" x-show="! selected">Select a message to read it.</p>
|
|
||||||
<p class="md-type-headline-sm" x-show="selected" x-text="{ 1: 'The photos from Saturday', 2: 'Contract, second draft', 3: 'Train times for Friday' }[selected]"></p>
|
|
||||||
<p class="md-type-body-lg" x-show="selected">Below 840px the list gives way to this message, with a back button that returns you — and your focus — to the list. From 840px both panes show, the list 360px wide and 412px from 1200px.</p>
|
|
||||||
</x-stack>
|
|
||||||
</x-pane>
|
|
||||||
</x-slot:detail>
|
|
||||||
</x-list-detail>
|
|
||||||
</x-surface>
|
|
||||||
BLADE,
|
|
||||||
'Supporting pane' => <<<'BLADE'
|
|
||||||
<x-supporting-pane label="Comments" compact="sheet">
|
|
||||||
<x-slot:main>
|
|
||||||
<x-surface level="surface" corner="lg" style="overflow: clip">
|
|
||||||
<x-pane title="Trip proposal" subtitle="Draft · edited today" heading="h3" :sticky="false">
|
|
||||||
<x-stack gap="space200" style="padding-block-end: var(--md-sys-measurement-space300)">
|
|
||||||
<p class="md-type-body-lg">Three days by the lake in June: the train on Friday morning, two nights at the harbour, and a walk round the old town on Sunday before the train home.</p>
|
|
||||||
<p class="md-type-body-lg">From 840px the comments sit beside the proposal, 360px wide (412px from 1200px). Below it they are a bottom sheet docked to the window: press its handle to read them.</p>
|
|
||||||
</x-stack>
|
|
||||||
</x-pane>
|
|
||||||
</x-surface>
|
|
||||||
</x-slot:main>
|
|
||||||
|
|
||||||
<x-slot:supporting>
|
|
||||||
<x-surface level="surface-container-low" corner="lg" padding="space200">
|
|
||||||
<x-list label="Comments">
|
|
||||||
<x-list-item title="Anna Müller" description="Friday works for me." avatar="AM" />
|
|
||||||
<x-list-item title="Ben Keller" description="Can we stay a third night?" avatar="BK" />
|
|
||||||
</x-list>
|
|
||||||
</x-surface>
|
|
||||||
</x-slot:supporting>
|
|
||||||
</x-supporting-pane>
|
|
||||||
BLADE,
|
|
||||||
'Feed' => <<<'BLADE'
|
|
||||||
<x-feed min-item="220px">
|
|
||||||
<x-card title="Harbour at dawn" subtitle="12 photos" variant="outlined">The boats before the ferry.</x-card>
|
|
||||||
<x-card title="Old town" subtitle="31 photos" variant="outlined">Every lane, twice.</x-card>
|
|
||||||
<x-card title="Lake swim" subtitle="4 videos" variant="outlined">Colder than it looks.</x-card>
|
|
||||||
<x-card title="Market" subtitle="18 photos" variant="outlined">Cheese, mostly.</x-card>
|
|
||||||
<x-card title="The long walk" subtitle="9 photos" variant="outlined">Up to the ridge and back.</x-card>
|
|
||||||
<x-card title="Train home" subtitle="3 photos" variant="outlined">Asleep by the second stop.</x-card>
|
|
||||||
</x-feed>
|
|
||||||
BLADE,
|
|
||||||
'Surfaces, stacks, rows and grids' => <<<'BLADE'
|
'Surfaces, stacks, rows and grids' => <<<'BLADE'
|
||||||
<x-stack gap="space300">
|
<x-stack gap="space300">
|
||||||
<x-grid :columns="['compact' => 1, 'medium' => 2, 'expanded' => 4]" gap="space200">
|
<x-grid :columns="['compact' => 1, 'medium' => 2, 'expanded' => 4]" gap="space200">
|
||||||
@@ -88,59 +30,48 @@
|
|||||||
];
|
];
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section id="layout" class="scroll-mt-24 space-y-6">
|
<x-livewire-material::stack as="section" id="layout" gap="space300">
|
||||||
<h2 class="type-headline-md">Layout</h2>
|
<h2 class="md-type-headline-md">Layout</h2>
|
||||||
|
|
||||||
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
<p class="md-type-body-md md-ink-variant">
|
||||||
Widths are M3's window size classes and nothing else: compact below 600px, then <code>medium:</code> 600,
|
Widths are M3's breakpoints and nothing else: compact below 600px, then medium 600, expanded 840, large 1200
|
||||||
<code>expanded:</code> 840, <code>large:</code> 1200 and <code>extra-large:</code> 1600, with
|
and extra-large 1600. Scripts read the same numbers from <code>resources/js/breakpoints.js</code>.
|
||||||
<code>max-medium:</code> and friends for "below". Tailwind's <code>sm:</code>…<code>2xl:</code> compile to
|
|
||||||
nothing. Scripts read the same numbers from <code>resources/js/breakpoints.js</code>.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<x-showcase::breakpoint-readout />
|
||||||
x-data="{ width: window.innerWidth }"
|
|
||||||
x-on:resize.window="width = window.innerWidth"
|
|
||||||
class="flex flex-wrap items-baseline gap-x-4 gap-y-1 rounded-corner-lg bg-surface-container-low p-6"
|
|
||||||
data-test="window-class"
|
|
||||||
>
|
|
||||||
<span class="type-label-lg text-on-surface-variant">This window</span>
|
|
||||||
<span class="type-emphasized-headline-sm tabular-nums" x-text="width + 'px'"></span>
|
|
||||||
<span class="type-title-md" x-text="width < 600 ? 'compact' : width < 840 ? 'medium' : width < 1200 ? 'expanded' : width < 1600 ? 'large' : 'extra-large'"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid gap-3 medium:grid-cols-5">
|
<x-livewire-material::grid :columns="['compact' => 1, 'medium' => 5]" gap="space200">
|
||||||
<div class="rounded-corner-md bg-surface-container p-4 max-medium:bg-primary-container max-medium:text-on-primary-container">
|
<div data-md-showcase-breakpoint-card="compact">
|
||||||
<p class="type-title-sm">Compact</p>
|
<p class="md-type-title-sm">Compact</p>
|
||||||
<p class="type-body-sm">below 600px</p>
|
<p class="md-type-body-sm">below 600px</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-corner-md bg-surface-container p-4 medium:max-expanded:bg-primary-container medium:max-expanded:text-on-primary-container">
|
<div data-md-showcase-breakpoint-card="medium">
|
||||||
<p class="type-title-sm">Medium</p>
|
<p class="md-type-title-sm">Medium</p>
|
||||||
<p class="type-body-sm">600–839px</p>
|
<p class="md-type-body-sm">600–839px</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-corner-md bg-surface-container p-4 expanded:max-large:bg-primary-container expanded:max-large:text-on-primary-container">
|
<div data-md-showcase-breakpoint-card="expanded">
|
||||||
<p class="type-title-sm">Expanded</p>
|
<p class="md-type-title-sm">Expanded</p>
|
||||||
<p class="type-body-sm">840–1199px</p>
|
<p class="md-type-body-sm">840–1199px</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-corner-md bg-surface-container p-4 large:max-extra-large:bg-primary-container large:max-extra-large:text-on-primary-container">
|
<div data-md-showcase-breakpoint-card="large">
|
||||||
<p class="type-title-sm">Large</p>
|
<p class="md-type-title-sm">Large</p>
|
||||||
<p class="type-body-sm">1200–1599px</p>
|
<p class="md-type-body-sm">1200–1599px</p>
|
||||||
</div>
|
|
||||||
<div class="rounded-corner-md bg-surface-container p-4 extra-large:bg-primary-container extra-large:text-on-primary-container">
|
|
||||||
<p class="type-title-sm">Extra-large</p>
|
|
||||||
<p class="type-body-sm">from 1600px</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div data-md-showcase-breakpoint-card="extra-large">
|
||||||
|
<p class="md-type-title-sm">Extra-large</p>
|
||||||
|
<p class="md-type-body-sm">from 1600px</p>
|
||||||
</div>
|
</div>
|
||||||
|
</x-livewire-material::grid>
|
||||||
|
|
||||||
<div class="overflow-x-auto rounded-corner-lg border border-structure">
|
<x-livewire-material::surface corner="lg" outlined style="overflow-x: auto">
|
||||||
<table class="w-full type-body-md" data-md-table>
|
<x-livewire-material::table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-start">Class</th>
|
<th>Class</th>
|
||||||
<th class="text-start">Navigation</th>
|
<th>Navigation</th>
|
||||||
<th class="text-start">Panes</th>
|
<th>Panes</th>
|
||||||
<th class="text-start">Dialogs and sheets</th>
|
<th>Dialogs and sheets</th>
|
||||||
<th class="text-start">Margin</th>
|
<th>Margin</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -180,10 +111,10 @@
|
|||||||
<td>24px</td>
|
<td>24px</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</x-livewire-material::table>
|
||||||
</div>
|
</x-livewire-material::surface>
|
||||||
|
|
||||||
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
<p class="md-type-body-md md-ink-variant">
|
||||||
<code><x-scaffold></code> does all of this: resize this showcase and watch the bar, the rail and the
|
<code><x-scaffold></code> does all of this: resize this showcase and watch the bar, the rail and the
|
||||||
content margin (<code>--material-margin</code>) change at 600, 840 and 1200px. A visitor who has chosen a rail
|
content margin (<code>--material-margin</code>) change at 600, 840 and 1200px. A visitor who has chosen a rail
|
||||||
width keeps it; until then the rail follows the class.
|
width keeps it; until then the rail follows the class.
|
||||||
@@ -209,7 +140,13 @@
|
|||||||
</x-livewire-material::stack>
|
</x-livewire-material::stack>
|
||||||
</x-livewire-material::stack>
|
</x-livewire-material::stack>
|
||||||
|
|
||||||
|
<x-livewire-material::stack gap="space200">
|
||||||
|
<h3 class="md-type-title-lg">Each canonical layout, on a page of its own</h3>
|
||||||
|
|
||||||
|
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
|
||||||
|
</x-livewire-material::stack>
|
||||||
|
|
||||||
@foreach ($examples as $title => $code)
|
@foreach ($examples as $title => $code)
|
||||||
<x-showcase::example :$title :$code stack />
|
<x-showcase::example :$title :$code stack />
|
||||||
@endforeach
|
@endforeach
|
||||||
</section>
|
</x-livewire-material::stack>
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ Route::get('mail', [ShowcasePageController::class, 'mail'])->name('mail');
|
|||||||
|
|
||||||
Route::get('search.json', [ShowcaseController::class, 'search'])->name('search');
|
Route::get('search.json', [ShowcaseController::class, 'search'])->name('search');
|
||||||
|
|
||||||
|
// The Layout section's three canonical layouts (plan step 38), each a page of its own rather
|
||||||
|
// than an anchor on the Layout section's Overview (`{section}` below).
|
||||||
|
Route::get('layout/{page}', [ShowcaseController::class, 'layout'])
|
||||||
|
->whereIn('page', ['list-detail', 'supporting-pane', 'feed'])
|
||||||
|
->name('layout');
|
||||||
|
|
||||||
Route::get('{section}', [ShowcaseController::class, 'section'])
|
Route::get('{section}', [ShowcaseController::class, 'section'])
|
||||||
->whereIn('section', array_keys(Sections::all()))
|
->whereIn('section', array_keys(Sections::all()))
|
||||||
->name('section');
|
->name('section');
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ class ShowcaseController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the Layout section's own pages (plan step 38): a canonical layout on a page of its
|
||||||
|
* own, sharing the Layout section's rail entry and title.
|
||||||
|
*/
|
||||||
|
public function layout(string $page): View
|
||||||
|
{
|
||||||
|
return view('livewire-material::showcase.layout.'.$page, [
|
||||||
|
'sections' => Sections::all(),
|
||||||
|
'section' => 'layout',
|
||||||
|
'title' => Sections::layoutPages()[$page]['title'].' · Layout',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The search's index, fetched when the search is first focused rather than written into every page.
|
* The search's index, fetched when the search is first focused rather than written into every page.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -40,6 +40,19 @@ class Sections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Layout section's three canonical layouts (plan step 38): each is a page of its own
|
||||||
|
// rather than an anchor on the Overview, so its search entry carries its own URL; the
|
||||||
|
// page's own source joins the Layout section's for the component-homes scan below, so
|
||||||
|
// <x-list-detail>, <x-supporting-pane> and <x-feed> still find a home.
|
||||||
|
foreach (static::layoutPages() as $key => $page) {
|
||||||
|
if ($key === 'layout') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries[] = ['title' => $page['title'], 'kind' => 'Example', 'context' => 'Layout', 'url' => $page['url'].'#'.static::anchor($page['title']), 'keywords' => ''];
|
||||||
|
$sources['layout'] .= (string) file_get_contents(static::layoutPagePath($key));
|
||||||
|
}
|
||||||
|
|
||||||
// A section that names a component in its introduction is its home; otherwise the first that uses it.
|
// A section that names a component in its introduction is its home; otherwise the first that uses it.
|
||||||
foreach (['/<x-([a-z0-9-]+)>/', '/<x-(?:livewire-material::)?([a-z0-9-]+)(?=[\\s\\/>])/'] as $pattern) {
|
foreach (['/<x-([a-z0-9-]+)>/', '/<x-(?:livewire-material::)?([a-z0-9-]+)(?=[\\s\\/>])/'] as $pattern) {
|
||||||
foreach ($sources as $key => $source) {
|
foreach ($sources as $key => $source) {
|
||||||
@@ -76,6 +89,34 @@ class Sections
|
|||||||
return dirname(__DIR__, 2).'/resources/views/showcase/sections/'.$key.'.blade.php';
|
return dirname(__DIR__, 2).'/resources/views/showcase/sections/'.$key.'.blade.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Layout section's own pages (plan step 38): the overview and the three canonical
|
||||||
|
* layouts, each on a page of its own, sharing one `<x-section-nav>` and the live breakpoint
|
||||||
|
* readout rather than three anchors on the Overview. They are not top-level destinations of
|
||||||
|
* their own — `Sections::all()` still has one `layout` entry for the rail and the overview
|
||||||
|
* grid — so this is their own small index instead.
|
||||||
|
*
|
||||||
|
* @return array<string, array{title: string, icon: string, url: string}>
|
||||||
|
*/
|
||||||
|
public static function layoutPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'layout' => ['title' => 'Overview', 'icon' => 'devices', 'url' => route('livewire-material.section', 'layout', false)],
|
||||||
|
'list-detail' => ['title' => 'List-detail', 'icon' => 'view_sidebar', 'url' => route('livewire-material.layout', 'list-detail', false)],
|
||||||
|
'supporting-pane' => ['title' => 'Supporting pane', 'icon' => 'splitscreen', 'url' => route('livewire-material.layout', 'supporting-pane', false)],
|
||||||
|
'feed' => ['title' => 'Feed', 'icon' => 'view_agenda', 'url' => route('livewire-material.layout', 'feed', false)],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the Layout section's own pages' view file (`layoutPages()`'s keys but `layout`
|
||||||
|
* itself, which is `path('layout')`).
|
||||||
|
*/
|
||||||
|
public static function layoutPagePath(string $key): string
|
||||||
|
{
|
||||||
|
return dirname(__DIR__, 2).'/resources/views/showcase/layout/'.$key.'.blade.php';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, array{title: string, icon: string, group: string, description: string}>
|
* @return array<string, array{title: string, icon: string, group: string, description: string}>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -529,25 +529,33 @@ it('draws no second margin for a pane or a canonical layout inside the scaffold\
|
|||||||
->assertScript(layoutStyle('#pane [data-md-pane-body]', 'paddingLeft')." === '0px'");
|
->assertScript(layoutStyle('#pane [data-md-pane-body]', 'paddingLeft')." === '0px'");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows each canonical layout working on the showcase\'s Layout page', function () {
|
it('shows each canonical layout working on its own Layout page', function () {
|
||||||
$example = fn (string $title): string => "#example-{$title}";
|
$listDetail = layoutReady(visit('/material/layout/list-detail')->resize(1280, 900))
|
||||||
|
|
||||||
$page = layoutReady(visit('/material/layout')->resize(1280, 900))
|
|
||||||
->assertScript("document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]').length === 2")
|
->assertScript("document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]').length === 2")
|
||||||
->assertScript("[...document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]')].every((pane) => pane.checkVisibility())")
|
->assertScript("[...document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]')].every((pane) => pane.checkVisibility())");
|
||||||
->assertScript(layoutRect($example('supporting-pane').' [data-md-supporting-pane-supporting]', 'top').' === '.layoutRect($example('supporting-pane').' [data-md-supporting-pane-main]', 'top'))
|
|
||||||
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' > 1');
|
|
||||||
|
|
||||||
$page->click('#example-list-detail a:has-text("Ben Keller")')
|
$listDetail->click('#example-list-detail a:has-text("Ben Keller")')
|
||||||
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Contract, second draft')
|
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Contract, second draft')
|
||||||
->assertNoJavaScriptErrors();
|
->assertNoJavaScriptErrors();
|
||||||
|
|
||||||
$compact = layoutReady(visit('/material/layout')->resize(599, 900));
|
$compactListDetail = layoutReady(visit('/material/layout/list-detail')->resize(599, 900));
|
||||||
|
|
||||||
$compact->click('#example-list-detail a:has-text("Chiara Rossi")')
|
$compactListDetail->click('#example-list-detail a:has-text("Chiara Rossi")')
|
||||||
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Train times for Friday')
|
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Train times for Friday')
|
||||||
->assertScript("! document.querySelector('#example-list-detail [data-md-list-detail-pane=\"list\"]').checkVisibility()")
|
->assertScript("! document.querySelector('#example-list-detail [data-md-list-detail-pane=\"list\"]').checkVisibility()")
|
||||||
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' === 1')
|
->assertNoJavaScriptErrors();
|
||||||
|
|
||||||
|
layoutReady(visit('/material/layout/supporting-pane')->resize(1280, 900))
|
||||||
|
->assertScript(layoutRect('#example-supporting-pane [data-md-supporting-pane-supporting]', 'top').' === '.layoutRect('#example-supporting-pane [data-md-supporting-pane-main]', 'top'));
|
||||||
|
|
||||||
|
layoutReady(visit('/material/layout/supporting-pane')->resize(599, 900))
|
||||||
->assertScript("document.querySelector('#example-supporting-pane [data-md-supporting-pane-handle]').checkVisibility()")
|
->assertScript("document.querySelector('#example-supporting-pane [data-md-supporting-pane-handle]').checkVisibility()")
|
||||||
->assertNoJavaScriptErrors();
|
->assertNoJavaScriptErrors();
|
||||||
|
|
||||||
|
layoutReady(visit('/material/layout/feed')->resize(1280, 900))
|
||||||
|
->assertScript(layoutColumns('#example-feed [data-md-feed]').' > 1');
|
||||||
|
|
||||||
|
layoutReady(visit('/material/layout/feed')->resize(599, 900))
|
||||||
|
->assertScript(layoutColumns('#example-feed [data-md-feed]').' === 1')
|
||||||
|
->assertNoJavaScriptErrors();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,6 +45,24 @@ it('gives every section a page of its own, linked from the overview and the rail
|
|||||||
$this->get('/material/not-a-section')->assertNotFound();
|
$this->get('/material/not-a-section')->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('gives each canonical layout a page of its own, linked from the Layout section', function () {
|
||||||
|
foreach (['list-detail', 'supporting-pane', 'feed'] as $page) {
|
||||||
|
test()->withoutVite()->get("/material/layout/{$page}")
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('id="example-'.$page.'"', false)
|
||||||
|
// Its own navigation reaches the other three pages, including itself.
|
||||||
|
->assertSee(route('livewire-material.layout', 'list-detail', false), false)
|
||||||
|
->assertSee(route('livewire-material.layout', 'supporting-pane', false), false)
|
||||||
|
->assertSee(route('livewire-material.layout', 'feed', false), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
test()->withoutVite()->get('/material/layout')
|
||||||
|
->assertOk()
|
||||||
|
->assertSee(route('livewire-material.layout', 'list-detail', false), false)
|
||||||
|
->assertSee(route('livewire-material.layout', 'supporting-pane', false), false)
|
||||||
|
->assertSee(route('livewire-material.layout', 'feed', false), false);
|
||||||
|
});
|
||||||
|
|
||||||
it('indexes every section, example and component for the search, each linking to a place that exists', function () {
|
it('indexes every section, example and component for the search, each linking to a place that exists', function () {
|
||||||
$index = collect($this->getJson('/material/search.json')->assertOk()->json());
|
$index = collect($this->getJson('/material/search.json')->assertOk()->json());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user