Draw the sort header without Tailwind
<x-sort-header> renders data-md-sort-header (data-md-active on the sorted column) with its button and arrow as data-md-* attributes; the button keeps md-focus-ring and md-touch-target, the arrow takes size 16, and the hover and hint-arrow states move into components/sort-header.css (plan step 36). A browser test presses the button at the edge of its 48px target. 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
25e1bb4788
commit
c68a1fdbef
@@ -30,6 +30,7 @@
|
||||
@import './components/slider.css';
|
||||
@import './components/search.css';
|
||||
@import './components/table.css';
|
||||
@import './components/sort-header.css';
|
||||
|
||||
/* Containment */
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* <x-sort-header>: a column header that sorts the table (resources/views/components/sort-header.blade.php),
|
||||
* inside <x-table> (components/table.css), which draws the cell itself.
|
||||
*
|
||||
* The button is the header's own text, in the header cell's type and colour, with a 16px arrow 4px
|
||||
* after it; the corner is extra-small, for the focus ring the button wears (`md-focus-ring`), and
|
||||
* `md-touch-target` gives the one-line button M3's 48px target (foundation/interaction.css). The
|
||||
* sorted column (`data-md-active`) reads in on-surface with its arrow showing the direction; another
|
||||
* column turns on-surface only while hovered, and shows a faint upward arrow while hovered or
|
||||
* focused, at 60% of the ink — a hint of what pressing would do, not a state M3 names, fading on the
|
||||
* fast effects spring.
|
||||
*
|
||||
* [data-md-sort-header] the <th>; data-md-active, aria-sort
|
||||
* [data-md-sort-header-button]
|
||||
* [data-md-sort-header-arrow]
|
||||
*/
|
||||
|
||||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||||
|
||||
@import './icon.css';
|
||||
|
||||
@layer material.components {
|
||||
[data-md-sort-header-button] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--md-sys-measurement-space50);
|
||||
border-radius: var(--md-sys-shape-corner-xs);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-md-sort-header][data-md-active] [data-md-sort-header-button] {
|
||||
color: var(--md-sys-color-on-surface);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
[data-md-sort-header-button]:hover {
|
||||
color: var(--md-sys-color-on-surface);
|
||||
}
|
||||
}
|
||||
|
||||
[data-md-sort-header-arrow] {
|
||||
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
|
||||
}
|
||||
|
||||
[data-md-sort-header]:not([data-md-active]) [data-md-sort-header-arrow] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
[data-md-sort-header]:not([data-md-active]) [data-md-sort-header-button]:focus-visible [data-md-sort-header-arrow] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
[data-md-sort-header]:not([data-md-active]) [data-md-sort-header-button]:hover [data-md-sort-header-arrow] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
Pressing it sorts by this column, ascending first and then flipping; the arrow says which way,
|
||||
and `aria-sort` says it to a screen reader. A column that cannot be sorted is a plain `<th>`.
|
||||
`class` lands on the `<th>` (`text-end` for a number column moves the button with it).
|
||||
`class` lands on the `<th>` (end-aligning a number column moves the button with it).
|
||||
|
||||
The button is only as tall as its title-small line, so it carries `touch-target` and catches
|
||||
presses over M3's 48px minimum; the cell's own padding belongs to the cell, not to it. --}}
|
||||
The button is only as tall as its title-small line, so it carries `md-touch-target` and catches
|
||||
presses over M3's 48px minimum; the cell's own padding belongs to the cell, not to it. The cell
|
||||
renders `data-md-sort-header` (`data-md-active` on the sorted column) and the button
|
||||
`data-md-sort-header-button`, drawn by resources/css/components/sort-header.css. --}}
|
||||
|
||||
@props([
|
||||
'sortBy' => [],
|
||||
@@ -20,21 +22,14 @@
|
||||
$next = $active && $direction === 'asc' ? 'desc' : 'asc';
|
||||
@endphp
|
||||
|
||||
<th @if ($active) aria-sort="{{ $direction === 'asc' ? 'ascending' : 'descending' }}" @endif {{ $attributes }}>
|
||||
<th data-md-sort-header @if ($active) data-md-active aria-sort="{{ $direction === 'asc' ? 'ascending' : 'descending' }}" @endif {{ $attributes }}>
|
||||
<button
|
||||
type="button"
|
||||
wire:click="$set('{{ $model }}', {{ json_encode(['column' => $column, 'direction' => $next]) }})"
|
||||
data-sort-header
|
||||
@class([
|
||||
'group/sort focus-ring touch-target inline-flex cursor-pointer items-center gap-1 rounded-corner-xs',
|
||||
'text-on-surface' => $active,
|
||||
'hover:text-on-surface' => ! $active,
|
||||
])
|
||||
data-md-sort-header-button
|
||||
class="md-focus-ring md-touch-target"
|
||||
>
|
||||
{{ $slot }}
|
||||
<x-livewire-material::icon :name="$direction === 'desc' ? 'arrow_downward' : 'arrow_upward'" optical="20" :class="\Illuminate\Support\Arr::toCssClasses([
|
||||
'size-4 transition-opacity duration-(--md-sys-motion-effects-fast-duration)',
|
||||
'opacity-0 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60' => ! $active,
|
||||
])" />
|
||||
<x-livewire-material::icon :name="$direction === 'desc' ? 'arrow_downward' : 'arrow_upward'" size="16" data-md-sort-header-arrow />
|
||||
</button>
|
||||
</th>
|
||||
|
||||
Reference in New Issue
Block a user