import type { ReactNode, Ref } from 'react'; import { Label as AriaLabel, Slider as AriaSlider, SliderFill as AriaSliderFill, SliderOutput as AriaSliderOutput, SliderThumb as AriaSliderThumb, SliderTrack as AriaSliderTrack, Text as AriaText, } from 'react-aria-components'; import { classes } from '../internal/classes'; import fieldStyles from '../internal/AriaField.module.css'; import styles from './Slider.module.css'; export type SliderValue = number | [number, number]; export type SliderOrientation = 'horizontal' | 'vertical'; export interface SliderProps { /** Visible label. Always rendered; `hideLabel` keeps it for assistive technology only. */ label: ReactNode; /** @default false */ hideLabel?: boolean; description?: ReactNode; /** One number, or a `[start, end]` pair for a range with two thumbs. */ value?: T; defaultValue?: T; /** Called on every change while dragging or pressing arrows. */ onChange?: (value: T) => void; /** Called once when the drag ends or the key is released. */ onChangeEnd?: (value: T) => void; /** @default 0 */ min?: number; /** @default 100 */ max?: number; /** @default 1 */ step?: number; /** How the value reads in the output and to assistive technology, for instance `{ style: 'percent' }` or `{ style: 'currency', currency: 'EUR' }`. */ formatOptions?: Intl.NumberFormatOptions; /** Names for the two thumbs of a range. @default ['Minimum', 'Maximum'] */ thumbLabels?: readonly [string, string]; /** Show the formatted value next to the label. @default true */ showOutput?: boolean; /** @default 'horizontal' */ orientation?: SliderOrientation; /** @default false */ disabled?: boolean; /** Submitted with a form; a range submits two inputs with this name. */ name?: string; className?: string; /** The group element. */ ref?: Ref; } /** * Picks a number, or a range, on a track: React Aria's slider (decision * record 001) provides the hidden range inputs, arrow, Page Up/Down, Home and * End keys, drag with a thumb that stays under the pointer, and * `aria-valuetext` in the formatted form. The output element shows the * same text, so what a screen reader hears is what sighted users see. * * @status experimental * @category Forms * @accessibility A `group` named by the label containing one hidden `input type="range"` per thumb (named by `thumbLabels` for a range) with `aria-valuetext` in the formatted form; the `output` element shows the same text. Arrows step, Page Up/Down step by ten, Home/End jump; thumbs drag with mouse or touch. * @do Set `formatOptions` so the spoken value matches the unit ("40%", "€200"). * Use `hideLabel` only when a visible heading already names it. * @dont Use it for precise values; provide a numeric field alongside. * Use a `step` so small the keyboard cannot reach the ends in reasonable time. */ export function Slider({ label, hideLabel = false, description, value, defaultValue, onChange, onChangeEnd, min = 0, max = 100, step = 1, formatOptions, thumbLabels = ['Minimum', 'Maximum'], showOutput = true, orientation = 'horizontal', disabled = false, name, className, ref, }: SliderProps) { const range = Array.isArray(value ?? defaultValue); return ( void) | undefined} onChangeEnd={onChangeEnd as ((value: number | number[]) => void) | undefined} minValue={min} maxValue={max} step={step} formatOptions={formatOptions} orientation={orientation} isDisabled={disabled} >
{label} {showOutput && }
{range && } {description && ( {description} )}
); } Slider.displayName = 'Slider';