tests / feature (8.4) (push) Successful in 2m0s
tests / feature (8.5) (push) Successful in 2m0s
tests / browser (chrome, chromium) (push) Failing after 8m3s
tests / browser (firefox, firefox) (push) Failing after 12m58s
tests / browser (safari, webkit) (push) Failing after 13m8s
Tailwind left the stack in 2.0.0, but the package still carried about 330 mentions of it. What the guard's Tailwind detection protected — a class that compiles to nothing — is now protected by a check that does not care where a dead class came from. DesignGuard: about 500 lines of Tailwind tables, scales, palettes and "2.0.0 replacement" hints give way to one check — a class a view or PHP file writes that neither the application's stylesheets nor the package's own declare. It catches a utility of any framework, a typo and a class whose rules were deleted alike, so it also found two classes ReStride draws nothing with. A stylesheet has to be in reach for it: the `.css` files among the scanned paths, or what the `missingStylesheets()` entry imports. The class reader no longer mistakes an array index for a class list (`$block['base']`), and it reads the array a class helper is given, where it read nothing before. The package's own three Tailwind self-guards go with it. Only their one unique check stays, as a test of its own: every `matchMedia` width in resources/js is an M3 breakpoint. The pagination views are `material.blade.php` and `simple-material.blade.php`; only Laravel's and Livewire's default theme names ever made them `tailwind`. The provider sets `Paginator`'s default views and switches `livewire.pagination_theme` to `material` when it is still Livewire's own default, so no application can forget the config; a theme an application chose, and a component's own `$paginationTheme` or `paginationView()`, still win. The rest is prose: the layer-order guidance for an application that still builds Tailwind, the Tailwind wording in the README, the Boost guidelines and the development skill, and about 25 "this used to be a Tailwind utility" comments, along with every "plan step NN" pointer into a gitignored folder. The reset keeps its credit, and NOTICE now carries it too. Feature suite 1159 passed, Chrome browser suite 299 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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: 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: 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.'],
|
|
];
|
|
}
|
|
}
|