Files
3dealer/resources/views/components/ui/range-slider.blade.php
T

122 lines
5.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@props([
'min',
'max',
'minValue' => null,
'maxValue' => null,
'step' => 1,
'name' => null,
'legend' => 'Range',
'minLabel' => 'Minimum',
'maxLabel' => 'Maximum',
'prefix' => '',
'suffix' => '',
'separator' => ' – ',
])
{{-- The default slot sits on the readout row, to the right of the min–max
text — use it for a "clear"/"reset" control when the slider is filtered.
Left empty otherwise. --}}
@php
$minValue ??= $min;
$maxValue ??= $max;
// Stable when a name is given so focus can survive a re-render; random
// otherwise, just to keep the label/input association unique on the page.
$uid = 'rs-' . ($name ?: uniqid());
@endphp
<div
data-controller="range-slider"
data-range-slider-min-value="{{ $min }}"
data-range-slider-max-value="{{ $max }}"
data-range-slider-step-value="{{ $step }}"
data-range-slider-prefix-value="{{ $prefix }}"
data-range-slider-suffix-value="{{ $suffix }}"
data-range-slider-separator-value="{{ $separator }}"
{{ $attributes->merge(['class' => 'flex flex-col gap-4']) }}
>
<fieldset class="m-0 min-w-0 border-0 p-0">
<legend class="sr-only">{{ $legend }}</legend>
{{-- Real controls — keyboard, assistive tech, form values, no-JS
fallback. The controller adds `sr-only` to each on connect. --}}
<label data-range-slider-target="field" class="flex items-center gap-2 text-sm">
<span>{{ $minLabel }}</span>
<input
type="range"
id="{{ $uid }}-min"
min="{{ $min }}"
max="{{ $max }}"
step="{{ $step }}"
value="{{ $minValue }}"
@if($name) name="{{ $name }}_min" @endif
data-range-slider-target="minInput"
data-action="input->range-slider#onInput change->range-slider#onChange focus->range-slider#syncFocus blur->range-slider#syncFocus"
>
</label>
<label data-range-slider-target="field" class="flex items-center gap-2 text-sm">
<span>{{ $maxLabel }}</span>
<input
type="range"
id="{{ $uid }}-max"
min="{{ $min }}"
max="{{ $max }}"
step="{{ $step }}"
value="{{ $maxValue }}"
@if($name) name="{{ $name }}_max" @endif
data-range-slider-target="maxInput"
data-action="input->range-slider#onInput change->range-slider#onChange focus->range-slider#syncFocus blur->range-slider#syncFocus"
>
</label>
{{-- Presentational track — pointer control + visual state only; the
real controls above own accessibility. --min / --max start at
the extremes so first paint has no jump. --}}
<div
data-range-slider-target="track"
data-action="pointerdown->range-slider#trackPointerDown"
aria-hidden="true"
class="relative h-8 touch-none select-none [--min:0%] [--max:100%]"
>
<span class="pointer-events-none absolute inset-x-0 top-1/2 h-1.25 -translate-y-1/2 bg-neutral-500"></span>
<span class="pointer-events-none absolute top-1/2 left-[var(--min)] right-[calc(100%_-_var(--max))] h-1.25 -translate-y-1/2 bg-black"></span>
<button
type="button"
tabindex="-1"
data-range-slider-target="minThumb"
data-action="pointerdown->range-slider#thumbPointerDown"
class="absolute top-1/2 left-[var(--min)] grid h-8 w-6 -translate-x-1/4 -translate-y-1/2 cursor-grab place-items-center text-black active:cursor-grabbing"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 32" fill="none" aria-hidden="true" class="h-[32px] w-[22px]">
<path d="M17 4 L6 16 L17 28" stroke="currentColor" stroke-width="5" stroke-linecap="square" stroke-linejoin="miter" />
</svg>
</button>
<button
type="button"
tabindex="-1"
data-range-slider-target="maxThumb"
data-action="pointerdown->range-slider#thumbPointerDown"
class="absolute top-1/2 left-[var(--max)] grid h-8 w-6 -translate-x-1/2 -translate-y-1/2 cursor-grab place-items-center text-black active:cursor-grabbing"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 32" fill="none" aria-hidden="true" class="h-[32px] w-[22px]">
<path d="M5 4 L16 16 L5 28" stroke="currentColor" stroke-width="5" stroke-linecap="square" stroke-linejoin="miter" />
</svg>
</button>
</div>
</fieldset>
<div class="flex items-center justify-between gap-4">
{{-- Server-rendered so it's right on first paint and without JS; the
controller rewrites it as the values change. --}}
<span data-range-slider-target="output" aria-hidden="true" class="text-sm tabular-nums"
>{{ $prefix }}{{ $minValue }}{{ $suffix }}{{ $separator }}{{ $prefix }}{{ $maxValue }}{{ $suffix }}</span>
{{ $slot }}
</div>
</div>