Show both symbol cuts in the showcase icon search
The search draws its 4,135 symbols as CSS masks through the symbol route, so the route learned `?optical=20` — the 24 cut otherwise, as on <x-icon> — and the section got a checkbox that switches the cut and draws the grid at 20px, where the difference in stroke weight is the point. Plan step 10; finding C16. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
699827d696
commit
1134dbac0b
@@ -5,6 +5,7 @@
|
|||||||
names: [],
|
names: [],
|
||||||
query: '',
|
query: '',
|
||||||
filled: false,
|
filled: false,
|
||||||
|
optical20: false,
|
||||||
async load() {
|
async load() {
|
||||||
this.names = await (await fetch('{{ route('livewire-material.symbols', [], false) }}')).json();
|
this.names = await (await fetch('{{ route('livewire-material.symbols', [], false) }}')).json();
|
||||||
},
|
},
|
||||||
@@ -19,7 +20,9 @@
|
|||||||
<h2 class="type-headline-md">Icons</h2>
|
<h2 class="type-headline-md">Icons</h2>
|
||||||
|
|
||||||
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
<p class="max-w-3xl type-body-md text-on-surface-variant">
|
||||||
Every Material Symbol (Rounded, 400, grade 0, 24px) as <code><x-icon name="…" /></code>, outlined or <code>filled</code>.
|
Every Material Symbol (Rounded, 400, grade 0) as <code><x-icon name="…" /></code>, outlined or <code>filled</code>,
|
||||||
|
drawn from the optical size 24 cut or — for an icon 20px or smaller, where 24's strokes would look thin —
|
||||||
|
the 20 cut: <code><x-icon name="…" optical="20" class="size-5" /></code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-4">
|
<div class="flex flex-wrap items-center gap-4">
|
||||||
@@ -32,15 +35,21 @@
|
|||||||
<input type="checkbox" x-model="filled" class="size-4 accent-primary" />
|
<input type="checkbox" x-model="filled" class="size-4 accent-primary" />
|
||||||
Filled
|
Filled
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="flex items-center gap-2 type-label-lg">
|
||||||
|
<input type="checkbox" x-model="optical20" class="size-4 accent-primary" />
|
||||||
|
Optical size 20
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="grid grid-cols-[repeat(auto-fill,minmax(7.5rem,1fr))] gap-2">
|
<ul class="grid grid-cols-[repeat(auto-fill,minmax(7.5rem,1fr))] gap-2">
|
||||||
<template x-for="name in matches" x-bind:key="name">
|
<template x-for="name in matches" x-bind:key="name">
|
||||||
<li class="flex flex-col items-center gap-2 rounded-corner-md bg-surface-container p-3 text-center">
|
<li class="flex flex-col items-center gap-2 rounded-corner-md bg-surface-container p-3 text-center">
|
||||||
<span
|
<span
|
||||||
class="size-6 bg-current"
|
class="bg-current"
|
||||||
|
x-bind:class="optical20 ? 'size-5' : 'size-6'"
|
||||||
x-bind:style="{
|
x-bind:style="{
|
||||||
mask: `url('{{ rtrim(route('livewire-material.showcase', [], false), '/') }}/symbols/${filled ? 'filled' : 'outlined'}/${name}.svg') center / contain no-repeat`,
|
mask: `url('{{ rtrim(route('livewire-material.showcase', [], false), '/') }}/symbols/${filled ? 'filled' : 'outlined'}/${name}.svg${optical20 ? '?optical=20' : ''}') center / contain no-repeat`,
|
||||||
}"
|
}"
|
||||||
></span>
|
></span>
|
||||||
<code class="w-full break-all type-label-sm text-on-surface-variant" x-text="name"></code>
|
<code class="w-full break-all type-label-sm text-on-surface-variant" x-text="name"></code>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace NoNameWeb\LivewireMaterial\Http\Controllers;
|
namespace NoNameWeb\LivewireMaterial\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\View\ComponentAttributeBag;
|
use Illuminate\View\ComponentAttributeBag;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
@@ -20,12 +21,16 @@ class ShowcaseSymbolController
|
|||||||
return response()->json(SvgFile::symbolNames())->setMaxAge(86400)->setPublic();
|
return response()->json(SvgFile::symbolNames())->setMaxAge(86400)->setPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(string $style, string $name): Response
|
/**
|
||||||
|
* The standard 24 cut, or the 20 one with `?optical=20`; any other value is the 24 cut,
|
||||||
|
* as it is on `<x-icon>`.
|
||||||
|
*/
|
||||||
|
public function show(Request $request, string $style, string $name): Response
|
||||||
{
|
{
|
||||||
abort_unless(in_array($style, ['outlined', 'filled'], true), 404);
|
abort_unless(in_array($style, ['outlined', 'filled'], true), 404);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$svg = SvgFile::symbol($name, $style === 'filled', new ComponentAttributeBag);
|
$svg = SvgFile::symbol($name, $style === 'filled', new ComponentAttributeBag, $request->integer('optical', 24));
|
||||||
} catch (InvalidArgumentException) {
|
} catch (InvalidArgumentException) {
|
||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,22 @@ it('serves a symbol for the icon search', function () {
|
|||||||
->assertSee('viewBox="0 -960 960 960"', false);
|
->assertSee('viewBox="0 -960 960 960"', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('serves the 24 cut by default and the 20 cut on request', function () {
|
||||||
|
$geometry = function (string $folder): string {
|
||||||
|
$svg = trim((string) file_get_contents(__DIR__."/../../resources/svg/symbols/{$folder}/home.svg"));
|
||||||
|
|
||||||
|
return substr($svg, (int) strpos($svg, '<path'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$standard = $this->get('/material/symbols/outlined/home.svg')->assertOk()->getContent();
|
||||||
|
$dense = $this->get('/material/symbols/outlined/home.svg?optical=20')->assertOk()->getContent();
|
||||||
|
|
||||||
|
expect($dense)->not->toBe($standard)
|
||||||
|
->and($standard)->toContain($geometry('outlined'))
|
||||||
|
->and($dense)->toContain($geometry('outlined-20'))
|
||||||
|
->and($this->get('/material/symbols/outlined/home.svg?optical=40')->getContent())->toBe($standard);
|
||||||
|
});
|
||||||
|
|
||||||
it('answers 404 for a symbol or style that does not exist', function () {
|
it('answers 404 for a symbol or style that does not exist', function () {
|
||||||
$this->get('/material/symbols/outlined/not_a_symbol_at_all.svg')->assertNotFound();
|
$this->get('/material/symbols/outlined/not_a_symbol_at_all.svg')->assertNotFound();
|
||||||
$this->get('/material/symbols/sharp/favorite.svg')->assertNotFound();
|
$this->get('/material/symbols/sharp/favorite.svg')->assertNotFound();
|
||||||
|
|||||||
Reference in New Issue
Block a user