Draw the empty state without Tailwind
Plan step 36 (actions, last of the batch). <x-empty-state> renders data-md-empty-state with data-md-empty-state-illustration/-shape/ -icon/-title/-description/-actions; no class list. empty-state.css draws the secondary-container shape behind the on-secondary-container icon (the icon needs its own stacking context to paint above the shape's absolute position), the title-large title and the 448px body-medium description. The docblock's illustration example moves from a caller class to a caller style, since the shape rule scans the whole file. EmptyStateTest is rewritten on the hooks and ComponentStylesheet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
4cc905b144
commit
99e73fdc58
@@ -31,6 +31,7 @@
|
||||
@import './components/progress.css';
|
||||
@import './components/alert.css';
|
||||
@import './components/stat.css';
|
||||
@import './components/empty-state.css';
|
||||
|
||||
/* Inputs, selection and data */
|
||||
@import './components/form.css';
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* <x-empty-state>: "you have nothing here yet" — not an M3 component; built from M3 Expressive's
|
||||
* parts, a shape in secondary-container behind an icon in on-secondary-container, a title-large
|
||||
* title, body-medium description at 448px and a centred row of actions.
|
||||
*
|
||||
* The shape is `position: absolute`, so the icon needs its own stacking context
|
||||
* (`position: relative`) to paint above it in DOM order rather than losing to the shape's own —
|
||||
* an application's illustration slot replaces both and sizes itself.
|
||||
*/
|
||||
|
||||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||||
|
||||
@import './icon.css';
|
||||
@import './shape.css';
|
||||
|
||||
@layer material.components {
|
||||
[data-md-empty-state] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--md-sys-measurement-space200);
|
||||
padding-inline: var(--md-sys-measurement-space200);
|
||||
padding-block: var(--md-sys-measurement-space500);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
[data-md-empty-state-illustration] {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
[data-md-empty-state-shape] {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--md-sys-color-secondary-container);
|
||||
}
|
||||
|
||||
[data-md-empty-state-icon] {
|
||||
position: relative;
|
||||
color: var(--md-sys-color-on-secondary-container);
|
||||
}
|
||||
|
||||
[data-md-empty-state-title] {
|
||||
font: var(--md-sys-typescale-title-lg);
|
||||
letter-spacing: var(--md-sys-typescale-title-lg-tracking);
|
||||
font-variation-settings: normal;
|
||||
}
|
||||
|
||||
[data-md-empty-state-description] {
|
||||
max-width: 448px;
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
font: var(--md-sys-typescale-body-md);
|
||||
letter-spacing: var(--md-sys-typescale-body-md-tracking);
|
||||
font-variation-settings: normal;
|
||||
}
|
||||
|
||||
[data-md-empty-state-actions] {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding-top: var(--md-sys-measurement-space100);
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,15 @@
|
||||
|
||||
An application with its own artwork puts it in the `illustration` slot, which is drawn in place
|
||||
of the shape and icon (both props are then unused). The slot sizes itself; its attributes go on
|
||||
the element around it, so `class` can set the colour an SVG's `currentColor` takes:
|
||||
the element around it, so `style` can set the colour an SVG's `currentColor` takes:
|
||||
|
||||
<x-empty-state title="No routes yet">
|
||||
<x-slot:illustration class="text-primary"><svg class="size-32" aria-hidden="true">…</svg></x-slot:illustration>
|
||||
<x-slot:illustration style="color: var(--md-sys-color-primary)"><svg style="width: 128px; height: 128px" aria-hidden="true">…</svg></x-slot:illustration>
|
||||
</x-empty-state>
|
||||
|
||||
A slot holding only whitespace or comments counts as empty, and the shape and icon stay. --}}
|
||||
A slot holding only whitespace or comments counts as empty, and the shape and icon stay.
|
||||
|
||||
Drawn by resources/css/components/empty-state.css from `data-md-empty-state`; no class list. --}}
|
||||
|
||||
@props([
|
||||
'icon' => 'inbox',
|
||||
@@ -27,25 +29,25 @@
|
||||
'description' => null,
|
||||
])
|
||||
|
||||
<div {{ $attributes->class('flex flex-col items-center gap-4 px-4 py-10 text-center') }}>
|
||||
<div data-md-empty-state {{ $attributes }}>
|
||||
@if (isset($illustration) && $illustration->hasActualContent())
|
||||
<div {{ $illustration->attributes }}>{{ $illustration }}</div>
|
||||
@else
|
||||
<div class="relative grid size-28 place-items-center">
|
||||
<x-livewire-material::shape :name="$shape" class="absolute inset-0 size-full text-secondary-container" />
|
||||
<x-livewire-material::icon :name="$icon" class="relative size-12 text-on-secondary-container" />
|
||||
<div data-md-empty-state-illustration>
|
||||
<x-livewire-material::shape :name="$shape" data-md-empty-state-shape />
|
||||
<x-livewire-material::icon :name="$icon" size="48" data-md-empty-state-icon />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($title)
|
||||
<h3 class="type-title-lg">{{ $title }}</h3>
|
||||
<h3 data-md-empty-state-title>{{ $title }}</h3>
|
||||
@endif
|
||||
|
||||
@if ($description || $slot->isNotEmpty())
|
||||
<div class="max-w-md type-body-md text-on-surface-variant">{{ $description ?? $slot }}</div>
|
||||
<div data-md-empty-state-description>{{ $description ?? $slot }}</div>
|
||||
@endif
|
||||
|
||||
@isset($actions)
|
||||
<div class="flex flex-wrap justify-center gap-3 pt-2">{{ $actions }}</div>
|
||||
<div data-md-empty-state-actions>{{ $actions }}</div>
|
||||
@endisset
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user