# Range Slider

> Form pieces added for search filters and admin settings: RangeSlider (two thumbs), StatusSelect (a status pill that opens a list), TilePicker (pick one or many visual options) and ColorField (hex input with contrast check).

Component · Forms · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/RangeSlider/)

Form pieces added for search filters and admin settings: `RangeSlider` (two thumbs), `StatusSelect` (a status pill that opens a list), `TilePicker` (pick one or many visual options) and `ColorField` (hex input with contrast check).

RangeSlider: `label`, `min`, `max`, `step`, `value` / `defaultValue` ([low, high]) + `onChange`, `unit`, `inputs` (false shows values instead of fields), `openEnd` (adds "+" at max), `format`.
StatusSelect: `options` ({value, label, status}), `value` / `onChange`, `size` ("sm"), `label`.
TilePicker: `options` ({value, label, meta, image | icon | art, tint, color, disabled}), `value` / `onChange`, `multiple`, `min` (tile width), `label`.
ColorField: `label`, `value` / `onChange`, `against` (colour to check contrast with; false hides it), `hint`.

## Example

```tsx
import * as React from "react";
import { ColorField, RangeSlider, StatusSelect, TilePicker } from "@twentyfirstmedia/pro";

function App() {
  const r = React.useState<[number, number]>([300000, 650000]),
    t = React.useState<string | string[]>("scandi"),
    s = React.useState("read");
  return (
    <div
      className="tf-grid"
      style={
        {
          "--cols": "1fr 1fr",
          gap: 16,
          alignItems: "start",
        } as React.CSSProperties
      }
    >
      <div
        className="tf-panel"
        style={{
          gap: 20,
        }}
      >
        <RangeSlider
          label="Price"
          min={0}
          max={1500000}
          step={10000}
          value={r[0]}
          onChange={r[1]}
          unit="€"
        />
        <RangeSlider
          label="Living area"
          min={20}
          max={300}
          defaultValue={[60, 140]}
          inputs={false}
          openEnd
          format={(v) => v + " m²"}
        />
        <div className="tf-row">
          <StatusSelect
            value={s[0]}
            onChange={s[1]}
            label="Status"
            options={[
              {
                value: "unread",
                label: "Unread",
                status: "running",
              },
              {
                value: "read",
                label: "Read",
                status: "idle",
              },
              {
                value: "contacted",
                label: "Contacted",
                status: "ok",
              },
              {
                value: "rejected",
                label: "Not a fit",
                status: "danger",
              },
            ]}
          />
          <ColorField value="#2f6fe0" against="#ffffff" />
        </div>
      </div>
      <div
        className="tf-panel"
        style={{
          gap: 12,
        }}
      >
        <b>Room style</b>
        <TilePicker
          value={t[0]}
          onChange={(v) => t[1](v ?? "")}
          label="Room style"
          min={120}
          options={[
            {
              value: "scandi",
              label: "Scandinavian",
              tint: "sky",
              icon: "home",
            },
            {
              value: "modern",
              label: "Modern",
              tint: "mint",
              icon: "grid",
            },
            {
              value: "industrial",
              label: "Industrial",
              tint: "butter",
              icon: "layers",
            },
            {
              value: "classic",
              label: "Classic",
              tint: "blush",
              icon: "star",
              meta: "Pro",
            },
            {
              value: "japandi",
              label: "Japandi",
              tint: "lilac",
              icon: "sun",
            },
            {
              value: "boho",
              label: "Boho",
              disabled: true,
              icon: "sparkle",
              meta: "Soon",
            },
          ]}
        />
      </div>
    </div>
  );
}
export default App;
```

## Props

### ColorField

| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `ReactNode` |  |
| `value?` | `string` |  |
| `defaultValue?` | `string` |  |
| `onChange?` | `(v: string) => void` |  |
| `against?` | `string \| false` |  |
| `hint?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### RangeSlider

| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `ReactNode` |  |
| `min?` | `number` |  |
| `max?` | `number` |  |
| `step?` | `number` |  |
| `value?` | `[number, number]` |  |
| `defaultValue?` | `[number, number]` |  |
| `onChange?` | `(v: [number, number]) => void` |  |
| `unit?` | `string` |  |
| `inputs?` | `boolean` |  |
| `openEnd?` | `boolean` |  |
| `format?` | `(v: number) => string` |  |
| `hint?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### StatusSelect

| Prop | Type | Notes |
| --- | --- | --- |
| `options` | `{ value: string; label: string; status?: Status }[]` | Required |
| `value?` | `string` |  |
| `defaultValue?` | `string` |  |
| `onChange?` | `(v: string) => void` |  |
| `size?` | `"sm"` |  |
| `label?` | `string` |  |
| `width?` | `number` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### TilePicker

| Prop | Type | Notes |
| --- | --- | --- |
| `options` | `{ value: string; label: ReactNode; meta?: ReactNode; image?: string; icon?: IconName; art?: ReactNode; tint?: Tint; color?: string; disabled?: boolean; }[]` | Required |
| `value?` | `string \| string[]` |  |
| `defaultValue?` | `string \| string[]` |  |
| `onChange?` | `(v: string \| string[] \| null) => void` |  |
| `multiple?` | `boolean` |  |
| `min?` | `number` |  |
| `label?` | `string` |  |
| `layout?` | `"grid" \| "list"` | `list`: one compact row per option (small picture or icon beside the label), e.g. on phones. |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
