Slider
An interactive input that allows user to select a value from a specific range.
import { Slider } from 'cwl-ui'
export function Default() {
return (
<form className="inline-flex w-full min-w-80 max-w-[400px] flex-col">
<Slider label="Label" description="(description)" helperText="Helper text" />
</form>
)
}
Examples
Orientation
Pass an orientation
prop to the Slider to change the orientation of the Slider.
import { Slider } from 'cwl-ui'
export function Orientation() {
return (
<form className="flex gap-8">
<Slider defaultValue={[20]} orientation="vertical" />
<Slider defaultValue={[60]} orientation="vertical" />
<Slider defaultValue={[80]} orientation="vertical" />
</form>
)
}
Disabled
Pass a disabled
prop to the Slider to disable the Slider.
import { Slider } from 'cwl-ui'
export function Disabled() {
return (
<form className="inline-flex w-full min-w-80 max-w-[400px] flex-col">
<Slider
defaultValue={[40]}
disabled
label="Label"
description="(description)"
helperText="Helper text"
/>
</form>
)
}
Range
Pass multiple values to the Slider to create a range Slider.
$2,500
$10,000
import * as React from 'react'
import { Slider } from 'cwl-ui'
export function Disabled() {
const [value, setValue] = React.useState([2500, 5000])
return (
<div className="inline-flex w-full min-w-80 max-w-[400px] flex-col">
<Slider
defaultValue={[2500, 5000]}
label="Price Range"
leadingAccessory="$2,500"
trailingAccessory="$10,000"
onValueChange={(val) => setValue(val)}
min={2500}
max={10000}
helperText={
'Selected range: $' +
value[0]?.toLocaleString('en-us') +
' - $' +
value[1]?.toLocaleString('en-us')
}
step={100}
/>
</div>
)
}
Installation
To install, copy the code below and paste into your project.
'use client'
import * as React from 'react'
import * as SliderPrimitive from '@radix-ui/react-slider'
import { Label, LabelHelperText } from './label'
import { AtomicProperties } from './lib/types'
import { cn } from './lib/utils'
import { Text } from './text'
export type SliderElement = React.ElementRef<typeof SliderPrimitive.Root>
export type SliderProps = Omit<
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,
'asChild'
> &
Pick<
AtomicProperties,
'description' | 'disabled' | 'label' | 'leadingAccessory' | 'trailingAccessory' | 'helperText'
>
const Slider = React.forwardRef<SliderElement, SliderProps>(
(
{
children,
disabled,
leadingAccessory,
trailingAccessory,
defaultValue,
label,
id,
orientation,
helperText,
description,
onValueChange,
...props
},
ref,
) => {
const [thumbs, setThumbs] = React.useState<number[]>(defaultValue ?? [0])
const generateId = React.useId()
const elId = id ?? generateId
return (
<div className={cn('flex flex-col gap-2', disabled && 'text-surface-300')}>
<Label id={`${elId}__label`} htmlFor={elId} description={description} disabled={disabled}>
{label}
</Label>
<div
className={cn(
'flex justify-center items-baseline gap-4 self-stretch',
orientation === 'vertical' && 'flex-col items-start',
orientation === 'horizontal' && 'items-center',
)}
>
{leadingAccessory ? (
<Text color="secondary" size="xsmall">
{leadingAccessory}
</Text>
) : null}
<SliderRoot
ref={ref}
id={elId}
disabled={disabled}
defaultValue={defaultValue}
aria-describedby={`${elId}__describe`}
orientation={orientation}
onValueChange={(value) => {
setThumbs(value)
onValueChange?.(value)
}}
{...props}
>
<SliderTrack>
<SliderRange aria-disabled={disabled} />
</SliderTrack>
{thumbs?.map((_, index) => (
<SliderThumb key={index} id={elId} aria-disabled={disabled} tooltip={false} />
))}
{children}
</SliderRoot>
{trailingAccessory ? (
<Text color="secondary" size="xsmall">
{trailingAccessory}
</Text>
) : null}
</div>
<LabelHelperText disabled={disabled} id={`${elId}__describe`}>
{helperText}
</LabelHelperText>
</div>
)
},
)
export type SliderRootElement = React.ElementRef<typeof SliderPrimitive.Root>
export type SliderRootProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
const SliderRoot = React.forwardRef<SliderRootElement, SliderRootProps>(
({ className, ...props }, ref) => {
return (
<SliderPrimitive.Root
ref={ref}
className={cn(
'relative flex w-full touch-none select-none items-center',
'data-[orientation=horizontal]:min-h-6',
'data-[orientation=vertical]:w-2 data-[orientation=vertical]:flex-col data-[orientation=vertical]:justify-center data-[orientation=vertical]:px-3',
className,
)}
{...props}
/>
)
},
)
export { Slider }
export type SliderTrackElement = React.ElementRef<typeof SliderPrimitive.Track>
export type SliderTrackProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Track>
const SliderTrack = React.forwardRef<SliderTrackElement, SliderTrackProps>(
({ className, ...props }, ref) => {
return (
<SliderPrimitive.Track
ref={ref}
cwl-ui-component="SliderTrack"
className={cn(
'relative h-2 w-full grow overflow-hidden rounded-full bg-surface-100',
'data-[orientation=vertical]:h-72 data-[orientation=vertical]:w-2',
className,
)}
{...props}
/>
)
},
)
export type SliderRangeElement = React.ElementRef<typeof SliderPrimitive.Range>
export type SliderRangeProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Range>
const SliderRange = React.forwardRef<SliderRangeElement, SliderRangeProps>(
({ className, ...props }, ref) => {
const disabled = props['aria-disabled'] ?? false
return (
<SliderPrimitive.Range
ref={ref}
cwl-ui-component="SliderRange"
className={cn(
'absolute rounded-full bg-primary',
'data-[orientation=vertical]:left-0 data-[orientation=vertical]:w-full',
'data-[orientation=horizontal]:l-0 data-[orientation=horizontal]:h-full',
disabled && 'bg-surface-200',
className,
)}
{...props}
/>
)
},
)
export type SliderThumbElement = React.ElementRef<typeof SliderPrimitive.Thumb>
export type SliderThumbProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Thumb> & {
/**
* Whether or not to show a tooltip.
*/
tooltip?: boolean
/**
* Which direction to render the thumb
*/
orientation?: 'horizontal' | 'vertical'
}
const SliderThumb = React.forwardRef<SliderThumbElement, SliderThumbProps>(
({ className, tooltip, ...props }, ref) => {
const disabled = props['aria-disabled'] ?? false
return (
<SliderPrimitive.Thumb
ref={ref}
cwl-ui-component="SliderThumb"
className={cn(
'block size-5 cursor-grab border border-surface-200 rounded-full bg-white shadow-sm hidden:disabled',
'focus:outline-none focus-visible:border-primary',
'data-[state=active]:cursor-grabbing data-[state=active]:border-primary',
disabled && 'hidden',
className,
)}
{...props}
>
{tooltip ? <span>Tooltip</span> : null}
</SliderPrimitive.Thumb>
)
},
)