CI / ci (push) Successful in 1m31s
Co-authored-by: dev_agent <dev_agent@cubecraftlabs.com> Co-committed-by: dev_agent <dev_agent@cubecraftlabs.com>
31 lines
1.1 KiB
TypeScript
31 lines
1.1 KiB
TypeScript
import type { CSSProperties, ComponentPropsWithRef } from 'react'
|
|
import styles from './Slider.module.css'
|
|
|
|
export type SliderProps = Omit<ComponentPropsWithRef<'input'>, 'type' | 'value' | 'min' | 'max'> & {
|
|
value: number
|
|
min: number
|
|
max: number
|
|
}
|
|
|
|
// Native range input, styled: 8 px track, grape fill up to the value, 24 px
|
|
// knob with a 3 px grape ring. Controlled, because the fill follows `value`.
|
|
// Name it with aria-label or aria-labelledby.
|
|
export function Slider({ value, min, max, className, style, ...rest }: SliderProps) {
|
|
if (!(max > min)) throw new Error(`Slider: max (${max}) must be greater than min (${min})`)
|
|
if (!(value >= min && value <= max)) {
|
|
throw new Error(`Slider: value (${value}) must be within [${min}, ${max}]`)
|
|
}
|
|
const fill = `${((value - min) / (max - min)) * 100}%`
|
|
return (
|
|
<input
|
|
type="range"
|
|
value={value}
|
|
min={min}
|
|
max={max}
|
|
className={[styles.slider, className].filter(Boolean).join(' ')}
|
|
style={{ ...style, '--slider-fill': fill } as CSSProperties}
|
|
{...rest}
|
|
/>
|
|
)
|
|
}
|