Plan step 38 review: the frame's structural rule that kept a section's own <h2> for screen readers (Tailwind's [&>section>h2]:sr-only before, a showcase.css rule during the rewrite) was removed as dead weight once every section had a real heading, so each page drew its title twice, the <h1> and an identical headline under it. The <h2>s are md-visually-hidden again, the frame's comment says why, and the section page test asserts it. The Layout section's description said "window size classes"; M3's term is breakpoints. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
151 lines
9.0 KiB
PHP
151 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace NoNameWeb\LivewireMaterial\Showcase;
|
|
|
|
use Illuminate\Support\Str;
|
|
use NoNameWeb\LivewireMaterial\LivewireMaterialServiceProvider;
|
|
|
|
/**
|
|
* The showcase's pages: one per section, grouped as the rail groups them.
|
|
*/
|
|
class Sections
|
|
{
|
|
/**
|
|
* What the showcase's search looks through: every section, every example in it (linked to the
|
|
* example's anchor), and every component, linked to the first section that introduces it.
|
|
*
|
|
* @return list<array{title: string, kind: string, context: string, url: string, keywords: string}>
|
|
*/
|
|
public static function index(): array
|
|
{
|
|
$sections = static::all();
|
|
$components = collect(glob(LivewireMaterialServiceProvider::componentPath().'/*.blade.php'))
|
|
->map(fn (string $path): string => basename($path, '.blade.php'))
|
|
->all();
|
|
$sources = collect($sections)->map(fn (array $section, string $key): string => (string) file_get_contents(static::path($key)));
|
|
|
|
$entries = [];
|
|
$homes = [];
|
|
|
|
foreach ($sections as $key => $section) {
|
|
$url = route('livewire-material.section', $key, false);
|
|
|
|
$entries[] = ['title' => $section['title'], 'kind' => 'Section', 'context' => $section['group'], 'url' => $url, 'keywords' => $section['description']];
|
|
|
|
preg_match_all("/^\\s*'((?:[^'\\\\]|\\\\.)*)'\\s*=>\\s*<<<'BLADE'/m", $sources[$key], $examples);
|
|
|
|
foreach ($examples[1] as $title) {
|
|
$title = stripslashes($title);
|
|
$entries[] = ['title' => $title, 'kind' => 'Example', 'context' => $section['title'], 'url' => $url.'#'.static::anchor($title), 'keywords' => ''];
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
foreach (['/<x-([a-z0-9-]+)>/', '/<x-(?:livewire-material::)?([a-z0-9-]+)(?=[\\s\\/>])/'] as $pattern) {
|
|
foreach ($sources as $key => $source) {
|
|
preg_match_all($pattern, $source, $tags);
|
|
|
|
foreach ($tags[1] as $tag) {
|
|
if (in_array($tag, $components, true)) {
|
|
$homes[$tag] ??= $key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($homes as $tag => $key) {
|
|
$entries[] = ['title' => "<x-{$tag}>", 'kind' => 'Component', 'context' => $sections[$key]['title'], 'url' => route('livewire-material.section', $key, false), 'keywords' => str_replace('-', ' ', $tag)];
|
|
}
|
|
|
|
return $entries;
|
|
}
|
|
|
|
/**
|
|
* The anchor of an example on its section's page.
|
|
*/
|
|
public static function anchor(string $title): string
|
|
{
|
|
return 'example-'.Str::slug($title);
|
|
}
|
|
|
|
/**
|
|
* A section's view file.
|
|
*/
|
|
public static function path(string $key): string
|
|
{
|
|
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}>
|
|
*/
|
|
public static function all(): array
|
|
{
|
|
return [
|
|
'colour' => ['title' => 'Colour', 'icon' => 'palette', 'group' => 'Foundations', 'description' => 'Every colour role of the generated scheme, light and dark.'],
|
|
'type' => ['title' => 'Type', 'icon' => 'text_fields', 'group' => 'Foundations', 'description' => 'The typescale, emphasized styles included, in Google Sans Flex.'],
|
|
'shape' => ['title' => 'Shape', 'icon' => 'interests', 'group' => 'Foundations', 'description' => 'Corner radii and the 35 M3 Expressive shapes.'],
|
|
'elevation' => ['title' => 'Elevation', 'icon' => 'layers', 'group' => 'Foundations', 'description' => 'Shadows for what floats over content.'],
|
|
'motion' => ['title' => 'Motion', 'icon' => 'animation', 'group' => 'Foundations', 'description' => 'Spatial springs and effects easings.'],
|
|
'layout' => ['title' => 'Layout', 'icon' => 'devices', 'group' => 'Foundations', 'description' => 'M3\'s breakpoints, what changes at each, and the scaffold\'s response.'],
|
|
'icons' => ['title' => 'Icons', 'icon' => 'emoji_symbols', 'group' => 'Foundations', 'description' => 'Every Material Symbol, searchable, outlined and filled.'],
|
|
'buttons' => ['title' => 'Buttons', 'icon' => 'smart_button', 'group' => 'Actions', 'description' => 'Buttons, icon buttons, groups, split buttons and FABs.'],
|
|
'menus' => ['title' => 'Menus', 'icon' => 'menu_open', 'group' => 'Actions', 'description' => 'Menus with items, groups, choices and shortcuts.'],
|
|
'chips' => ['title' => 'Chips', 'icon' => 'sell', 'group' => 'Actions', 'description' => 'Assist, filter, input and suggestion chips.'],
|
|
'communication' => ['title' => 'Communication', 'icon' => 'notifications', 'group' => 'Communication', 'description' => 'Badges, snackbars, tooltips, alerts, stats and empty states.'],
|
|
'progress' => ['title' => 'Progress', 'icon' => 'progress_activity', 'group' => 'Communication', 'description' => 'Linear, circular and wavy progress, and the loading indicator.'],
|
|
'containment' => ['title' => 'Containment', 'icon' => 'web_asset', 'group' => 'Containment', 'description' => 'Cards, lists, dividers, dialogs and sheets.'],
|
|
'carousel' => ['title' => 'Carousel', 'icon' => 'view_carousel', 'group' => 'Containment', 'description' => 'Multi-browse, hero, uncontained and full-screen carousels.'],
|
|
'fields' => ['title' => 'Text fields', 'icon' => 'edit_note', 'group' => 'Inputs', 'description' => 'Text fields, selects, checkboxes, radios, switches, choices and search.'],
|
|
'sliders' => ['title' => 'Sliders', 'icon' => 'tune', 'group' => 'Inputs', 'description' => 'Standard, centered and range sliders in five sizes.'],
|
|
'pickers' => ['title' => 'Date pickers', 'icon' => 'calendar_month', 'group' => 'Inputs', 'description' => 'Docked, modal and input date pickers, single and range.'],
|
|
'timepickers' => ['title' => 'Time pickers', 'icon' => 'schedule', 'group' => 'Inputs', 'description' => 'The dial and input time picker.'],
|
|
'bars' => ['title' => 'App bars and tabs', 'icon' => 'toolbar', 'group' => 'Navigation', 'description' => 'Top app bars, toolbars, tabs, section navigation and the account menu.'],
|
|
'navigation' => ['title' => 'Navigation', 'icon' => 'explore', 'group' => 'Navigation', 'description' => 'The navigation bar, the navigation rail and the scaffold.'],
|
|
'data' => ['title' => 'Data', 'icon' => 'table_chart', 'group' => 'Data and pages', 'description' => 'Data tables, sort headers and pagination.'],
|
|
'pages' => ['title' => 'Error pages and mail', 'icon' => 'page_info', 'group' => 'Data and pages', 'description' => 'The HTTP error pages and the Markdown mail theme.'],
|
|
];
|
|
}
|
|
}
|