# Slider

Import: `import { Slider } from '@neo4j-ndl/react'`

## Props

### Slider

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `as` | `ElementType<any, keyof IntrinsicElements>` |  |  | An override of the default HTML tag of the root of the component. Can also be another React component. |
| `inputProps` | `HtmlAttributes<"input">` |  | `{}` | Input props of the underlying input element |
| `isDisabled` | `boolean` |  | `false` | Whether the slider is disabled |
| `maxValue` | `number` |  | `100` | The maximum value possible to choose |
| `minValue` | `number` |  | `0` | The minimum value possible to choose |
| `onChange` | `((value: number) => void) \| ((value: [number, number]) => void)` | ✅ |  | Callback function triggered when the slider value(s) changes. The function parameter varies depending on the slider type, for single slider, it is a number and for range slider, it is an array of two numbers. |
| `ref` | `any` |  |  | A ref to apply to the root element. |
| `showSteps` | `boolean` |  | `false` | Whether to display visual indicators for each step in the slider |
| `showValues` | `boolean` |  | `false` | Whether to display a tooltip with the current value when the slider is being dragged |
| `step` | `number` |  | `1` | Determines the step size that will be added or removed from the current value when moving the slider button |
| `type` | `'range' \| 'single'` |  | `single` | Type of the slider |
| `value` | `number` | ✅ |  | Current value of the single slider. **Only required when the type is 'single'**. |
| `values` | `[number, number]` | ✅ |  | Current values of the range slider as an array of two numbers `[min, max]`. **Only required when the type is 'range'**. |

The `type` prop is used to determine the type of the slider. It can be `single` or `range`. The `value` prop is required when the `type` is `single` and `values` is required when the `type` is `range`. It also affects the `onChange` prop type.

## Accessibility

## Implementation guidelines

The Slider component is built with full accessibility support using React Aria. It includes proper ARIA attributes, keyboard navigation support, and focus management. Users can interact with the slider using:

 - **Mouse/Touch**: Click and drag the thumb(s) to adjust values
- **Keyboard**: Use arrow keys to increment/decrement values, Home/End keys to jump to min/max values
- **Screen Readers**: Proper ARIA labels and value announcements

The component supports explicit and custom `aria-valuetext` through the `inputProps` for enhanced screen reader experience.

## Examples

### Default

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      minValue={0}
      maxValue={100}
      value={50}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
    />
  );
};

export default Component;
```

### Custom Step Length

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      minValue={0}
      maxValue={100}
      step={10}
      value={50}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      showSteps
      showValues
    />
  );
};

export default Component;
```

### Disabled

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      minValue={0}
      maxValue={100}
      step={10}
      value={50}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      isDisabled
    />
  );
};

export default Component;
```

### Range

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      type="range"
      values={[10, 50]}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      minValue={0}
      maxValue={200}
      step={10}
      isDisabled={false}
      showSteps={true}
      showValues={true}
    />
  );
};

export default Component;
```

### Show Steps

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      minValue={0}
      maxValue={100}
      value={50}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      showSteps
    />
  );
};

export default Component;
```

### Show Values

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      minValue={0}
      maxValue={100}
      value={50}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      showValues
    />
  );
};

export default Component;
```

### Single

```tsx
import { Slider } from '@neo4j-ndl/react';

const Component = () => {
  return (
    <Slider
      type="single"
      value={100}
      onChange={(newValue) => {
        console.info('onChange', newValue);
      }}
      minValue={0}
      maxValue={200}
      isDisabled={false}
      step={10}
      showSteps={true}
      showValues={true}
      inputProps={{ 'aria-valuetext': 'percent' }}
    />
  );
};

export default Component;
```

### Single Controlled

```tsx
import { Slider, TextInput } from '@neo4j-ndl/react';
import { useCallback, useState } from 'react';

const MIN_VALUE = 0;
const MAX_VALUE = 100;
const STEP = 5;
const INITIAL_VALUE = 50;

const getValidationError = (
  input: string,
): { error: string; isValid: false } | { error: undefined; isValid: true } => {
  const trimmed = input.trim();

  if (!trimmed) {
    return { error: 'Value is required', isValid: false };
  }

  const numValue = Number(trimmed);
  if (isNaN(numValue)) {
    return { error: 'Please enter a valid number', isValid: false };
  }

  if (!Number.isInteger(numValue)) {
    return { error: 'Please enter a whole number', isValid: false };
  }

  if (numValue < MIN_VALUE) {
    return { error: `Value must be at least ${MIN_VALUE}`, isValid: false };
  }

  if (numValue > MAX_VALUE) {
    return { error: `Value must be at most ${MAX_VALUE}`, isValid: false };
  }

  return { error: undefined, isValid: true };
};

const Component = () => {
  const [value, setValue] = useState(INITIAL_VALUE);
  const [inputValue, setInputValue] = useState(String(INITIAL_VALUE));
  const { error, isValid } = getValidationError(inputValue);
  const hasError = !isValid;

  const handleInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const newInputValue = e.target.value;
      setInputValue(newInputValue);

      if (isValid) {
        setValue(Number(newInputValue));
      }
    },
    [setInputValue, setValue, isValid],
  );

  const handleSliderChange = useCallback(
    (newValue: number) => {
      setValue(newValue);
      setInputValue(String(newValue));
    },
    [setInputValue, setValue],
  );

  return (
    <div className="n-flex n-flex-col n-gap-token-16">
      <TextInput
        label="Slider value"
        helpText={`Enter a value between ${MIN_VALUE} and ${MAX_VALUE}`}
        isRequired
        errorText={error}
        htmlAttributes={{
          max: MAX_VALUE,
          min: MIN_VALUE,
          step: STEP,
          type: 'number',
        }}
        value={inputValue}
        onChange={handleInputChange}
      />

      <Slider
        type="single"
        value={value}
        onChange={handleSliderChange}
        minValue={MIN_VALUE}
        maxValue={MAX_VALUE}
        step={STEP}
        showSteps
        showValues
        isDisabled={hasError}
        inputProps={{
          'aria-valuetext': `${value} out of ${MAX_VALUE}`,
        }}
      />
    </div>
  );
};

export default Component;
```
