@adamosuiteservices/ui 1.2.4 → 1.3.5

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.
Files changed (178) hide show
  1. package/README.md +4 -0
  2. package/dist/accordion-rounded.cjs +1 -1
  3. package/dist/accordion-rounded.js +1 -1
  4. package/dist/accordion.cjs +1 -1
  5. package/dist/accordion.js +1 -1
  6. package/dist/avatar.cjs +1 -1
  7. package/dist/avatar.js +1 -1
  8. package/dist/badge.cjs +1 -1
  9. package/dist/badge.js +1 -1
  10. package/dist/breadcrumb.cjs +1 -0
  11. package/dist/breadcrumb.js +105 -0
  12. package/dist/{button-C1n6snOY.js → button-2GdKenQI.js} +1 -1
  13. package/dist/{button-BV-_FVKZ.cjs → button-DEQVHMrX.cjs} +1 -1
  14. package/dist/button-group.cjs +1 -1
  15. package/dist/button-group.js +2 -2
  16. package/dist/button.cjs +1 -1
  17. package/dist/button.js +1 -1
  18. package/dist/calendar.cjs +1 -1
  19. package/dist/calendar.js +1 -1
  20. package/dist/{checkbox-BrmXPKTn.js → checkbox-Dr487kAg.js} +3 -3
  21. package/dist/{checkbox-Lq-HvSgc.cjs → checkbox-YWAnswaW.cjs} +1 -1
  22. package/dist/checkbox.cjs +1 -1
  23. package/dist/checkbox.js +1 -1
  24. package/dist/collapsible.cjs +1 -1
  25. package/dist/collapsible.js +1 -1
  26. package/dist/combobox.cjs +1 -1
  27. package/dist/combobox.js +6 -6
  28. package/dist/components/ui/breadcrumb/breadcrumb.d.ts +11 -0
  29. package/dist/components/ui/breadcrumb/breadcrumb.stories.d.ts +26 -0
  30. package/dist/components/ui/breadcrumb/index.d.ts +1 -0
  31. package/dist/components/ui/dialog/dialog.d.ts +2 -1
  32. package/dist/context-menu.cjs +1 -1
  33. package/dist/context-menu.js +2 -2
  34. package/dist/custom-layered-styles.css +1 -1
  35. package/dist/dialog.cjs +1 -1
  36. package/dist/dialog.js +33 -19
  37. package/dist/dropdown-menu.cjs +1 -1
  38. package/dist/dropdown-menu.js +3 -3
  39. package/dist/ellipsis-CryjZKZn.js +15 -0
  40. package/dist/ellipsis-Ct9VTDOG.cjs +6 -0
  41. package/dist/field.cjs +1 -1
  42. package/dist/field.js +2 -2
  43. package/dist/hover-card.cjs +1 -1
  44. package/dist/hover-card.js +6 -6
  45. package/dist/{index-CAOY367Y.js → index-B0M7VOwp.js} +2 -2
  46. package/dist/{index-B-ZRqW0J.js → index-BBoIAjAs.js} +3 -3
  47. package/dist/{index-gO_QEiaK.cjs → index-BDs8lUfq.cjs} +1 -1
  48. package/dist/index-BFyr34mw.cjs +5 -0
  49. package/dist/index-BMWt1NBG.js +79 -0
  50. package/dist/{index-yR-v1A4G.js → index-BX9hz-JD.js} +1 -1
  51. package/dist/{index-BGiGvaq8.cjs → index-BcGMAmWE.cjs} +1 -1
  52. package/dist/{index-IKJMQref.cjs → index-Bd0gQB0k.cjs} +1 -1
  53. package/dist/{index-VIUqZjyP.cjs → index-BeWgla7c.cjs} +1 -1
  54. package/dist/{index-EUea2gfp.js → index-BpWB3aFK.js} +1 -1
  55. package/dist/index-BvLQnI56.js +59 -0
  56. package/dist/{index-CwUFT-GQ.js → index-C0YiLSjW.js} +4 -4
  57. package/dist/{index-o0sNTcKe.js → index-CBjZooac.js} +2 -2
  58. package/dist/{index-DnS_sBBe.cjs → index-COuvjZLM.cjs} +1 -1
  59. package/dist/index-CTjlbbt9.cjs +1 -0
  60. package/dist/index-CUWMxxKG.js +97 -0
  61. package/dist/{index-C329e3yQ.js → index-CZZ3llmi.js} +2 -2
  62. package/dist/index-CjyiloO7.cjs +1 -0
  63. package/dist/{index-D3wSWKST.cjs → index-Cmx9M9cZ.cjs} +1 -1
  64. package/dist/index-CocSS1YK.cjs +1 -0
  65. package/dist/index-CzRiuk60.cjs +1 -0
  66. package/dist/index-DFPDUUq7.js +658 -0
  67. package/dist/{index-D3S7dBDI.cjs → index-DIwmXz1u.cjs} +1 -1
  68. package/dist/index-DLcqcWxM.js +29 -0
  69. package/dist/index-DMLQL2aG.js +286 -0
  70. package/dist/{index-DXQ-7kNJ.cjs → index-DMs8RL3E.cjs} +1 -1
  71. package/dist/{index-Ce3QBKyj.cjs → index-Dbj9vHNq.cjs} +1 -1
  72. package/dist/{index-BRLtxFFr.cjs → index-DmGzwG2z.cjs} +1 -1
  73. package/dist/{index-P1sVIHE3.js → index-PYkEXTqJ.js} +1 -1
  74. package/dist/{index-DulPG3F9.js → index-Se4vRnIO.js} +3 -3
  75. package/dist/index-_XxjJPRD.cjs +1 -0
  76. package/dist/{index-B-cHTKrs.js → index-yWvyIlmA.js} +4 -4
  77. package/dist/input-group.cjs +1 -1
  78. package/dist/input-group.js +1 -1
  79. package/dist/{label-Cne2J57f.cjs → label-BjXORCBM.cjs} +1 -1
  80. package/dist/{label-Ky8qBEC3.js → label-CmwGvhy1.js} +1 -1
  81. package/dist/label.cjs +1 -1
  82. package/dist/label.js +1 -1
  83. package/dist/pagination.cjs +1 -6
  84. package/dist/pagination.js +58 -69
  85. package/dist/popover-3rIoNCXs.js +306 -0
  86. package/dist/popover-FCKBtFo-.cjs +1 -0
  87. package/dist/popover.cjs +1 -1
  88. package/dist/popover.js +1 -1
  89. package/dist/progress.cjs +1 -1
  90. package/dist/progress.js +1 -1
  91. package/dist/radio-group.cjs +1 -1
  92. package/dist/radio-group.js +5 -5
  93. package/dist/select.cjs +2 -2
  94. package/dist/select.js +585 -542
  95. package/dist/{separator-CGnu_jIu.cjs → separator-BaZqZZ9R.cjs} +1 -1
  96. package/dist/{separator-BH73A90k.js → separator-DR7lQjv9.js} +1 -1
  97. package/dist/separator.cjs +1 -1
  98. package/dist/separator.js +1 -1
  99. package/dist/{sheet-CcxnJ6LH.cjs → sheet-CU-sFSaJ.cjs} +1 -1
  100. package/dist/{sheet-_DVpQIVF.js → sheet-UZWAbdXr.js} +1 -1
  101. package/dist/sheet.cjs +1 -1
  102. package/dist/sheet.js +1 -1
  103. package/dist/sidebar.cjs +1 -1
  104. package/dist/sidebar.js +4 -4
  105. package/dist/slider.cjs +1 -1
  106. package/dist/slider.js +3 -3
  107. package/dist/styles.css +1 -1
  108. package/dist/switch.cjs +1 -1
  109. package/dist/switch.js +2 -2
  110. package/dist/tabs-underline.cjs +1 -1
  111. package/dist/tabs-underline.js +1 -1
  112. package/dist/tabs.cjs +1 -1
  113. package/dist/tabs.js +1 -1
  114. package/dist/toaster.cjs +1 -1
  115. package/dist/toaster.js +1 -1
  116. package/dist/toggle.cjs +1 -1
  117. package/dist/toggle.js +1 -1
  118. package/dist/tooltip.cjs +1 -1
  119. package/dist/tooltip.js +114 -108
  120. package/dist/typography.cjs +1 -1
  121. package/dist/typography.js +16 -16
  122. package/docs/AI-GUIDE.md +321 -0
  123. package/docs/components/layout/sidebar.md +330 -0
  124. package/docs/components/layout/toaster.md +436 -0
  125. package/docs/components/ui/accordion-rounded.md +583 -0
  126. package/docs/components/ui/accordion.md +267 -0
  127. package/docs/components/ui/alert.md +671 -0
  128. package/docs/components/ui/avatar.md +588 -0
  129. package/docs/components/ui/badge.md +1024 -0
  130. package/docs/components/ui/breadcrumb.md +614 -0
  131. package/docs/components/ui/button-group.md +1002 -0
  132. package/docs/components/ui/button.md +1078 -0
  133. package/docs/components/ui/calendar.md +1159 -0
  134. package/docs/components/ui/card.md +1265 -0
  135. package/docs/components/ui/checkbox.md +292 -0
  136. package/docs/components/ui/collapsible.md +320 -0
  137. package/docs/components/ui/combobox.md +328 -0
  138. package/docs/components/ui/command.md +454 -0
  139. package/docs/components/ui/context-menu.md +540 -0
  140. package/docs/components/ui/dialog.md +628 -0
  141. package/docs/components/ui/dropdown-menu.md +731 -0
  142. package/docs/components/ui/field.md +706 -0
  143. package/docs/components/ui/hover-card.md +446 -0
  144. package/docs/components/ui/input-group.md +509 -0
  145. package/docs/components/ui/input.md +362 -0
  146. package/docs/components/ui/kbd.md +434 -0
  147. package/docs/components/ui/label.md +359 -0
  148. package/docs/components/ui/pagination.md +650 -0
  149. package/docs/components/ui/popover.md +536 -0
  150. package/docs/components/ui/progress.md +182 -0
  151. package/docs/components/ui/radio-group.md +311 -0
  152. package/docs/components/ui/select.md +352 -0
  153. package/docs/components/ui/separator.md +214 -0
  154. package/docs/components/ui/sheet.md +142 -0
  155. package/docs/components/ui/skeleton.md +140 -0
  156. package/docs/components/ui/slider.md +341 -0
  157. package/docs/components/ui/spinner.md +170 -0
  158. package/docs/components/ui/switch.md +402 -0
  159. package/docs/components/ui/table.md +183 -0
  160. package/docs/components/ui/tabs-underline.md +106 -0
  161. package/docs/components/ui/tabs.md +122 -0
  162. package/docs/components/ui/textarea.md +243 -0
  163. package/docs/components/ui/toggle.md +243 -0
  164. package/docs/components/ui/tooltip.md +320 -0
  165. package/docs/components/ui/typography.md +191 -0
  166. package/package.json +11 -5
  167. package/dist/index-6oTEokEx.js +0 -82
  168. package/dist/index-B-NyefE0.js +0 -243
  169. package/dist/index-BKbK2GzY.cjs +0 -1
  170. package/dist/index-BMitW9UR.cjs +0 -1
  171. package/dist/index-BpvjJ_T6.cjs +0 -5
  172. package/dist/index-C5wjudc-.js +0 -36
  173. package/dist/index-CezwiPd_.js +0 -615
  174. package/dist/index-D02K8KOB.js +0 -54
  175. package/dist/index-D7hQvndv.cjs +0 -1
  176. package/dist/index-DQvx1rG_.cjs +0 -1
  177. package/dist/popover-BjdTqaB8.cjs +0 -1
  178. package/dist/popover-EnVfE0YA.js +0 -263
@@ -0,0 +1,1002 @@
1
+ # Button Group Component
2
+
3
+ ## Descripción
4
+
5
+ Componente para **agrupar botones relacionados visualmente**, creando una interfaz cohesiva donde múltiples acciones están conectadas. Soporta orientación horizontal y vertical, separadores visuales, anidamiento de grupos, integración con inputs/selects, texto descriptivo, y manejo automático de bordes/esquinas. Ideal para toolbars, controles de medios, formateo de texto y acciones relacionadas.
6
+
7
+ ## Características
8
+
9
+ - ✅ 2 orientaciones: horizontal (default) y vertical
10
+ - ✅ Separadores visuales entre botones (ButtonGroupSeparator)
11
+ - ✅ Texto descriptivo dentro del grupo (ButtonGroupText)
12
+ - ✅ Soporte para grupos anidados con spacing automático
13
+ - ✅ Integración con Input y Select
14
+ - ✅ Bordes y esquinas automáticamente ajustados
15
+ - ✅ Focus z-index para overlapping correcto
16
+ - ✅ Split button pattern (acción + dropdown)
17
+ - ✅ Compatible con todos los tamaños de Button
18
+ - ✅ Role="group" para accesibilidad
19
+ - ✅ Width auto-ajustable (w-fit)
20
+
21
+ ## Importación
22
+
23
+ ```typescript
24
+ import {
25
+ ButtonGroup,
26
+ ButtonGroupSeparator,
27
+ ButtonGroupText,
28
+ } from "@adamosuiteservices/ui/button-group";
29
+ ```
30
+
31
+ ## Uso Básico
32
+
33
+ ### Grupo Horizontal Simple
34
+
35
+ ```tsx
36
+ import { Button } from "@adamosuiteservices/ui/button";
37
+
38
+ <ButtonGroup>
39
+ <Button variant="outline">Archive</Button>
40
+ <Button variant="outline">Report</Button>
41
+ <Button variant="outline">Snooze</Button>
42
+ </ButtonGroup>;
43
+ ```
44
+
45
+ **Comportamiento**: Los botones se conectan visualmente eliminando bordes internos y redondeando solo las esquinas externas.
46
+
47
+ ### Con Separadores
48
+
49
+ ```tsx
50
+ <ButtonGroup>
51
+ <Button variant="secondary" size="sm">
52
+ Copy
53
+ </Button>
54
+ <ButtonGroupSeparator />
55
+ <Button variant="secondary" size="sm">
56
+ Paste
57
+ </Button>
58
+ </ButtonGroup>
59
+ ```
60
+
61
+ **Uso**: Los separadores son útiles con variantes no-outline (secondary, default, etc.) para mejorar la separación visual.
62
+
63
+ ### Orientación Vertical
64
+
65
+ ```tsx
66
+ <ButtonGroup orientation="vertical" className="h-fit">
67
+ <Button variant="outline" size="icon">
68
+ <PlusIcon />
69
+ </Button>
70
+ <Button variant="outline" size="icon">
71
+ <MinusIcon />
72
+ </Button>
73
+ </ButtonGroup>
74
+ ```
75
+
76
+ **Nota**: Agrega `className="h-fit"` para evitar que el grupo se estire verticalmente.
77
+
78
+ ## Componentes
79
+
80
+ ### ButtonGroup (Root)
81
+
82
+ Contenedor principal que agrupa botones.
83
+
84
+ | Prop | Tipo | Default | Descripción |
85
+ | ----------- | -------------------------------- | -------------- | -------------------------------------------- |
86
+ | orientation | `"horizontal" \| "vertical"` | `"horizontal"` | Orientación del grupo |
87
+ | className | `string` | - | Clases CSS adicionales |
88
+ | children | `ReactNode` | - | Botones, separadores, texto, inputs, selects |
89
+ | ...props | `HTMLAttributes<HTMLDivElement>` | - | Props nativas de div |
90
+
91
+ **Atributos automáticos**:
92
+
93
+ - `role="group"`: Para accesibilidad
94
+ - `data-slot="button-group"`: Identificación interna
95
+ - `data-orientation`: Valor de orientation prop
96
+
97
+ **Estilos aplicados**:
98
+
99
+ - `flex`: Display flex
100
+ - `w-fit`: Ancho auto-ajustable
101
+ - `items-stretch`: Estira items verticalmente
102
+ - Bordes y esquinas ajustados automáticamente según orientation
103
+
104
+ ### ButtonGroupSeparator
105
+
106
+ Línea separadora visual entre botones.
107
+
108
+ | Prop | Tipo | Default | Descripción |
109
+ | ----------- | ---------------------------- | ------------ | ------------------------------ |
110
+ | orientation | `"horizontal" \| "vertical"` | `"vertical"` | Orientación del separador |
111
+ | className | `string` | - | Clases CSS adicionales |
112
+ | ...props | Props de Separator | - | Props del componente Separator |
113
+
114
+ **Estilos aplicados**:
115
+
116
+ - `bg-input`: Color de fondo
117
+ - `relative`: Posicionamiento
118
+ - `!m-0`: Sin márgenes
119
+ - `self-stretch`: Se estira para llenar altura/ancho
120
+ - `h-auto` cuando orientation="vertical"
121
+
122
+ **Nota**: Usa `orientation="vertical"` (default) para grupos horizontales, y `orientation="horizontal"` para grupos verticales.
123
+
124
+ ### ButtonGroupText
125
+
126
+ Texto descriptivo dentro del grupo.
127
+
128
+ | Prop | Tipo | Default | Descripción |
129
+ | --------- | -------------------------------- | ------- | --------------------------------- |
130
+ | asChild | `boolean` | `false` | Renderiza el hijo en lugar de div |
131
+ | className | `string` | - | Clases CSS adicionales |
132
+ | children | `ReactNode` | - | Texto o contenido |
133
+ | ...props | `HTMLAttributes<HTMLDivElement>` | - | Props nativas de div |
134
+
135
+ **Estilos aplicados**:
136
+
137
+ - `bg-muted`: Fondo gris claro
138
+ - `flex items-center gap-2`: Layout flex centrado
139
+ - `rounded-md border`: Bordes redondeados
140
+ - `px-4`: Padding horizontal
141
+ - `text-sm font-medium`: Tipografía
142
+ - `shadow-xs`: Sombra sutil
143
+ - Iconos auto-dimensionados a 16px
144
+
145
+ ## Orientaciones
146
+
147
+ ### horizontal (Default)
148
+
149
+ Botones en fila horizontal.
150
+
151
+ ```tsx
152
+ <ButtonGroup orientation="horizontal">
153
+ <Button variant="outline">First</Button>
154
+ <Button variant="outline">Second</Button>
155
+ <Button variant="outline">Third</Button>
156
+ </ButtonGroup>
157
+ ```
158
+
159
+ **Estilos automáticos**:
160
+
161
+ - `:not(:first-child)`: `rounded-l-none border-l-0` (elimina esquinas izquierdas y borde)
162
+ - `:not(:last-child)`: `rounded-r-none` (elimina esquinas derechas)
163
+
164
+ ### vertical
165
+
166
+ Botones apilados verticalmente.
167
+
168
+ ```tsx
169
+ <ButtonGroup orientation="vertical" className="h-fit">
170
+ <Button variant="outline">First</Button>
171
+ <Button variant="outline">Second</Button>
172
+ <Button variant="outline">Third</Button>
173
+ </ButtonGroup>
174
+ ```
175
+
176
+ **Estilos automáticos**:
177
+
178
+ - `flex-col`: Dirección vertical
179
+ - `:not(:first-child)`: `rounded-t-none border-t-0` (elimina esquinas superiores y borde)
180
+ - `:not(:last-child)`: `rounded-b-none` (elimina esquinas inferiores)
181
+
182
+ ## Tamaños
183
+
184
+ Todos los tamaños de Button funcionan en ButtonGroup:
185
+
186
+ ```tsx
187
+ {
188
+ /* Small */
189
+ }
190
+ <ButtonGroup>
191
+ <Button variant="outline" size="sm">
192
+ Small
193
+ </Button>
194
+ <Button variant="outline" size="sm">
195
+ Buttons
196
+ </Button>
197
+ <Button variant="outline" size="icon-sm">
198
+ <PlusIcon />
199
+ </Button>
200
+ </ButtonGroup>;
201
+
202
+ {
203
+ /* Default */
204
+ }
205
+ <ButtonGroup>
206
+ <Button variant="outline">Default</Button>
207
+ <Button variant="outline">Buttons</Button>
208
+ <Button variant="outline" size="icon">
209
+ <PlusIcon />
210
+ </Button>
211
+ </ButtonGroup>;
212
+
213
+ {
214
+ /* Large */
215
+ }
216
+ <ButtonGroup>
217
+ <Button variant="outline" size="lg">
218
+ Large
219
+ </Button>
220
+ <Button variant="outline" size="lg">
221
+ Buttons
222
+ </Button>
223
+ <Button variant="outline" size="icon-lg">
224
+ <PlusIcon />
225
+ </Button>
226
+ </ButtonGroup>;
227
+ ```
228
+
229
+ **Regla importante**: Mantén tamaños consistentes dentro del mismo grupo.
230
+
231
+ ## Patrones Avanzados
232
+
233
+ ### Grupos Anidados
234
+
235
+ Para crear toolbars complejas con espaciado entre grupos lógicos.
236
+
237
+ ```tsx
238
+ <ButtonGroup>
239
+ <ButtonGroup>
240
+ <Button variant="outline" size="sm">
241
+ 1
242
+ </Button>
243
+ <Button variant="outline" size="sm">
244
+ 2
245
+ </Button>
246
+ <Button variant="outline" size="sm">
247
+ 3
248
+ </Button>
249
+ </ButtonGroup>
250
+ <ButtonGroup>
251
+ <Button variant="outline" size="icon-sm">
252
+ <ArrowLeftIcon />
253
+ </Button>
254
+ <Button variant="outline" size="icon-sm">
255
+ <ArrowRightIcon />
256
+ </Button>
257
+ </ButtonGroup>
258
+ </ButtonGroup>
259
+ ```
260
+
261
+ **Comportamiento**: Los grupos anidados tienen `gap-2` automático entre ellos.
262
+
263
+ ### Split Button
264
+
265
+ Botón con acción principal y menú dropdown.
266
+
267
+ ```tsx
268
+ <ButtonGroup>
269
+ <Button variant="secondary">Deploy</Button>
270
+ <ButtonGroupSeparator />
271
+ <Button size="icon" variant="secondary">
272
+ <ChevronDownIcon />
273
+ </Button>
274
+ </ButtonGroup>
275
+ ```
276
+
277
+ **Uso común**: Acción por defecto + opciones adicionales en dropdown.
278
+
279
+ ### Con Input (Search Box)
280
+
281
+ ```tsx
282
+ import { Input } from "@adamosuiteservices/ui/input";
283
+ import { SearchIcon } from "lucide-react";
284
+
285
+ <ButtonGroup>
286
+ <Input placeholder="Search..." />
287
+ <Button variant="outline" aria-label="Search">
288
+ <SearchIcon />
289
+ </Button>
290
+ </ButtonGroup>;
291
+ ```
292
+
293
+ **Comportamiento**: El input se integra visualmente con el botón, creando una barra de búsqueda cohesiva.
294
+
295
+ ### Con Select (Currency Selector)
296
+
297
+ ```tsx
298
+ import { Input } from "@adamosuiteservices/ui/input";
299
+ import {
300
+ Select,
301
+ SelectTrigger,
302
+ SelectContent,
303
+ SelectItem,
304
+ } from "@adamosuiteservices/ui/select";
305
+ import { ArrowRightIcon } from "lucide-react";
306
+
307
+ function CurrencyInput() {
308
+ const [currency, setCurrency] = useState("$");
309
+
310
+ return (
311
+ <ButtonGroup>
312
+ <ButtonGroup>
313
+ <Select value={currency} onValueChange={setCurrency}>
314
+ <SelectTrigger className="font-mono">{currency}</SelectTrigger>
315
+ <SelectContent className="min-w-24">
316
+ <SelectItem value="$">$ US Dollar</SelectItem>
317
+ <SelectItem value="€">€ Euro</SelectItem>
318
+ <SelectItem value="£">£ British Pound</SelectItem>
319
+ </SelectContent>
320
+ </Select>
321
+ <Input placeholder="10.00" pattern="[0-9]*" />
322
+ </ButtonGroup>
323
+ <ButtonGroup>
324
+ <Button aria-label="Send" size="icon" variant="outline">
325
+ <ArrowRightIcon />
326
+ </Button>
327
+ </ButtonGroup>
328
+ </ButtonGroup>
329
+ );
330
+ }
331
+ ```
332
+
333
+ ### Controles de Medios
334
+
335
+ Player de audio/video con controles agrupados.
336
+
337
+ ```tsx
338
+ import {
339
+ PlayIcon,
340
+ PauseIcon,
341
+ SkipBackIcon,
342
+ SkipForwardIcon,
343
+ SquareIcon,
344
+ } from "lucide-react";
345
+ import { VolumeXIcon, Volume2Icon } from "lucide-react";
346
+
347
+ function MediaControls() {
348
+ const [isPlaying, setIsPlaying] = useState(false);
349
+
350
+ return (
351
+ <div className="space-y-4">
352
+ {/* Controles de reproducción */}
353
+ <ButtonGroup>
354
+ <Button variant="outline" size="icon">
355
+ <SkipBackIcon />
356
+ </Button>
357
+ <Button
358
+ variant="outline"
359
+ size="icon"
360
+ onClick={() => setIsPlaying(!isPlaying)}
361
+ >
362
+ {isPlaying ? <PauseIcon /> : <PlayIcon />}
363
+ </Button>
364
+ <Button variant="outline" size="icon">
365
+ <SquareIcon />
366
+ </Button>
367
+ <Button variant="outline" size="icon">
368
+ <SkipForwardIcon />
369
+ </Button>
370
+ </ButtonGroup>
371
+
372
+ {/* Controles de volumen */}
373
+ <ButtonGroup>
374
+ <Button variant="outline" size="icon">
375
+ <VolumeXIcon />
376
+ </Button>
377
+ <ButtonGroupText className="px-4">Volume</ButtonGroupText>
378
+ <Button variant="outline" size="icon">
379
+ <Volume2Icon />
380
+ </Button>
381
+ </ButtonGroup>
382
+ </div>
383
+ );
384
+ }
385
+ ```
386
+
387
+ ### Editor de Texto (Toolbar)
388
+
389
+ Controles de formateo con estados toggle.
390
+
391
+ ```tsx
392
+ import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react";
393
+ import { AlignLeftIcon, AlignCenterIcon, AlignRightIcon } from "lucide-react";
394
+
395
+ function TextEditor() {
396
+ const [formatting, setFormatting] = useState({
397
+ bold: false,
398
+ italic: false,
399
+ underline: false,
400
+ });
401
+ const [alignment, setAlignment] = useState("left");
402
+
403
+ return (
404
+ <div className="space-y-4">
405
+ {/* Formato de texto */}
406
+ <ButtonGroup>
407
+ <Button
408
+ variant={formatting.bold ? "default" : "outline"}
409
+ size="icon"
410
+ onClick={() =>
411
+ setFormatting((prev) => ({ ...prev, bold: !prev.bold }))
412
+ }
413
+ >
414
+ <BoldIcon />
415
+ </Button>
416
+ <Button
417
+ variant={formatting.italic ? "default" : "outline"}
418
+ size="icon"
419
+ onClick={() =>
420
+ setFormatting((prev) => ({ ...prev, italic: !prev.italic }))
421
+ }
422
+ >
423
+ <ItalicIcon />
424
+ </Button>
425
+ <Button
426
+ variant={formatting.underline ? "default" : "outline"}
427
+ size="icon"
428
+ onClick={() =>
429
+ setFormatting((prev) => ({ ...prev, underline: !prev.underline }))
430
+ }
431
+ >
432
+ <UnderlineIcon />
433
+ </Button>
434
+ </ButtonGroup>
435
+
436
+ {/* Alineación */}
437
+ <ButtonGroup>
438
+ <Button
439
+ variant={alignment === "left" ? "default" : "outline"}
440
+ size="icon"
441
+ onClick={() => setAlignment("left")}
442
+ >
443
+ <AlignLeftIcon />
444
+ </Button>
445
+ <Button
446
+ variant={alignment === "center" ? "default" : "outline"}
447
+ size="icon"
448
+ onClick={() => setAlignment("center")}
449
+ >
450
+ <AlignCenterIcon />
451
+ </Button>
452
+ <Button
453
+ variant={alignment === "right" ? "default" : "outline"}
454
+ size="icon"
455
+ onClick={() => setAlignment("right")}
456
+ >
457
+ <AlignRightIcon />
458
+ </Button>
459
+ </ButtonGroup>
460
+ </div>
461
+ );
462
+ }
463
+ ```
464
+
465
+ ### Acciones de Archivo
466
+
467
+ Grupos para operaciones de archivo.
468
+
469
+ ```tsx
470
+ import { SaveIcon, DownloadIcon, UploadIcon } from "lucide-react";
471
+ import {
472
+ UndoIcon,
473
+ RedoIcon,
474
+ CopyIcon,
475
+ ShareIcon,
476
+ TrashIcon,
477
+ } from "lucide-react";
478
+
479
+ <div className="space-y-4">
480
+ {/* Acciones principales */}
481
+ <ButtonGroup>
482
+ <Button variant="outline">
483
+ <SaveIcon />
484
+ Save
485
+ </Button>
486
+ <Button variant="outline">
487
+ <DownloadIcon />
488
+ Download
489
+ </Button>
490
+ <Button variant="outline">
491
+ <UploadIcon />
492
+ Upload
493
+ </Button>
494
+ </ButtonGroup>
495
+
496
+ {/* Acciones rápidas */}
497
+ <ButtonGroup>
498
+ <Button variant="outline" size="icon">
499
+ <UndoIcon />
500
+ </Button>
501
+ <Button variant="outline" size="icon">
502
+ <RedoIcon />
503
+ </Button>
504
+ <ButtonGroupSeparator />
505
+ <Button variant="outline" size="icon">
506
+ <CopyIcon />
507
+ </Button>
508
+ <Button variant="outline" size="icon">
509
+ <ShareIcon />
510
+ </Button>
511
+ <ButtonGroupSeparator />
512
+ <Button variant="outline" size="icon">
513
+ <TrashIcon />
514
+ </Button>
515
+ </ButtonGroup>
516
+ </div>;
517
+ ```
518
+
519
+ ### Toolbar Compleja
520
+
521
+ Layout con múltiples grupos anidados.
522
+
523
+ ```tsx
524
+ import {
525
+ ArrowLeftIcon,
526
+ MoreHorizontalIcon,
527
+ SettingsIcon,
528
+ RefreshCwIcon,
529
+ } from "lucide-react";
530
+
531
+ <ButtonGroup>
532
+ <ButtonGroup>
533
+ <Button variant="outline" size="icon">
534
+ <ArrowLeftIcon />
535
+ </Button>
536
+ </ButtonGroup>
537
+ <ButtonGroup>
538
+ <Button variant="outline">Archive</Button>
539
+ <Button variant="outline">Report</Button>
540
+ </ButtonGroup>
541
+ <ButtonGroup>
542
+ <Button variant="outline">Snooze</Button>
543
+ <Button variant="outline" size="icon">
544
+ <MoreHorizontalIcon />
545
+ </Button>
546
+ </ButtonGroup>
547
+ <ButtonGroup>
548
+ <Button variant="outline" size="icon">
549
+ <SettingsIcon />
550
+ </Button>
551
+ <Button variant="outline" size="icon">
552
+ <RefreshCwIcon />
553
+ </Button>
554
+ </ButtonGroup>
555
+ </ButtonGroup>;
556
+ ```
557
+
558
+ ### Con Texto Descriptivo
559
+
560
+ Etiquetas para claridad contextual.
561
+
562
+ ```tsx
563
+ import { EditIcon, CopyIcon } from "lucide-react";
564
+
565
+ <div className="space-y-4">
566
+ {/* Texto antes */}
567
+ <ButtonGroup>
568
+ <ButtonGroupText>Actions:</ButtonGroupText>
569
+ <Button variant="outline">Edit</Button>
570
+ <Button variant="outline">Delete</Button>
571
+ </ButtonGroup>
572
+
573
+ {/* Texto entre botones */}
574
+ <ButtonGroup>
575
+ <Button variant="outline">
576
+ <EditIcon />
577
+ Edit
578
+ </Button>
579
+ <ButtonGroupText>or</ButtonGroupText>
580
+ <Button variant="outline">
581
+ <CopyIcon />
582
+ Copy
583
+ </Button>
584
+ </ButtonGroup>
585
+ </div>;
586
+ ```
587
+
588
+ ## Casos de Uso Comunes
589
+
590
+ ### Paginación
591
+
592
+ ```tsx
593
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
594
+
595
+ <ButtonGroup>
596
+ <Button variant="outline" size="icon" disabled={currentPage === 1}>
597
+ <ChevronLeftIcon />
598
+ </Button>
599
+ <ButtonGroupText>
600
+ {currentPage} of {totalPages}
601
+ </ButtonGroupText>
602
+ <Button variant="outline" size="icon" disabled={currentPage === totalPages}>
603
+ <ChevronRightIcon />
604
+ </Button>
605
+ </ButtonGroup>;
606
+ ```
607
+
608
+ ### Filtros
609
+
610
+ ```tsx
611
+ <ButtonGroup>
612
+ <ButtonGroupText>Filter:</ButtonGroupText>
613
+ <Button
614
+ variant={filter === "all" ? "default" : "outline"}
615
+ onClick={() => setFilter("all")}
616
+ >
617
+ All
618
+ </Button>
619
+ <Button
620
+ variant={filter === "active" ? "default" : "outline"}
621
+ onClick={() => setFilter("active")}
622
+ >
623
+ Active
624
+ </Button>
625
+ <Button
626
+ variant={filter === "archived" ? "default" : "outline"}
627
+ onClick={() => setFilter("archived")}
628
+ >
629
+ Archived
630
+ </Button>
631
+ </ButtonGroup>
632
+ ```
633
+
634
+ ### Tabs Alternativos
635
+
636
+ ```tsx
637
+ <ButtonGroup>
638
+ <Button
639
+ variant={tab === "overview" ? "default" : "outline"}
640
+ onClick={() => setTab("overview")}
641
+ >
642
+ Overview
643
+ </Button>
644
+ <Button
645
+ variant={tab === "analytics" ? "default" : "outline"}
646
+ onClick={() => setTab("analytics")}
647
+ >
648
+ Analytics
649
+ </Button>
650
+ <Button
651
+ variant={tab === "reports" ? "default" : "outline"}
652
+ onClick={() => setTab("reports")}
653
+ >
654
+ Reports
655
+ </Button>
656
+ </ButtonGroup>
657
+ ```
658
+
659
+ ### Vista Toggle (List/Grid)
660
+
661
+ ```tsx
662
+ import { LayoutGridIcon, LayoutListIcon } from "lucide-react";
663
+
664
+ <ButtonGroup>
665
+ <Button
666
+ variant={view === "grid" ? "default" : "outline"}
667
+ size="icon"
668
+ onClick={() => setView("grid")}
669
+ aria-label="Grid view"
670
+ >
671
+ <LayoutGridIcon />
672
+ </Button>
673
+ <Button
674
+ variant={view === "list" ? "default" : "outline"}
675
+ size="icon"
676
+ onClick={() => setView("list")}
677
+ aria-label="List view"
678
+ >
679
+ <LayoutListIcon />
680
+ </Button>
681
+ </ButtonGroup>;
682
+ ```
683
+
684
+ ### Zoom Controls
685
+
686
+ ```tsx
687
+ import { ZoomInIcon, ZoomOutIcon } from "lucide-react";
688
+
689
+ <ButtonGroup>
690
+ <Button variant="outline" size="icon" onClick={handleZoomOut}>
691
+ <ZoomOutIcon />
692
+ </Button>
693
+ <ButtonGroupText className="px-6 font-mono">{zoom}%</ButtonGroupText>
694
+ <Button variant="outline" size="icon" onClick={handleZoomIn}>
695
+ <ZoomInIcon />
696
+ </Button>
697
+ </ButtonGroup>;
698
+ ```
699
+
700
+ ## Mejores Prácticas
701
+
702
+ ### Consistencia de Tamaños
703
+
704
+ ```tsx
705
+ {
706
+ /* ✅ Correcto - Tamaños consistentes */
707
+ }
708
+ <ButtonGroup>
709
+ <Button variant="outline" size="sm">
710
+ One
711
+ </Button>
712
+ <Button variant="outline" size="sm">
713
+ Two
714
+ </Button>
715
+ <Button variant="outline" size="icon-sm">
716
+ <PlusIcon />
717
+ </Button>
718
+ </ButtonGroup>;
719
+
720
+ {
721
+ /* ❌ Incorrecto - Mezcla de tamaños */
722
+ }
723
+ <ButtonGroup>
724
+ <Button variant="outline" size="sm">
725
+ One
726
+ </Button>
727
+ <Button variant="outline">Two</Button> {/* Tamaño diferente */}
728
+ <Button variant="outline" size="lg">
729
+ Three
730
+ </Button>
731
+ </ButtonGroup>;
732
+ ```
733
+
734
+ ### Variantes Consistentes
735
+
736
+ ```tsx
737
+ {
738
+ /* ✅ Correcto - Misma variante */
739
+ }
740
+ <ButtonGroup>
741
+ <Button variant="outline">One</Button>
742
+ <Button variant="outline">Two</Button>
743
+ <Button variant="outline">Three</Button>
744
+ </ButtonGroup>;
745
+
746
+ {
747
+ /* ⚠️ Permitido - Variantes diferentes para estado activo */
748
+ }
749
+ <ButtonGroup>
750
+ <Button variant={isActive ? "default" : "outline"}>Active</Button>
751
+ <Button variant="outline">Inactive</Button>
752
+ </ButtonGroup>;
753
+ ```
754
+
755
+ ### Separadores con Variantes No-Outline
756
+
757
+ ```tsx
758
+ {
759
+ /* ✅ Correcto - Separador con variante secondary */
760
+ }
761
+ <ButtonGroup>
762
+ <Button variant="secondary">Copy</Button>
763
+ <ButtonGroupSeparator />
764
+ <Button variant="secondary">Paste</Button>
765
+ </ButtonGroup>;
766
+
767
+ {
768
+ /* ⚠️ Innecesario - Separador con outline (ya hay bordes) */
769
+ }
770
+ <ButtonGroup>
771
+ <Button variant="outline">Copy</Button>
772
+ <ButtonGroupSeparator /> {/* No se ve bien */}
773
+ <Button variant="outline">Paste</Button>
774
+ </ButtonGroup>;
775
+ ```
776
+
777
+ ### aria-label en Botones de Icono
778
+
779
+ ```tsx
780
+ {
781
+ /* ✅ Correcto - Labels descriptivas */
782
+ }
783
+ <ButtonGroup>
784
+ <Button variant="outline" size="icon" aria-label="Previous page">
785
+ <ArrowLeftIcon />
786
+ </Button>
787
+ <Button variant="outline" size="icon" aria-label="Next page">
788
+ <ArrowRightIcon />
789
+ </Button>
790
+ </ButtonGroup>;
791
+
792
+ {
793
+ /* ❌ Incorrecto - Sin labels */
794
+ }
795
+ <ButtonGroup>
796
+ <Button variant="outline" size="icon">
797
+ <ArrowLeftIcon />
798
+ </Button>
799
+ <Button variant="outline" size="icon">
800
+ <ArrowRightIcon />
801
+ </Button>
802
+ </ButtonGroup>;
803
+ ```
804
+
805
+ ### Grupos Anidados para Organización
806
+
807
+ ```tsx
808
+ {
809
+ /* ✅ Correcto - Agrupación lógica */
810
+ }
811
+ <ButtonGroup>
812
+ <ButtonGroup>
813
+ {/* Acciones de navegación */}
814
+ <Button variant="outline" size="icon">
815
+ <ArrowLeftIcon />
816
+ </Button>
817
+ <Button variant="outline" size="icon">
818
+ <ArrowRightIcon />
819
+ </Button>
820
+ </ButtonGroup>
821
+ <ButtonGroup>
822
+ {/* Acciones de contenido */}
823
+ <Button variant="outline">Save</Button>
824
+ <Button variant="outline">Delete</Button>
825
+ </ButtonGroup>
826
+ </ButtonGroup>;
827
+
828
+ {
829
+ /* ❌ Incorrecto - Todo mezclado */
830
+ }
831
+ <ButtonGroup>
832
+ <Button variant="outline" size="icon">
833
+ <ArrowLeftIcon />
834
+ </Button>
835
+ <Button variant="outline">Save</Button>
836
+ <Button variant="outline" size="icon">
837
+ <ArrowRightIcon />
838
+ </Button>
839
+ <Button variant="outline">Delete</Button>
840
+ </ButtonGroup>;
841
+ ```
842
+
843
+ ## Notas de Implementación
844
+
845
+ - Basado en CVA (class-variance-authority) para variants
846
+ - Usa selectores CSS avanzados para bordes/esquinas automáticas
847
+ - Focus z-index (`focus-visible:z-10 focus-visible:relative`) previene overlap
848
+ - Width auto-ajustable con `w-fit`
849
+ - `items-stretch` estira items verticalmente para altura consistente
850
+ - Separadores usan componente Separator con `self-stretch`
851
+ - Grupos anidados detectados con `has-[>[data-slot=button-group]]` para aplicar gap
852
+ - Input en grupo usa `flex-1` para expandirse
853
+ - Select width ajustado con `[&>[data-slot=select-trigger]:not([class*=w-])]:w-fit`
854
+ - Role="group" para accesibilidad semántica
855
+
856
+ ## Accesibilidad
857
+
858
+ ### Role Group
859
+
860
+ ```tsx
861
+ {
862
+ /* ✅ Automático - role="group" aplicado */
863
+ }
864
+ <ButtonGroup>
865
+ <Button variant="outline">One</Button>
866
+ <Button variant="outline">Two</Button>
867
+ </ButtonGroup>;
868
+ ```
869
+
870
+ ### aria-label para Grupos
871
+
872
+ ```tsx
873
+ {
874
+ /* ✅ Correcto - Describe el propósito del grupo */
875
+ }
876
+ <ButtonGroup aria-label="Text alignment options">
877
+ <Button variant="outline" size="icon" aria-label="Align left">
878
+ <AlignLeftIcon />
879
+ </Button>
880
+ <Button variant="outline" size="icon" aria-label="Align center">
881
+ <AlignCenterIcon />
882
+ </Button>
883
+ <Button variant="outline" size="icon" aria-label="Align right">
884
+ <AlignRightIcon />
885
+ </Button>
886
+ </ButtonGroup>;
887
+ ```
888
+
889
+ ### Navegación por Teclado
890
+
891
+ - ✅ **Tab**: Navega entre botones del grupo
892
+ - ✅ **Arrow keys**: No implementado por defecto (considera `role="toolbar"` si necesitas)
893
+ - ✅ **Space/Enter**: Activa el botón enfocado
894
+
895
+ ### Estados Toggle
896
+
897
+ ```tsx
898
+ {
899
+ /* ✅ Correcto - aria-pressed para toggles */
900
+ }
901
+ <ButtonGroup>
902
+ <Button
903
+ variant={isBold ? "default" : "outline"}
904
+ size="icon"
905
+ aria-pressed={isBold}
906
+ onClick={() => setIsBold(!isBold)}
907
+ >
908
+ <BoldIcon />
909
+ </Button>
910
+ </ButtonGroup>;
911
+ ```
912
+
913
+ ### Focus Visual
914
+
915
+ El focus ring se superpone correctamente gracias a `focus-visible:z-10 focus-visible:relative`.
916
+
917
+ ## Troubleshooting
918
+
919
+ ### Grupo Vertical Demasiado Alto
920
+
921
+ **Problema**: El grupo vertical se estira al 100% de altura.
922
+
923
+ **Solución**:
924
+
925
+ ```tsx
926
+ // ❌ Problema - se estira
927
+ <ButtonGroup orientation="vertical">
928
+ <Button variant="outline">One</Button>
929
+ <Button variant="outline">Two</Button>
930
+ </ButtonGroup>
931
+
932
+ // ✅ Solución - agrega h-fit
933
+ <ButtonGroup orientation="vertical" className="h-fit">
934
+ <Button variant="outline">One</Button>
935
+ <Button variant="outline">Two</Button>
936
+ </ButtonGroup>
937
+ ```
938
+
939
+ ### Bordes Dobles Visibles
940
+
941
+ **Problema**: Se ven bordes dobles entre botones.
942
+
943
+ **Solución**:
944
+
945
+ ```tsx
946
+ // ❌ Problema - variantes con bordes gruesos
947
+ <ButtonGroup>
948
+ <Button>One</Button> {/* variant="default" tiene border */}
949
+ <Button>Two</Button>
950
+ </ButtonGroup>
951
+
952
+ // ✅ Solución - usa outline o agrega separadores
953
+ <ButtonGroup>
954
+ <Button variant="outline">One</Button>
955
+ <Button variant="outline">Two</Button>
956
+ </ButtonGroup>
957
+ ```
958
+
959
+ ### Input No Se Expande
960
+
961
+ **Problema**: Input dentro del grupo no toma espacio disponible.
962
+
963
+ **Solución**:
964
+
965
+ ```tsx
966
+ // ✅ El input automáticamente usa flex-1
967
+ <ButtonGroup>
968
+ <Input placeholder="Search..." /> {/* Se expande automáticamente */}
969
+ <Button variant="outline" size="icon">
970
+ <SearchIcon />
971
+ </Button>
972
+ </ButtonGroup>
973
+ ```
974
+
975
+ ### Spacing entre Grupos Anidados
976
+
977
+ **Problema**: No hay espacio entre grupos anidados.
978
+
979
+ **Solución**:
980
+
981
+ ```tsx
982
+ // ✅ El spacing se aplica automáticamente
983
+ <ButtonGroup>
984
+ <ButtonGroup>
985
+ <Button variant="outline">1</Button>
986
+ <Button variant="outline">2</Button>
987
+ </ButtonGroup>
988
+ <ButtonGroup>
989
+ {" "}
990
+ {/* gap-2 automático */}
991
+ <Button variant="outline">3</Button>
992
+ <Button variant="outline">4</Button>
993
+ </ButtonGroup>
994
+ </ButtonGroup>
995
+ ```
996
+
997
+ ## Referencias
998
+
999
+ - CVA (Class Variance Authority): https://cva.style/docs
1000
+ - ARIA Group Role: https://www.w3.org/TR/wai-aria-1.2/#group
1001
+ - ARIA Toolbar: https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/
1002
+ - Button Component: Ver documentación de Button