tests / feature (8.5) (push) Successful in 1m14s
tests / lint (push) Successful in 1m1s
tests / feature (8.4) (push) Successful in 1m9s
tests / browser (chrome, chromium) (push) Successful in 3m22s
tests / browser (firefox, firefox) (push) Successful in 4m37s
tests / browser (safari, webkit) (push) Successful in 5m4s
A search app bar looks through every section, example and component, opens the section, or the example at its anchor on its page, and is reached from anywhere with / or Ctrl+K. Section pages now carry their own heading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
109 lines
6.6 KiB
PHP
109 lines
6.6 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' => ''];
|
|
}
|
|
}
|
|
|
|
// 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';
|
|
}
|
|
|
|
/**
|
|
* @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.'],
|
|
'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 app shell.'],
|
|
'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.'],
|
|
];
|
|
}
|
|
}
|