@adamosuiteservices/ui 2.11.15 → 2.11.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/colors.css +1 -1
- package/dist/styles.css +1 -1
- package/docs/AI-GUIDE.md +321 -321
- package/docs/components/layout/sidebar.md +399 -399
- package/docs/components/layout/toaster.md +436 -436
- package/docs/components/ui/accordion-rounded.md +584 -584
- package/docs/components/ui/accordion.md +269 -269
- package/docs/components/ui/button-group.md +984 -984
- package/docs/components/ui/button.md +1137 -1137
- package/docs/components/ui/calendar.md +1159 -1159
- package/docs/components/ui/card.md +1455 -1455
- package/docs/components/ui/checkbox.md +292 -292
- package/docs/components/ui/collapsible.md +323 -323
- package/docs/components/ui/command.md +454 -454
- package/docs/components/ui/context-menu.md +540 -540
- package/docs/components/ui/dialog.md +628 -628
- package/docs/components/ui/dropdown-menu.md +709 -709
- package/docs/components/ui/field.md +706 -706
- package/docs/components/ui/hover-card.md +446 -446
- package/docs/components/ui/input.md +362 -362
- package/docs/components/ui/kbd.md +434 -434
- package/docs/components/ui/label.md +359 -359
- package/docs/components/ui/pagination.md +650 -650
- package/docs/components/ui/popover.md +536 -536
- package/docs/components/ui/progress.md +182 -182
- package/docs/components/ui/radio-group.md +311 -311
- package/docs/components/ui/select.md +352 -352
- package/docs/components/ui/separator.md +214 -214
- package/docs/components/ui/sheet.md +142 -142
- package/docs/components/ui/skeleton.md +140 -140
- package/docs/components/ui/slider.md +341 -341
- package/docs/components/ui/spinner.md +170 -170
- package/docs/components/ui/switch.md +408 -408
- package/docs/components/ui/tabs-underline.md +106 -106
- package/docs/components/ui/tabs.md +122 -122
- package/docs/components/ui/textarea.md +243 -243
- package/docs/components/ui/toggle.md +237 -237
- package/docs/components/ui/tooltip.md +317 -317
- package/docs/components/ui/typography.md +280 -280
- package/package.json +1 -1
|
@@ -1,341 +1,341 @@
|
|
|
1
|
-
# Slider
|
|
2
|
-
|
|
3
|
-
Control de rango con thumbs deslizables. Soporta valores simples y rangos. Basado en Radix UI.
|
|
4
|
-
|
|
5
|
-
## Descripción
|
|
6
|
-
|
|
7
|
-
El componente `Slider` permite seleccionar un valor arrastrando un control deslizante.
|
|
8
|
-
|
|
9
|
-
## Importación
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { Slider } from "@adamosuiteservices/ui/slider";
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Anatomía
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
<Slider defaultValue={[50]} max={100} step={1} />
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Componentes**: 1 (Slider) con 3 partes internas (Root, Track, Range, Thumb)
|
|
22
|
-
|
|
23
|
-
## Props
|
|
24
|
-
|
|
25
|
-
| Prop | Tipo | Descripción |
|
|
26
|
-
| --------------- | ---------------------------- | ----------------------------------- |
|
|
27
|
-
| `defaultValue` | `number[]` | Valor inicial (uncontrolled) |
|
|
28
|
-
| `value` | `number[]` | Valor controlado (array de números) |
|
|
29
|
-
| `onValueChange` | `(value: number[]) => void` | Callback al cambiar valor |
|
|
30
|
-
| `min` | `number` | Valor mínimo (default: 0) |
|
|
31
|
-
| `max` | `number` | Valor máximo (default: 100) |
|
|
32
|
-
| `step` | `number` | Incremento (default: 1) |
|
|
33
|
-
| `disabled` | `boolean` | Deshabilita el slider |
|
|
34
|
-
| `orientation` | `"horizontal" \| "vertical"` | Orientación (default: horizontal) |
|
|
35
|
-
| `theme` | `Theme` | Tema personalizado |
|
|
36
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
37
|
-
|
|
38
|
-
**Nota**: `value` y `defaultValue` siempre son arrays. Single value = `[50]`, range = `[20, 80]`
|
|
39
|
-
|
|
40
|
-
## Patrones de Uso
|
|
41
|
-
|
|
42
|
-
### Básico
|
|
43
|
-
|
|
44
|
-
```tsx
|
|
45
|
-
import { Slider } from "@adamosuiteservices/ui/slider";
|
|
46
|
-
|
|
47
|
-
<Slider defaultValue={[33]} max={100} step={1} className="w-[60%]" />;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Con Label
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import { Label } from "@adamosuiteservices/ui/label";
|
|
54
|
-
import { useState } from "react";
|
|
55
|
-
|
|
56
|
-
function App() {
|
|
57
|
-
const [value, setValue] = useState([50]);
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<div className="grid w-full max-w-sm items-center gap-2">
|
|
61
|
-
<Label htmlFor="volume">Volume: {value[0]}%</Label>
|
|
62
|
-
<Slider
|
|
63
|
-
id="volume"
|
|
64
|
-
max={100}
|
|
65
|
-
step={1}
|
|
66
|
-
value={value}
|
|
67
|
-
onValueChange={setValue}
|
|
68
|
-
className="w-full"
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Range (Dos Valores)
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
function App() {
|
|
79
|
-
const [value, setValue] = useState([20, 80]);
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<div className="grid w-full max-w-sm items-center gap-2">
|
|
83
|
-
<Label>
|
|
84
|
-
Price Range: ${value[0]} - ${value[1]}
|
|
85
|
-
</Label>
|
|
86
|
-
<Slider
|
|
87
|
-
max={100}
|
|
88
|
-
step={1}
|
|
89
|
-
value={value}
|
|
90
|
-
onValueChange={setValue}
|
|
91
|
-
className="w-full"
|
|
92
|
-
/>
|
|
93
|
-
</div>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Con Steps
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
function App() {
|
|
102
|
-
const [value, setValue] = useState([25]);
|
|
103
|
-
const stepSize = 25;
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<div className="grid w-full max-w-sm items-center gap-2">
|
|
107
|
-
<Label>Progress: {value[0]}%</Label>
|
|
108
|
-
<Slider
|
|
109
|
-
max={100}
|
|
110
|
-
step={stepSize}
|
|
111
|
-
value={value}
|
|
112
|
-
onValueChange={setValue}
|
|
113
|
-
className="w-full"
|
|
114
|
-
/>
|
|
115
|
-
<div className="flex justify-between text-xs text-muted-foreground">
|
|
116
|
-
<span>0%</span>
|
|
117
|
-
<span>25%</span>
|
|
118
|
-
<span>50%</span>
|
|
119
|
-
<span>75%</span>
|
|
120
|
-
<span>100%</span>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Vertical
|
|
128
|
-
|
|
129
|
-
```tsx
|
|
130
|
-
function App() {
|
|
131
|
-
const [value, setValue] = useState([60]);
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<div className="flex h-64 items-center space-x-6">
|
|
135
|
-
<div className="flex flex-col items-center space-y-2">
|
|
136
|
-
<Label>Volume</Label>
|
|
137
|
-
<Slider
|
|
138
|
-
orientation="vertical"
|
|
139
|
-
max={100}
|
|
140
|
-
step={1}
|
|
141
|
-
value={value}
|
|
142
|
-
onValueChange={setValue}
|
|
143
|
-
className="h-48"
|
|
144
|
-
/>
|
|
145
|
-
<span className="text-sm text-muted-foreground">{value[0]}%</span>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Con Botones
|
|
153
|
-
|
|
154
|
-
```tsx
|
|
155
|
-
import { Button } from "@adamosuiteservices/ui/button";
|
|
156
|
-
|
|
157
|
-
function App() {
|
|
158
|
-
const [value, setValue] = useState([50]);
|
|
159
|
-
|
|
160
|
-
const decrease = () => setValue([Math.max(0, value[0] - 10)]);
|
|
161
|
-
const increase = () => setValue([Math.min(100, value[0] + 10)]);
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<div className="grid w-full max-w-sm items-center gap-4">
|
|
165
|
-
<Label>Volume: {value[0]}%</Label>
|
|
166
|
-
<div className="flex items-center space-x-2">
|
|
167
|
-
<Button
|
|
168
|
-
variant="outline"
|
|
169
|
-
size="sm"
|
|
170
|
-
onClick={decrease}
|
|
171
|
-
disabled={value[0] === 0}
|
|
172
|
-
>
|
|
173
|
-
-
|
|
174
|
-
</Button>
|
|
175
|
-
<Slider
|
|
176
|
-
value={value}
|
|
177
|
-
onValueChange={setValue}
|
|
178
|
-
max={100}
|
|
179
|
-
step={1}
|
|
180
|
-
className="flex-1"
|
|
181
|
-
/>
|
|
182
|
-
<Button
|
|
183
|
-
variant="outline"
|
|
184
|
-
size="sm"
|
|
185
|
-
onClick={increase}
|
|
186
|
-
disabled={value[0] === 100}
|
|
187
|
-
>
|
|
188
|
-
+
|
|
189
|
-
</Button>
|
|
190
|
-
</div>
|
|
191
|
-
</div>
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Price Filter
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
import {
|
|
200
|
-
Card,
|
|
201
|
-
CardContent,
|
|
202
|
-
CardHeader,
|
|
203
|
-
CardTitle,
|
|
204
|
-
} from "@adamosuiteservices/ui/card";
|
|
205
|
-
import { Button } from "@adamosuiteservices/ui/button";
|
|
206
|
-
|
|
207
|
-
function PriceFilter() {
|
|
208
|
-
const [priceRange, setPriceRange] = useState([25, 75]);
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<Card className="w-full max-w-sm">
|
|
212
|
-
<CardHeader>
|
|
213
|
-
<CardTitle>Filter by Price</CardTitle>
|
|
214
|
-
</CardHeader>
|
|
215
|
-
<CardContent className="space-y-4">
|
|
216
|
-
<div className="space-y-2">
|
|
217
|
-
<Label>Price Range</Label>
|
|
218
|
-
<Slider
|
|
219
|
-
value={priceRange}
|
|
220
|
-
onValueChange={setPriceRange}
|
|
221
|
-
max={100}
|
|
222
|
-
step={1}
|
|
223
|
-
className="w-full"
|
|
224
|
-
/>
|
|
225
|
-
</div>
|
|
226
|
-
<div className="flex justify-between text-sm">
|
|
227
|
-
<span>${priceRange[0]}</span>
|
|
228
|
-
<span>${priceRange[1]}</span>
|
|
229
|
-
</div>
|
|
230
|
-
<Button className="w-full">Apply Filter</Button>
|
|
231
|
-
</CardContent>
|
|
232
|
-
</Card>
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### RGB Color Picker
|
|
238
|
-
|
|
239
|
-
```tsx
|
|
240
|
-
function ColorPicker() {
|
|
241
|
-
const [red, setRed] = useState([128]);
|
|
242
|
-
const [green, setGreen] = useState([200]);
|
|
243
|
-
const [blue, setBlue] = useState([75]);
|
|
244
|
-
|
|
245
|
-
const rgbColor = `rgb(${red[0]}, ${green[0]}, ${blue[0]})`;
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<Card className="w-full max-w-sm">
|
|
249
|
-
<CardHeader>
|
|
250
|
-
<CardTitle>RGB Color Picker</CardTitle>
|
|
251
|
-
</CardHeader>
|
|
252
|
-
<CardContent className="space-y-6">
|
|
253
|
-
<div
|
|
254
|
-
className="w-full h-20 rounded-lg border"
|
|
255
|
-
style={{ backgroundColor: rgbColor }}
|
|
256
|
-
/>
|
|
257
|
-
|
|
258
|
-
<div className="space-y-4">
|
|
259
|
-
<div className="space-y-2">
|
|
260
|
-
<div className="flex justify-between">
|
|
261
|
-
<Label>Red</Label>
|
|
262
|
-
<span className="text-sm text-muted-foreground">{red[0]}</span>
|
|
263
|
-
</div>
|
|
264
|
-
<Slider value={red} onValueChange={setRed} max={255} step={1} />
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
<div className="space-y-2">
|
|
268
|
-
<div className="flex justify-between">
|
|
269
|
-
<Label>Green</Label>
|
|
270
|
-
<span className="text-sm text-muted-foreground">{green[0]}</span>
|
|
271
|
-
</div>
|
|
272
|
-
<Slider value={green} onValueChange={setGreen} max={255} step={1} />
|
|
273
|
-
</div>
|
|
274
|
-
|
|
275
|
-
<div className="space-y-2">
|
|
276
|
-
<div className="flex justify-between">
|
|
277
|
-
<Label>Blue</Label>
|
|
278
|
-
<span className="text-sm text-muted-foreground">{blue[0]}</span>
|
|
279
|
-
</div>
|
|
280
|
-
<Slider value={blue} onValueChange={setBlue} max={255} step={1} />
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
283
|
-
</CardContent>
|
|
284
|
-
</Card>
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### Deshabilitado
|
|
290
|
-
|
|
291
|
-
```tsx
|
|
292
|
-
<Slider defaultValue={[50]} max={100} step={1} disabled className="w-full" />
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
## Casos de Uso
|
|
296
|
-
|
|
297
|
-
**Volume control**: Audio, música, efectos de sonido
|
|
298
|
-
**Price filters**: Rango de precios en e-commerce
|
|
299
|
-
**Color pickers**: RGB, HSL sliders
|
|
300
|
-
**Brightness/Contrast**: Ajustes de imagen
|
|
301
|
-
**Temperature**: Termostatos, controles de clima
|
|
302
|
-
**Range selection**: Filtros de fecha, edad, tamaño
|
|
303
|
-
|
|
304
|
-
## Estilos Base
|
|
305
|
-
|
|
306
|
-
- **Track height**: `h-1.5` (horizontal), `w-1.5` (vertical)
|
|
307
|
-
- **Track bg**: `bg-muted`
|
|
308
|
-
- **Range bg**: `bg-primary`
|
|
309
|
-
- **Thumb**: `size-4` con `border-primary`, `bg-white`, `shadow-sm`
|
|
310
|
-
- **Thumb hover/focus**: `ring-4` con `ring-ring/50`
|
|
311
|
-
- **Vertical**: `min-h-44` default, `flex-col`
|
|
312
|
-
|
|
313
|
-
## Accesibilidad
|
|
314
|
-
|
|
315
|
-
- ✅ **Role**: `role="slider"` con `aria-valuenow`, `aria-valuemin`, `aria-valuemax`
|
|
316
|
-
- ✅ **Keyboard**: Arrow keys para ajustar valor, Home/End para min/max
|
|
317
|
-
- ✅ **Focus**: Focus ring visible en thumbs
|
|
318
|
-
- ✅ **Label**: Asociar con `id` para screen readers
|
|
319
|
-
|
|
320
|
-
## Notas de Implementación
|
|
321
|
-
|
|
322
|
-
- **Radix UI**: Basado en `@radix-ui/react-slider`
|
|
323
|
-
- **Multiple thumbs**: Array length determina número de thumbs
|
|
324
|
-
- **Value format**: Siempre array, ej: `[50]` para single, `[20, 80]` para range
|
|
325
|
-
- **Auto-thumbs**: Genera thumbs automáticamente basado en `value.length`
|
|
326
|
-
- **Data attributes**: `data-orientation` para estilos horizontal/vertical
|
|
327
|
-
- **Theme support**: Prop `theme` para personalización
|
|
328
|
-
|
|
329
|
-
## Troubleshooting
|
|
330
|
-
|
|
331
|
-
**Thumb no se mueve**: Verifica `max`, `min` y `step` son números válidos
|
|
332
|
-
**Value no actualiza**: En modo controlado usa `value` + `onValueChange`, no `defaultValue`
|
|
333
|
-
**Range invertido**: Asegura `value[0] < value[1]` en ranges
|
|
334
|
-
**Vertical no funciona**: Agrega `className="h-[Xpx]"` para altura explícita
|
|
335
|
-
**Step no funciona**: `step` debe ser divisor válido del rango (max - min)
|
|
336
|
-
**Multiple thumbs**: `value={[10, 50, 90]}` crea 3 thumbs
|
|
337
|
-
|
|
338
|
-
## Referencias
|
|
339
|
-
|
|
340
|
-
- **Radix UI Slider**: <https://www.radix-ui.com/primitives/docs/components/slider>
|
|
341
|
-
- **shadcn/ui Slider**: <https://ui.shadcn.com/docs/components/slider>
|
|
1
|
+
# Slider
|
|
2
|
+
|
|
3
|
+
Control de rango con thumbs deslizables. Soporta valores simples y rangos. Basado en Radix UI.
|
|
4
|
+
|
|
5
|
+
## Descripción
|
|
6
|
+
|
|
7
|
+
El componente `Slider` permite seleccionar un valor arrastrando un control deslizante.
|
|
8
|
+
|
|
9
|
+
## Importación
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Slider } from "@adamosuiteservices/ui/slider";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Anatomía
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<Slider defaultValue={[50]} max={100} step={1} />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Componentes**: 1 (Slider) con 3 partes internas (Root, Track, Range, Thumb)
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Prop | Tipo | Descripción |
|
|
26
|
+
| --------------- | ---------------------------- | ----------------------------------- |
|
|
27
|
+
| `defaultValue` | `number[]` | Valor inicial (uncontrolled) |
|
|
28
|
+
| `value` | `number[]` | Valor controlado (array de números) |
|
|
29
|
+
| `onValueChange` | `(value: number[]) => void` | Callback al cambiar valor |
|
|
30
|
+
| `min` | `number` | Valor mínimo (default: 0) |
|
|
31
|
+
| `max` | `number` | Valor máximo (default: 100) |
|
|
32
|
+
| `step` | `number` | Incremento (default: 1) |
|
|
33
|
+
| `disabled` | `boolean` | Deshabilita el slider |
|
|
34
|
+
| `orientation` | `"horizontal" \| "vertical"` | Orientación (default: horizontal) |
|
|
35
|
+
| `theme` | `Theme` | Tema personalizado |
|
|
36
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
37
|
+
|
|
38
|
+
**Nota**: `value` y `defaultValue` siempre son arrays. Single value = `[50]`, range = `[20, 80]`
|
|
39
|
+
|
|
40
|
+
## Patrones de Uso
|
|
41
|
+
|
|
42
|
+
### Básico
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Slider } from "@adamosuiteservices/ui/slider";
|
|
46
|
+
|
|
47
|
+
<Slider defaultValue={[33]} max={100} step={1} className="w-[60%]" />;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Con Label
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { Label } from "@adamosuiteservices/ui/label";
|
|
54
|
+
import { useState } from "react";
|
|
55
|
+
|
|
56
|
+
function App() {
|
|
57
|
+
const [value, setValue] = useState([50]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="grid w-full max-w-sm items-center gap-2">
|
|
61
|
+
<Label htmlFor="volume">Volume: {value[0]}%</Label>
|
|
62
|
+
<Slider
|
|
63
|
+
id="volume"
|
|
64
|
+
max={100}
|
|
65
|
+
step={1}
|
|
66
|
+
value={value}
|
|
67
|
+
onValueChange={setValue}
|
|
68
|
+
className="w-full"
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Range (Dos Valores)
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
function App() {
|
|
79
|
+
const [value, setValue] = useState([20, 80]);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="grid w-full max-w-sm items-center gap-2">
|
|
83
|
+
<Label>
|
|
84
|
+
Price Range: ${value[0]} - ${value[1]}
|
|
85
|
+
</Label>
|
|
86
|
+
<Slider
|
|
87
|
+
max={100}
|
|
88
|
+
step={1}
|
|
89
|
+
value={value}
|
|
90
|
+
onValueChange={setValue}
|
|
91
|
+
className="w-full"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Con Steps
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
function App() {
|
|
102
|
+
const [value, setValue] = useState([25]);
|
|
103
|
+
const stepSize = 25;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className="grid w-full max-w-sm items-center gap-2">
|
|
107
|
+
<Label>Progress: {value[0]}%</Label>
|
|
108
|
+
<Slider
|
|
109
|
+
max={100}
|
|
110
|
+
step={stepSize}
|
|
111
|
+
value={value}
|
|
112
|
+
onValueChange={setValue}
|
|
113
|
+
className="w-full"
|
|
114
|
+
/>
|
|
115
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
116
|
+
<span>0%</span>
|
|
117
|
+
<span>25%</span>
|
|
118
|
+
<span>50%</span>
|
|
119
|
+
<span>75%</span>
|
|
120
|
+
<span>100%</span>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Vertical
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
function App() {
|
|
131
|
+
const [value, setValue] = useState([60]);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="flex h-64 items-center space-x-6">
|
|
135
|
+
<div className="flex flex-col items-center space-y-2">
|
|
136
|
+
<Label>Volume</Label>
|
|
137
|
+
<Slider
|
|
138
|
+
orientation="vertical"
|
|
139
|
+
max={100}
|
|
140
|
+
step={1}
|
|
141
|
+
value={value}
|
|
142
|
+
onValueChange={setValue}
|
|
143
|
+
className="h-48"
|
|
144
|
+
/>
|
|
145
|
+
<span className="text-sm text-muted-foreground">{value[0]}%</span>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Con Botones
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { Button } from "@adamosuiteservices/ui/button";
|
|
156
|
+
|
|
157
|
+
function App() {
|
|
158
|
+
const [value, setValue] = useState([50]);
|
|
159
|
+
|
|
160
|
+
const decrease = () => setValue([Math.max(0, value[0] - 10)]);
|
|
161
|
+
const increase = () => setValue([Math.min(100, value[0] + 10)]);
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div className="grid w-full max-w-sm items-center gap-4">
|
|
165
|
+
<Label>Volume: {value[0]}%</Label>
|
|
166
|
+
<div className="flex items-center space-x-2">
|
|
167
|
+
<Button
|
|
168
|
+
variant="outline"
|
|
169
|
+
size="sm"
|
|
170
|
+
onClick={decrease}
|
|
171
|
+
disabled={value[0] === 0}
|
|
172
|
+
>
|
|
173
|
+
-
|
|
174
|
+
</Button>
|
|
175
|
+
<Slider
|
|
176
|
+
value={value}
|
|
177
|
+
onValueChange={setValue}
|
|
178
|
+
max={100}
|
|
179
|
+
step={1}
|
|
180
|
+
className="flex-1"
|
|
181
|
+
/>
|
|
182
|
+
<Button
|
|
183
|
+
variant="outline"
|
|
184
|
+
size="sm"
|
|
185
|
+
onClick={increase}
|
|
186
|
+
disabled={value[0] === 100}
|
|
187
|
+
>
|
|
188
|
+
+
|
|
189
|
+
</Button>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Price Filter
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import {
|
|
200
|
+
Card,
|
|
201
|
+
CardContent,
|
|
202
|
+
CardHeader,
|
|
203
|
+
CardTitle,
|
|
204
|
+
} from "@adamosuiteservices/ui/card";
|
|
205
|
+
import { Button } from "@adamosuiteservices/ui/button";
|
|
206
|
+
|
|
207
|
+
function PriceFilter() {
|
|
208
|
+
const [priceRange, setPriceRange] = useState([25, 75]);
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Card className="w-full max-w-sm">
|
|
212
|
+
<CardHeader>
|
|
213
|
+
<CardTitle>Filter by Price</CardTitle>
|
|
214
|
+
</CardHeader>
|
|
215
|
+
<CardContent className="space-y-4">
|
|
216
|
+
<div className="space-y-2">
|
|
217
|
+
<Label>Price Range</Label>
|
|
218
|
+
<Slider
|
|
219
|
+
value={priceRange}
|
|
220
|
+
onValueChange={setPriceRange}
|
|
221
|
+
max={100}
|
|
222
|
+
step={1}
|
|
223
|
+
className="w-full"
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
<div className="flex justify-between text-sm">
|
|
227
|
+
<span>${priceRange[0]}</span>
|
|
228
|
+
<span>${priceRange[1]}</span>
|
|
229
|
+
</div>
|
|
230
|
+
<Button className="w-full">Apply Filter</Button>
|
|
231
|
+
</CardContent>
|
|
232
|
+
</Card>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### RGB Color Picker
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
function ColorPicker() {
|
|
241
|
+
const [red, setRed] = useState([128]);
|
|
242
|
+
const [green, setGreen] = useState([200]);
|
|
243
|
+
const [blue, setBlue] = useState([75]);
|
|
244
|
+
|
|
245
|
+
const rgbColor = `rgb(${red[0]}, ${green[0]}, ${blue[0]})`;
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<Card className="w-full max-w-sm">
|
|
249
|
+
<CardHeader>
|
|
250
|
+
<CardTitle>RGB Color Picker</CardTitle>
|
|
251
|
+
</CardHeader>
|
|
252
|
+
<CardContent className="space-y-6">
|
|
253
|
+
<div
|
|
254
|
+
className="w-full h-20 rounded-lg border"
|
|
255
|
+
style={{ backgroundColor: rgbColor }}
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
<div className="space-y-4">
|
|
259
|
+
<div className="space-y-2">
|
|
260
|
+
<div className="flex justify-between">
|
|
261
|
+
<Label>Red</Label>
|
|
262
|
+
<span className="text-sm text-muted-foreground">{red[0]}</span>
|
|
263
|
+
</div>
|
|
264
|
+
<Slider value={red} onValueChange={setRed} max={255} step={1} />
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div className="space-y-2">
|
|
268
|
+
<div className="flex justify-between">
|
|
269
|
+
<Label>Green</Label>
|
|
270
|
+
<span className="text-sm text-muted-foreground">{green[0]}</span>
|
|
271
|
+
</div>
|
|
272
|
+
<Slider value={green} onValueChange={setGreen} max={255} step={1} />
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div className="space-y-2">
|
|
276
|
+
<div className="flex justify-between">
|
|
277
|
+
<Label>Blue</Label>
|
|
278
|
+
<span className="text-sm text-muted-foreground">{blue[0]}</span>
|
|
279
|
+
</div>
|
|
280
|
+
<Slider value={blue} onValueChange={setBlue} max={255} step={1} />
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</CardContent>
|
|
284
|
+
</Card>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Deshabilitado
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
<Slider defaultValue={[50]} max={100} step={1} disabled className="w-full" />
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Casos de Uso
|
|
296
|
+
|
|
297
|
+
**Volume control**: Audio, música, efectos de sonido
|
|
298
|
+
**Price filters**: Rango de precios en e-commerce
|
|
299
|
+
**Color pickers**: RGB, HSL sliders
|
|
300
|
+
**Brightness/Contrast**: Ajustes de imagen
|
|
301
|
+
**Temperature**: Termostatos, controles de clima
|
|
302
|
+
**Range selection**: Filtros de fecha, edad, tamaño
|
|
303
|
+
|
|
304
|
+
## Estilos Base
|
|
305
|
+
|
|
306
|
+
- **Track height**: `h-1.5` (horizontal), `w-1.5` (vertical)
|
|
307
|
+
- **Track bg**: `bg-muted`
|
|
308
|
+
- **Range bg**: `bg-primary`
|
|
309
|
+
- **Thumb**: `size-4` con `border-primary`, `bg-white`, `shadow-sm`
|
|
310
|
+
- **Thumb hover/focus**: `ring-4` con `ring-ring/50`
|
|
311
|
+
- **Vertical**: `min-h-44` default, `flex-col`
|
|
312
|
+
|
|
313
|
+
## Accesibilidad
|
|
314
|
+
|
|
315
|
+
- ✅ **Role**: `role="slider"` con `aria-valuenow`, `aria-valuemin`, `aria-valuemax`
|
|
316
|
+
- ✅ **Keyboard**: Arrow keys para ajustar valor, Home/End para min/max
|
|
317
|
+
- ✅ **Focus**: Focus ring visible en thumbs
|
|
318
|
+
- ✅ **Label**: Asociar con `id` para screen readers
|
|
319
|
+
|
|
320
|
+
## Notas de Implementación
|
|
321
|
+
|
|
322
|
+
- **Radix UI**: Basado en `@radix-ui/react-slider`
|
|
323
|
+
- **Multiple thumbs**: Array length determina número de thumbs
|
|
324
|
+
- **Value format**: Siempre array, ej: `[50]` para single, `[20, 80]` para range
|
|
325
|
+
- **Auto-thumbs**: Genera thumbs automáticamente basado en `value.length`
|
|
326
|
+
- **Data attributes**: `data-orientation` para estilos horizontal/vertical
|
|
327
|
+
- **Theme support**: Prop `theme` para personalización
|
|
328
|
+
|
|
329
|
+
## Troubleshooting
|
|
330
|
+
|
|
331
|
+
**Thumb no se mueve**: Verifica `max`, `min` y `step` son números válidos
|
|
332
|
+
**Value no actualiza**: En modo controlado usa `value` + `onValueChange`, no `defaultValue`
|
|
333
|
+
**Range invertido**: Asegura `value[0] < value[1]` en ranges
|
|
334
|
+
**Vertical no funciona**: Agrega `className="h-[Xpx]"` para altura explícita
|
|
335
|
+
**Step no funciona**: `step` debe ser divisor válido del rango (max - min)
|
|
336
|
+
**Multiple thumbs**: `value={[10, 50, 90]}` crea 3 thumbs
|
|
337
|
+
|
|
338
|
+
## Referencias
|
|
339
|
+
|
|
340
|
+
- **Radix UI Slider**: <https://www.radix-ui.com/primitives/docs/components/slider>
|
|
341
|
+
- **shadcn/ui Slider**: <https://ui.shadcn.com/docs/components/slider>
|