import type { SliderProps as AriaSliderProps } from "react-aria-components"; import { Label as AriaLabel, Slider as AriaSlider, SliderOutput as AriaSliderOutput, SliderThumb as AriaSliderThumb, SliderTrack as AriaSliderTrack, } from "react-aria-components"; import { cx, sortCx } from "@/utils/cx"; const styles = sortCx({ default: "hidden", bottom: "absolute top-2 left-1/2 -translate-x-1/2 translate-y-full text-md font-medium text-primary", "top-floating": "absolute -top-2 left-1/2 -translate-x-1/2 -translate-y-full rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-secondary shadow-lg ring-1 ring-secondary_alt", }); interface SliderProps extends AriaSliderProps { labelPosition?: keyof typeof styles; labelFormatter?: (value: number) => string; } export const Slider = ({ labelPosition = "default", minValue = 0, maxValue = 100, labelFormatter, formatOptions, ...rest }: SliderProps) => { // Format thumb value as percentage by default. const defaultFormatOptions: Intl.NumberFormatOptions = { style: "percent", maximumFractionDigits: 0, }; return ( {({ state: { values, getThumbValue, getThumbPercent, getFormattedValue } }) => { const left = values.length === 1 ? 0 : getThumbPercent(0); const width = values.length === 1 ? getThumbPercent(0) : getThumbPercent(1) - left; return ( <> {values.map((_, index) => { return ( cx( "top-1/2 box-border size-6 cursor-grab rounded-full bg-slider-handle-bg shadow-md ring-2 ring-slider-handle-border ring-inset", isFocusVisible && "outline-2 outline-offset-2 outline-focus-ring", isDragging && "cursor-grabbing", ) } > {labelFormatter ? labelFormatter(getThumbValue(index)) : getFormattedValue(getThumbValue(index) / 100)} ); })} ); }} ); };