# Slider

Picks a value or a range of values by dragging a thumb along a track.

```tsx
import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

function SliderDemo() {
  return (
    <div className="grid w-full max-w-xs gap-3">
      <Label id="risk-threshold-label">Risk score threshold</Label>
      <Slider defaultValue={[70]} aria-labelledby="risk-threshold-label" />
    </div>
  )
}
```

## Usage

```tsx
import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

<Label id="risk-threshold-label">Risk score threshold</Label>
<Slider defaultValue={[70]} aria-labelledby="risk-threshold-label" />
```

Use a slider when an approximate value is good enough and seeing it relative to the range
helps: thresholds, sampling rates, score ranges. When the user needs an exact value, use a
[Number Input](/components/number-input), or pair the slider with one.

Pass a number for one thumb (`defaultValue={70}`) or an array with one entry per thumb
(`[4, 8.9]` for a range). Without a value the slider has one thumb.

`min` and `max` default to `0` and `100`; `step` defaults to `1`. The slider fills the width of
its container.

## Examples

### With the value

The slider does not print its value. Keep it in state with `value` and `onValueChange` and
show it next to the label. `onValueChange` is typed `number | number[]`; with an array value
it always receives an array.

```tsx
import * as React from "react"
import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

function SliderWithValue() {
  const [sampling, setSampling] = React.useState([25])

  return (
    <div className="grid w-full max-w-xs gap-3">
      <div className="flex items-center justify-between">
        <Label id="sampling-label">Log sampling rate</Label>
        <span className="text-sm text-text-secondary tabular-nums">
          {sampling[0]}%
        </span>
      </div>
      <Slider
        value={sampling}
        onValueChange={(value) => setSampling(value as number[])}
        step={5}
        aria-labelledby="sampling-label"
      />
    </div>
  )
}
```

### Range

Two entries render two thumbs. `minStepsBetweenValues` keeps them a number of steps apart.

```tsx
import * as React from "react"
import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

function SliderRange() {
  const [range, setRange] = React.useState([4, 8.9])

  return (
    <div className="grid w-full max-w-xs gap-3">
      <div className="flex items-center justify-between">
        <Label id="cvss-label">CVSS score</Label>
        <span className="text-sm text-text-secondary tabular-nums">
          {range[0]?.toFixed(1)} to {range[1]?.toFixed(1)}
        </span>
      </div>
      <Slider
        value={range}
        onValueChange={(value) => setRange(value as number[])}
        min={0}
        max={10}
        step={0.1}
        minStepsBetweenValues={5}
        aria-labelledby="cvss-label"
      />
    </div>
  )
}
```

### Disabled

```tsx
import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

function SliderDisabled() {
  return (
    <div className="grid w-full max-w-xs gap-3">
      <Label id="retention-label">Retention (set by your plan)</Label>
      <Slider
        defaultValue={[30]}
        max={90}
        disabled
        aria-labelledby="retention-label"
      />
    </div>
  )
}
```

## Accessibility

- Each thumb is a native `<input type="range">`. Name the slider with `aria-labelledby`
  pointing at a visible label's `id`; the slider passes it to every thumb. To name thumbs
  individually ("Minimum", "Maximum"), pass `thumbLabels`. `aria-label` on `Slider` and `htmlFor`
  on a label do not reach the thumbs.
- A thumb shows the focus ring when its input has keyboard focus, the same 3px ring as the
  other form controls.
- Arrow keys move a thumb by `step`; Shift plus an arrow, Page Up and Page Down by
  `largeStep` (10 by default); Home and End to `min` and `max`.
- In a range, screen readers announce the thumbs as "start range" and "end range".
- Use `onValueCommitted` instead of `onValueChange` for expensive work, such as a request: it
  fires once when the user releases the thumb.

## API reference

`import { ... } from "@zenoxai/ui/components/slider"`

### Slider

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| defaultValue | `number \| readonly number[]` |  | The uncontrolled value of the slider when it's initially rendered.  To render a controlled slider, use the `value` prop instead. |
| disabled | `boolean` | `false` | Whether the slider should ignore user interaction. |
| form | `string` |  | Identifies the form that owns the slider inputs. Useful when the slider is rendered outside the form. |
| format | `NumberFormatOptions` |  | Options to format the value. |
| largeStep | `number` | `10` | The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down. |
| locale | `LocalesArgument` |  | The locale used by `Intl.NumberFormat` when formatting the value. Defaults to the user's runtime locale. |
| max | `number` | `100` | The maximum allowed value of the slider. Should not be equal to min. |
| min | `number` | `0` | The minimum allowed value of the slider. Should not be equal to max. |
| minStepsBetweenValues | `number` | `0` | The minimum steps between values in a range slider. |
| name | `string` |  | Identifies the field when a form is submitted. |
| onValueChange | `((value: number \| readonly number[], eventDetails: SliderRootChangeEventDetails) => void)` |  | Callback function that is fired when the slider's value changed. Receives the new value as the first argument; the originating event is available as `eventDetails.event`. The value is also reflected on `eventDetails.event.target.value` for form integration.  The `eventDetails.reason` indicates what triggered the change:  - `'input-change'` when the hidden range input emits a change event (for example, via form integration) - `'track-press'` when the control track is pressed - `'drag'` while dragging a thumb - `'keyboard'` for keyboard input - `'none'` when the change is triggered without a specific interaction |
| onValueCommitted | `((value: number \| readonly number[], eventDetails: SliderRootCommitEventDetails) => void)` |  | Callback function that is fired when a value change is committed. Does not fire if the value did not change, or if the change was canceled. **Warning**: This is a generic event, not a change event.  The `eventDetails.reason` indicates what triggered the commit:  - `'drag'` while dragging a thumb - `'track-press'` when the control track is pressed - `'keyboard'` for keyboard input - `'input-change'` when the hidden range input emits a change event (for example, via form integration) - `'none'` when the commit occurs without a specific interaction |
| orientation | `"horizontal" \| "vertical"` | `'horizontal'` | The component orientation. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, SliderRootState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| step | `number` | `1` | The granularity with which the slider can step through values. (A "discrete" slider.) The `min` prop serves as the origin for the valid values. We recommend (max - min) to be evenly divisible by the step. |
| thumbAlignment | `"center" \| "edge" \| "edge-client-only"` | `'center'` | How the thumb(s) are aligned relative to `Slider.Control` when the value is at `min` or `max`: - `center`: The center of the thumb is aligned with the control edge - `edge`: The thumb is inset within the control such that its edge is aligned with the control edge - `edge-client-only`: Same as `edge` but renders after React hydration on the client, reducing bundle size in return |
| thumbCollisionBehavior | `"none" \| "push" \| "swap"` | `'push'` | Controls how thumbs behave when they collide during pointer interactions.  - `'push'` (default): Thumbs push each other without restoring their previous positions when dragged back. - `'swap'`: Thumbs swap places when dragged past each other. - `'none'`: Thumbs cannot move past each other; excess movement is ignored. |
| thumbLabels | `string[]` |  | Already-translated accessible name of each thumb, in order. |
| value | `number \| readonly number[]` |  | The value of the slider. For range sliders, provide an array with one value per thumb. |

