M3 lists "supporting text + character counter" as a configuration of both text-field variants; nothing in the package drew one (plan step 24, audit docs/audits/m3-alignment/inputs.md § Missing). `counter` on `<x-input>` and `<x-textarea>` now puts `n/max` at the end of the supporting-text row, counted from the control on every input, in the error colour past the maximum, and said as M3's own "Character count, 5/20" from a polite region once typing settles. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
177 lines
11 KiB
PHP
177 lines
11 KiB
PHP
@php
|
|
$examples = [
|
|
'Outlined and filled text fields' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div class="grid content-start gap-4">
|
|
<x-input label="Share name" hint="Recipients see this name" />
|
|
<x-input label="Recipient" icon="mail" type="email" placeholder="name@example.com" clearable value="anna@example.com" />
|
|
<x-input label="Price" prefix="CHF" suffix="per month" type="number" value="12" />
|
|
<x-input label="Share link" value="https://sealshare.test/s/8f3k2" readonly copyable />
|
|
<x-password label="Password" hint="At least 12 characters" />
|
|
<x-input label="Disabled" value="Not editable" disabled />
|
|
</div>
|
|
|
|
<div class="grid content-start gap-4">
|
|
<x-input variant="filled" label="Share name" hint="Recipients see this name" />
|
|
<x-input variant="filled" label="Recipient" icon="mail" type="email" placeholder="name@example.com" clearable value="anna@example.com" />
|
|
<x-input variant="filled" label="Price" prefix="CHF" suffix="per month" type="number" value="12" />
|
|
<x-input variant="filled" label="Search" icon="search" icon-right="mic" />
|
|
<x-password variant="filled" label="Password" hint="At least 12 characters" />
|
|
<x-input variant="filled" label="Disabled" value="Not editable" disabled />
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Errors, required and sizes' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div class="grid content-start gap-4">
|
|
<x-input label="Email" required />
|
|
<x-field id="showcase-error" label="Expiry" :messages="['Choose a date in the future.']">
|
|
<input id="showcase-error" class="field-control" value="2020-01-01" aria-invalid="true" />
|
|
</x-field>
|
|
</div>
|
|
|
|
<div class="grid content-start gap-4">
|
|
<x-field id="showcase-error-filled" variant="filled" label="Expiry" :messages="['Choose a date in the future.']">
|
|
<input id="showcase-error-filled" class="field-control" value="2020-01-01" aria-invalid="true" />
|
|
</x-field>
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<x-input size="sm" icon="search" placeholder="Filter" aria-label="Filter" class="w-48" />
|
|
<x-input size="xs" placeholder="Find" aria-label="Find" class="w-36" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Character counter' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div class="grid content-start gap-4">
|
|
<x-input label="Share name" maxlength="40" counter hint="Recipients see this name" />
|
|
<x-input variant="filled" label="Subject" maxlength="60" counter />
|
|
</div>
|
|
|
|
<x-textarea label="Message for recipients" maxlength="180" counter rows="3" hint="Sent with the link" />
|
|
</div>
|
|
BLADE,
|
|
'Textarea and select' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div class="grid content-start gap-4">
|
|
<x-textarea label="Message" hint="Grows as you type, up to six lines" rows="2" max-rows="6" />
|
|
<x-select label="Expires after" :options="[['id' => 1, 'name' => '1 hour'], ['id' => 24, 'name' => '1 day'], ['id' => 168, 'name' => '7 days'], ['id' => 720, 'name' => '30 days', 'disabled' => true]]" />
|
|
<x-file label="Attachments" multiple hint="Up to 2 GB" />
|
|
</div>
|
|
|
|
<div class="grid content-start gap-4">
|
|
<x-textarea variant="filled" label="Message" hint="Grows as you type, up to six lines" rows="2" max-rows="6" />
|
|
<x-select variant="filled" label="Expires after" icon="schedule" placeholder="Choose…" :options="[['id' => 1, 'name' => '1 hour'], ['id' => 24, 'name' => '1 day'], ['id' => 168, 'name' => '7 days']]" />
|
|
<x-file variant="filled" label="Attachments" multiple />
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Checkboxes' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 expanded:grid-cols-3">
|
|
<div class="grid content-start gap-4">
|
|
<x-checkbox label="Notify me on download" checked />
|
|
<x-checkbox label="Select all files" indeterminate />
|
|
<x-checkbox label="Burn after reading" hint="The share is deleted after its first download" />
|
|
</div>
|
|
<div class="grid content-start gap-4">
|
|
<x-checkbox label="Disabled" disabled />
|
|
<x-checkbox label="Disabled and ticked" checked disabled />
|
|
<x-checkbox label="Disabled, partly ticked" indeterminate disabled />
|
|
</div>
|
|
<div class="grid content-start gap-4">
|
|
<x-checkbox label="Show in the dashboard" right />
|
|
<x-checkbox aria-label="Unlabelled" />
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Radio buttons' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<x-radio label="Who can open the link" name="showcase-audience" value="password" :options="[['id' => 'anyone', 'name' => 'Anyone with the link'], ['id' => 'password', 'name' => 'Anyone with the password', 'hint' => 'Share the password separately'], ['id' => 'team', 'name' => 'My team only', 'disabled' => true]]" />
|
|
<x-radio label="Theme" name="showcase-radio-theme" value="system" inline :options="[['id' => 'light', 'name' => 'Light'], ['id' => 'dark', 'name' => 'Dark'], ['id' => 'system', 'name' => 'System']]" hint="Inline from sm" />
|
|
</div>
|
|
BLADE,
|
|
'Switches' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 expanded:grid-cols-3">
|
|
<div class="grid content-start gap-4">
|
|
<x-toggle label="Email notifications" checked />
|
|
<x-toggle label="Weekly summary" />
|
|
</div>
|
|
<div class="grid content-start gap-4">
|
|
<x-toggle label="With icons" icons checked />
|
|
<x-toggle label="With icons, off" icons />
|
|
<x-toggle label="Only the check" icons="selected" checked />
|
|
</div>
|
|
<div class="grid content-start gap-4">
|
|
<x-toggle label="Disabled" disabled />
|
|
<x-toggle label="Disabled, on" checked disabled icons />
|
|
<x-toggle label="Public link" hint="Anyone with the link can open it" right />
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Choices' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div class="grid content-start gap-6">
|
|
<x-choices label="Days you are free" hint="Filter chips: any number" :value="[1, 3]" :options="[['id' => 1, 'name' => 'Mon'], ['id' => 2, 'name' => 'Tue'], ['id' => 3, 'name' => 'Wed'], ['id' => 4, 'name' => 'Thu'], ['id' => 5, 'name' => 'Fri'], ['id' => 6, 'name' => 'Sat', 'disabled' => true]]" />
|
|
<x-choices label="Expires after" single :value="24" :options="[['id' => 1, 'name' => '1 hour'], ['id' => 24, 'name' => '1 day'], ['id' => 168, 'name' => '7 days']]" />
|
|
</div>
|
|
|
|
<div class="grid content-start gap-4">
|
|
<x-choices label="Time zone" searchable icon="public" value="Europe/Zurich" :options="collect(timezone_identifiers_list())->map(fn ($zone) => ['id' => $zone, 'name' => str_replace('_', ' ', $zone)])->all()" />
|
|
<x-choices label="Language" searchable variant="filled" placeholder="Type a language" :options="[['id' => 'de', 'name' => 'Deutsch'], ['id' => 'en', 'name' => 'English'], ['id' => 'fr', 'name' => 'Français'], ['id' => 'it', 'name' => 'Italiano'], ['id' => 'rm', 'name' => 'Rumantsch', 'disabled' => true]]" />
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'Search' => <<<'BLADE'
|
|
<div class="grid w-full gap-6 medium:grid-cols-2">
|
|
<div x-data="{ query: '' }">
|
|
<x-search x-model="query" placeholder="Search shares">
|
|
<x-list>
|
|
<x-list-item title="holiday-photos.zip" description="248 MB · 3 days left" icon="folder_zip" link="#fields" x-show="'holiday-photos.zip'.includes(query.toLowerCase())" />
|
|
<x-list-item title="contract.pdf" description="1.2 MB · 1 hour left" icon="picture_as_pdf" link="#fields" x-show="'contract.pdf'.includes(query.toLowerCase())" />
|
|
<x-list-item title="design-review.mp4" description="3.4 GB · 7 days left" icon="movie" link="#fields" x-show="'design-review.mp4'.includes(query.toLowerCase())" />
|
|
</x-list>
|
|
<x-slot:trailing>
|
|
<span class="grid size-8 place-items-center rounded-corner-full bg-primary-container type-label-lg text-on-primary-container">AR</span>
|
|
</x-slot:trailing>
|
|
</x-search>
|
|
</div>
|
|
|
|
{{-- M3's resting bar is 360px and grows to 720 while focused; --search-width sets the resting one. --}}
|
|
<div style="--search-width: 22.5rem">
|
|
<x-search placeholder="Search in settings" docked>
|
|
<x-slot:empty>Type to search your settings.</x-slot:empty>
|
|
</x-search>
|
|
</div>
|
|
</div>
|
|
BLADE,
|
|
'A form' => <<<'BLADE'
|
|
<x-card variant="outlined" class="w-full max-w-xl">
|
|
<x-form x-on:submit.prevent="materialToast('Share created', { type: 'success' })" separator>
|
|
<x-input label="Share name" required />
|
|
<x-textarea label="Message for recipients" />
|
|
<x-select label="Expires after" :options="[['id' => 24, 'name' => '1 day'], ['id' => 168, 'name' => '7 days']]" />
|
|
<x-toggle label="Protect with a password" right />
|
|
|
|
<x-slot:actions>
|
|
<x-button label="Cancel" type="reset" />
|
|
<x-button label="Create share" variant="filled" type="submit" />
|
|
</x-slot:actions>
|
|
</x-form>
|
|
</x-card>
|
|
BLADE,
|
|
];
|
|
@endphp
|
|
|
|
<section id="fields" class="scroll-mt-24 space-y-6">
|
|
<h2 class="type-headline-md">Text fields and selection</h2>
|
|
|
|
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
|
<code><x-input></code>, <code><x-password></code>, <code><x-textarea></code>, <code><x-select></code>, <code><x-file></code>
|
|
(all on <code><x-field></code>, outlined or filled), <code><x-checkbox></code>, <code><x-radio></code>, <code><x-toggle></code>, <code><x-choices></code>, <code><x-search></code> and <code><x-form></code>.
|
|
</p>
|
|
|
|
@foreach ($examples as $title => $code)
|
|
<x-showcase::example :$title :$code />
|
|
@endforeach
|
|
</section>
|