Skip to main content

General

Range code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/range.blade.php

@props([
    'name' => null,
    'value' => 50,
    'min' => 0,
    'max' => 100,
    'step' => 1,
    'showValue' => false,
    'prefix' => '',
    'suffix' => '',
    'disabled' => false,
])

@php
    /*
    | Range slider with a brand-colored filled track and optional live value
    | bubble. The fill is driven by Alpine so it works across browsers.
    |
    | Usage:
    |   <x-ui.range name="volume" :value="40" show-value suffix="%" />
    |   <x-ui.range name="price" :min="0" :max="500" :step="10" :value="250" prefix="$" show-value />
    */
    $min = (float) $min;
    $max = (float) $max;
    $normalizedName = $name
        ? trim((string) preg_replace('/[^A-Za-z0-9\-_:.]+/', '-', str_replace(['[', ']'], '', (string) $name)), '-')
        : null;
    $inputId = $attributes->get('id') ?: $normalizedName;
    $providedAriaLabel = $attributes->get('aria-label');
    $providedAriaLabelledBy = $attributes->get('aria-labelledby');
    $fallbackLabel = null;

    if (! $providedAriaLabel && ! $providedAriaLabelledBy && ! $attributes->get('id')) {
        $fallbackLabel = $name
            ? Str::headline(str_replace(['-', '_'], ' ', $normalizedName ?? (string) $name))
            : 'Range slider';
    }
@endphp

<div
    x-data="{
        value: @js((float) $value),
        min: @js($min),
        max: @js($max),
        get percent() {
            return this.max > this.min ? ((this.value - this.min) / (this.max - this.min)) * 100 : 0;
        },
    }"
    {{ $attributes->only('class')->merge(['class' => 'w-full']) }}
>
    @if ($showValue)
        <div class="mb-2 flex items-center justify-between text-sm">
            <span class="font-medium text-gray-700 dark:text-gray-300">{{ $prefix }}<span x-text="value"></span>{{ $suffix }}</span>
        </div>
    @endif

    <input
        type="range"
        @if ($name) name="{{ $name }}" @endif
        @if ($inputId) id="{{ $inputId }}" @endif
        @if ($fallbackLabel) aria-label="{{ $fallbackLabel }}" @endif
        min="{{ $min }}"
        max="{{ $max }}"
        step="{{ $step }}"
        x-model="value"
        @disabled($disabled)
        :style="`background: linear-gradient(to right, var(--color-brand-600) ${percent}%, var(--color-gray-200) ${percent}%)`"
        class="h-2 w-full cursor-pointer appearance-none rounded-full bg-gray-200 dark:bg-gray-800 outline-none transition disabled:opacity-50 disabled:cursor-not-allowed
               [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-brand-600 [&::-webkit-slider-thumb]:shadow [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:transition
               [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-brand-600 [&::-moz-range-thumb]:shadow [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-track]:bg-transparent"
        {{ $attributes->except(['class', 'id', 'aria-label']) }}
    >
</div>