@adamosuiteservices/ui 2.11.15 → 2.11.17

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 (41) hide show
  1. package/dist/colors.css +1 -1
  2. package/dist/styles.css +1 -1
  3. package/dist/themes.css +1 -1
  4. package/docs/AI-GUIDE.md +321 -321
  5. package/docs/components/layout/sidebar.md +399 -399
  6. package/docs/components/layout/toaster.md +436 -436
  7. package/docs/components/ui/accordion-rounded.md +584 -584
  8. package/docs/components/ui/accordion.md +269 -269
  9. package/docs/components/ui/button-group.md +984 -984
  10. package/docs/components/ui/button.md +1137 -1137
  11. package/docs/components/ui/calendar.md +1159 -1159
  12. package/docs/components/ui/card.md +1455 -1455
  13. package/docs/components/ui/checkbox.md +292 -292
  14. package/docs/components/ui/collapsible.md +323 -323
  15. package/docs/components/ui/command.md +454 -454
  16. package/docs/components/ui/context-menu.md +540 -540
  17. package/docs/components/ui/dialog.md +628 -628
  18. package/docs/components/ui/dropdown-menu.md +709 -709
  19. package/docs/components/ui/field.md +706 -706
  20. package/docs/components/ui/hover-card.md +446 -446
  21. package/docs/components/ui/input.md +362 -362
  22. package/docs/components/ui/kbd.md +434 -434
  23. package/docs/components/ui/label.md +359 -359
  24. package/docs/components/ui/pagination.md +650 -650
  25. package/docs/components/ui/popover.md +536 -536
  26. package/docs/components/ui/progress.md +182 -182
  27. package/docs/components/ui/radio-group.md +311 -311
  28. package/docs/components/ui/select.md +352 -352
  29. package/docs/components/ui/separator.md +214 -214
  30. package/docs/components/ui/sheet.md +142 -142
  31. package/docs/components/ui/skeleton.md +140 -140
  32. package/docs/components/ui/slider.md +341 -341
  33. package/docs/components/ui/spinner.md +170 -170
  34. package/docs/components/ui/switch.md +408 -408
  35. package/docs/components/ui/tabs-underline.md +106 -106
  36. package/docs/components/ui/tabs.md +122 -122
  37. package/docs/components/ui/textarea.md +243 -243
  38. package/docs/components/ui/toggle.md +237 -237
  39. package/docs/components/ui/tooltip.md +317 -317
  40. package/docs/components/ui/typography.md +280 -280
  41. package/package.json +1 -1
@@ -1,650 +1,650 @@
1
- # Pagination
2
-
3
- Controles de navegación para contenido paginado. Incluye Previous/Next, números de página, ellipsis para gaps y estado activo.
4
-
5
- ## Descripción
6
-
7
- El componente `Pagination` proporciona controles de navegación para contenido paginado.
8
-
9
- ## Importación
10
-
11
- ```typescript
12
- import {
13
- Pagination,
14
- PaginationContent,
15
- PaginationEllipsis,
16
- PaginationItem,
17
- PaginationLink,
18
- PaginationNext,
19
- PaginationPrevious,
20
- } from "@adamosuiteservices/ui/pagination";
21
- ```
22
-
23
- ## Anatomía
24
-
25
- ```tsx
26
- <Pagination>
27
- <PaginationContent>
28
- <PaginationItem>
29
- <PaginationPrevious href="#" />
30
- </PaginationItem>
31
- <PaginationItem>
32
- <PaginationLink href="#">1</PaginationLink>
33
- </PaginationItem>
34
- <PaginationItem>
35
- <PaginationLink href="#" isActive>
36
- 2
37
- </PaginationLink>
38
- </PaginationItem>
39
- <PaginationItem>
40
- <PaginationLink href="#">3</PaginationLink>
41
- </PaginationItem>
42
- <PaginationItem>
43
- <PaginationEllipsis />
44
- </PaginationItem>
45
- <PaginationItem>
46
- <PaginationNext href="#" />
47
- </PaginationItem>
48
- </PaginationContent>
49
- </Pagination>
50
- ```
51
-
52
- **Componentes**: 7 (Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis)
53
-
54
- ## Props Principales
55
-
56
- ### Pagination (Root)
57
-
58
- | Prop | Tipo | Descripción |
59
- | ----------- | -------- | ---------------------- |
60
- | `className` | `string` | Clases CSS adicionales |
61
-
62
- **Nota**: `<nav>` con `role="navigation"` y `aria-label="pagination"`
63
-
64
- ### PaginationContent
65
-
66
- | Prop | Tipo | Descripción |
67
- | ----------- | -------- | ---------------------- |
68
- | `className` | `string` | Clases CSS adicionales |
69
-
70
- **Nota**: `<ul>` con flex y gap-1
71
-
72
- ### PaginationItem
73
-
74
- | Prop | Tipo | Descripción |
75
- | ----------- | -------- | ---------------------- |
76
- | `className` | `string` | Clases CSS adicionales |
77
-
78
- **Nota**: `<li>` wrapper simple
79
-
80
- ### PaginationLink
81
-
82
- | Prop | Tipo | Default | Descripción |
83
- | ----------- | ------------- | -------- | ------------------------ |
84
- | `isActive` | `boolean` | `false` | Marca página como activa |
85
- | `size` | Button size | `"icon"` | Tamaño del link |
86
- | `href` | `string` | - | URL destino |
87
- | `onClick` | `(e) => void` | - | Click handler |
88
- | `className` | `string` | - | Clases CSS adicionales |
89
-
90
- **Nota**: `<a>` estilizado como Button
91
-
92
- ### PaginationPrevious
93
-
94
- | Prop | Tipo | Default | Descripción |
95
- | ----------- | ------------- | ------------ | ---------------------- |
96
- | `children` | `ReactNode` | `"Previous"` | Texto del botón |
97
- | `href` | `string` | - | URL destino |
98
- | `onClick` | `(e) => void` | - | Click handler |
99
- | `className` | `string` | - | Clases CSS adicionales |
100
-
101
- **Nota**: PaginationLink con chevron_left icon (Material Symbol), texto "Previous" oculto en mobile
102
-
103
- ### PaginationNext
104
-
105
- | Prop | Tipo | Default | Descripción |
106
- | ----------- | ------------- | -------- | ---------------------- |
107
- | `children` | `ReactNode` | `"Next"` | Texto del botón |
108
- | `href` | `string` | - | URL destino |
109
- | `onClick` | `(e) => void` | - | Click handler |
110
- | `className` | `string` | - | Clases CSS adicionales |
111
-
112
- **Nota**: PaginationLink con chevron_right icon (Material Symbol), texto "Next" oculto en mobile
113
-
114
- ### PaginationEllipsis
115
-
116
- | Prop | Tipo | Descripción |
117
- | ----------- | -------- | ---------------------- |
118
- | `className` | `string` | Clases CSS adicionales |
119
-
120
- **Nota**: `<span>` con more_horiz icon (Material Symbol) y `aria-hidden`
121
-
122
- ## Patrones de Uso
123
-
124
- ### Básico
125
-
126
- ```tsx
127
- <Pagination>
128
- <PaginationContent>
129
- <PaginationItem>
130
- <PaginationPrevious href="#" />
131
- </PaginationItem>
132
- <PaginationItem>
133
- <PaginationLink href="#">1</PaginationLink>
134
- </PaginationItem>
135
- <PaginationItem>
136
- <PaginationLink href="#" isActive>
137
- 2
138
- </PaginationLink>
139
- </PaginationItem>
140
- <PaginationItem>
141
- <PaginationLink href="#">3</PaginationLink>
142
- </PaginationItem>
143
- <PaginationItem>
144
- <PaginationEllipsis />
145
- </PaginationItem>
146
- <PaginationItem>
147
- <PaginationNext href="#" />
148
- </PaginationItem>
149
- </PaginationContent>
150
- </Pagination>
151
- ```
152
-
153
- ### Simple (Sin Ellipsis)
154
-
155
- ```tsx
156
- <Pagination>
157
- <PaginationContent>
158
- <PaginationItem>
159
- <PaginationPrevious href="#" />
160
- </PaginationItem>
161
- <PaginationItem>
162
- <PaginationLink href="#">1</PaginationLink>
163
- </PaginationItem>
164
- <PaginationItem>
165
- <PaginationLink href="#">2</PaginationLink>
166
- </PaginationItem>
167
- <PaginationItem>
168
- <PaginationLink href="#">3</PaginationLink>
169
- </PaginationItem>
170
- <PaginationItem>
171
- <PaginationNext href="#" />
172
- </PaginationItem>
173
- </PaginationContent>
174
- </Pagination>
175
- ```
176
-
177
- ### Con Ellipsis Doble
178
-
179
- ```tsx
180
- <Pagination>
181
- <PaginationContent>
182
- <PaginationItem>
183
- <PaginationPrevious href="#" />
184
- </PaginationItem>
185
- <PaginationItem>
186
- <PaginationLink href="#">1</PaginationLink>
187
- </PaginationItem>
188
- <PaginationItem>
189
- <PaginationEllipsis />
190
- </PaginationItem>
191
- <PaginationItem>
192
- <PaginationLink href="#">10</PaginationLink>
193
- </PaginationItem>
194
- <PaginationItem>
195
- <PaginationLink href="#" isActive>
196
- 11
197
- </PaginationLink>
198
- </PaginationItem>
199
- <PaginationItem>
200
- <PaginationLink href="#">12</PaginationLink>
201
- </PaginationItem>
202
- <PaginationItem>
203
- <PaginationEllipsis />
204
- </PaginationItem>
205
- <PaginationItem>
206
- <PaginationLink href="#">50</PaginationLink>
207
- </PaginationItem>
208
- <PaginationItem>
209
- <PaginationNext href="#" />
210
- </PaginationItem>
211
- </PaginationContent>
212
- </Pagination>
213
- ```
214
-
215
- ### Primera Página (Disabled Previous)
216
-
217
- ```tsx
218
- <Pagination>
219
- <PaginationContent>
220
- <PaginationItem>
221
- <PaginationPrevious href="#" className="pointer-events-none opacity-50" />
222
- </PaginationItem>
223
- <PaginationItem>
224
- <PaginationLink href="#" isActive>
225
- 1
226
- </PaginationLink>
227
- </PaginationItem>
228
- <PaginationItem>
229
- <PaginationLink href="#">2</PaginationLink>
230
- </PaginationItem>
231
- <PaginationItem>
232
- <PaginationLink href="#">3</PaginationLink>
233
- </PaginationItem>
234
- <PaginationItem>
235
- <PaginationEllipsis />
236
- </PaginationItem>
237
- <PaginationItem>
238
- <PaginationLink href="#">25</PaginationLink>
239
- </PaginationItem>
240
- <PaginationItem>
241
- <PaginationNext href="#" />
242
- </PaginationItem>
243
- </PaginationContent>
244
- </Pagination>
245
- ```
246
-
247
- ### Última Página (Disabled Next)
248
-
249
- ```tsx
250
- <Pagination>
251
- <PaginationContent>
252
- <PaginationItem>
253
- <PaginationPrevious href="#" />
254
- </PaginationItem>
255
- <PaginationItem>
256
- <PaginationLink href="#">1</PaginationLink>
257
- </PaginationItem>
258
- <PaginationItem>
259
- <PaginationEllipsis />
260
- </PaginationItem>
261
- <PaginationItem>
262
- <PaginationLink href="#">23</PaginationLink>
263
- </PaginationItem>
264
- <PaginationItem>
265
- <PaginationLink href="#">24</PaginationLink>
266
- </PaginationItem>
267
- <PaginationItem>
268
- <PaginationLink href="#" isActive>
269
- 25
270
- </PaginationLink>
271
- </PaginationItem>
272
- <PaginationItem>
273
- <PaginationNext href="#" className="pointer-events-none opacity-50" />
274
- </PaginationItem>
275
- </PaginationContent>
276
- </Pagination>
277
- ```
278
-
279
- ### Página Única
280
-
281
- ```tsx
282
- <Pagination>
283
- <PaginationContent>
284
- <PaginationItem>
285
- <PaginationPrevious href="#" className="pointer-events-none opacity-50" />
286
- </PaginationItem>
287
- <PaginationItem>
288
- <PaginationLink href="#" isActive>
289
- 1
290
- </PaginationLink>
291
- </PaginationItem>
292
- <PaginationItem>
293
- <PaginationNext href="#" className="pointer-events-none opacity-50" />
294
- </PaginationItem>
295
- </PaginationContent>
296
- </Pagination>
297
- ```
298
-
299
- ### Compacto (Solo Previous/Next)
300
-
301
- ```tsx
302
- <Pagination className="justify-start">
303
- <PaginationContent>
304
- <PaginationItem>
305
- <PaginationPrevious href="#" />
306
- </PaginationItem>
307
- <PaginationItem>
308
- <PaginationNext href="#" />
309
- </PaginationItem>
310
- </PaginationContent>
311
- </Pagination>
312
- ```
313
-
314
- ### Con Información de Página
315
-
316
- ```tsx
317
- <div className="space-y-4">
318
- <div className="flex items-center justify-between text-sm text-muted-foreground">
319
- <span>Showing 1-10 of 250 results</span>
320
- <span>Page 2 of 25</span>
321
- </div>
322
-
323
- <Pagination>
324
- <PaginationContent>
325
- <PaginationItem>
326
- <PaginationPrevious href="#" />
327
- </PaginationItem>
328
- <PaginationItem>
329
- <PaginationLink href="#">1</PaginationLink>
330
- </PaginationItem>
331
- <PaginationItem>
332
- <PaginationLink href="#" isActive>
333
- 2
334
- </PaginationLink>
335
- </PaginationItem>
336
- <PaginationItem>
337
- <PaginationLink href="#">3</PaginationLink>
338
- </PaginationItem>
339
- <PaginationItem>
340
- <PaginationEllipsis />
341
- </PaginationItem>
342
- <PaginationItem>
343
- <PaginationLink href="#">25</PaginationLink>
344
- </PaginationItem>
345
- <PaginationItem>
346
- <PaginationNext href="#" />
347
- </PaginationItem>
348
- </PaginationContent>
349
- </Pagination>
350
- </div>
351
- ```
352
-
353
- ### Interactivo (Con Estado)
354
-
355
- ```tsx
356
- import { useState } from "react";
357
-
358
- function App() {
359
- const [currentPage, setCurrentPage] = useState(5);
360
- const totalPages = 20;
361
-
362
- const generatePageNumbers = () => {
363
- const pages = [];
364
-
365
- // Always show first page
366
- pages.push(1);
367
-
368
- // Add ellipsis if there's a gap
369
- if (currentPage > 4) {
370
- pages.push(-1); // -1 represents ellipsis
371
- }
372
-
373
- // Show pages around current page
374
- const start = Math.max(2, currentPage - 1);
375
- const end = Math.min(totalPages - 1, currentPage + 1);
376
-
377
- for (let i = start; i <= end; i++) {
378
- if (i !== 1 && i !== totalPages) {
379
- pages.push(i);
380
- }
381
- }
382
-
383
- // Add ellipsis if there's a gap
384
- if (currentPage < totalPages - 3) {
385
- pages.push(-2); // -2 represents ellipsis
386
- }
387
-
388
- // Always show last page if more than 1 page
389
- if (totalPages > 1) {
390
- pages.push(totalPages);
391
- }
392
-
393
- return pages;
394
- };
395
-
396
- const pages = generatePageNumbers();
397
-
398
- const handlePageClick = (page: number) => {
399
- setCurrentPage(page);
400
- };
401
-
402
- const handlePrevious = () => {
403
- if (currentPage > 1) {
404
- setCurrentPage(currentPage - 1);
405
- }
406
- };
407
-
408
- const handleNext = () => {
409
- if (currentPage < totalPages) {
410
- setCurrentPage(currentPage + 1);
411
- }
412
- };
413
-
414
- return (
415
- <div className="space-y-4">
416
- <p className="text-center text-sm text-muted-foreground">
417
- Page {currentPage} of {totalPages}
418
- </p>
419
-
420
- <Pagination>
421
- <PaginationContent>
422
- <PaginationItem>
423
- <PaginationPrevious
424
- href="#"
425
- onClick={(e) => {
426
- e.preventDefault();
427
- handlePrevious();
428
- }}
429
- className={
430
- currentPage === 1 ? "pointer-events-none opacity-50" : ""
431
- }
432
- />
433
- </PaginationItem>
434
-
435
- {pages.map((page, index) => (
436
- <PaginationItem key={index}>
437
- {page < 0 ? (
438
- <PaginationEllipsis />
439
- ) : (
440
- <PaginationLink
441
- href="#"
442
- isActive={page === currentPage}
443
- onClick={(e) => {
444
- e.preventDefault();
445
- handlePageClick(page);
446
- }}
447
- >
448
- {page}
449
- </PaginationLink>
450
- )}
451
- </PaginationItem>
452
- ))}
453
-
454
- <PaginationItem>
455
- <PaginationNext
456
- href="#"
457
- onClick={(e) => {
458
- e.preventDefault();
459
- handleNext();
460
- }}
461
- className={
462
- currentPage === totalPages
463
- ? "pointer-events-none opacity-50"
464
- : ""
465
- }
466
- />
467
- </PaginationItem>
468
- </PaginationContent>
469
- </Pagination>
470
- </div>
471
- );
472
- }
473
- ```
474
-
475
- ### Tamaños Personalizados
476
-
477
- ```tsx
478
- {
479
- /* Small */
480
- }
481
- <Pagination>
482
- <PaginationContent className="gap-0.5">
483
- <PaginationItem>
484
- <PaginationPrevious href="#" className="h-8 px-2 text-xs" />
485
- </PaginationItem>
486
- <PaginationItem>
487
- <PaginationLink href="#" className="h-8 w-8 text-xs">
488
- 1
489
- </PaginationLink>
490
- </PaginationItem>
491
- <PaginationItem>
492
- <PaginationLink href="#" isActive className="h-8 w-8 text-xs">
493
- 2
494
- </PaginationLink>
495
- </PaginationItem>
496
- <PaginationItem>
497
- <PaginationNext href="#" className="h-8 px-2 text-xs" />
498
- </PaginationItem>
499
- </PaginationContent>
500
- </Pagination>;
501
-
502
- {
503
- /* Large */
504
- }
505
- <Pagination>
506
- <PaginationContent className="gap-2">
507
- <PaginationItem>
508
- <PaginationPrevious href="#" className="h-12 px-4 text-base" />
509
- </PaginationItem>
510
- <PaginationItem>
511
- <PaginationLink href="#" className="h-12 w-12 text-base">
512
- 1
513
- </PaginationLink>
514
- </PaginationItem>
515
- <PaginationItem>
516
- <PaginationLink href="#" isActive className="h-12 w-12 text-base">
517
- 2
518
- </PaginationLink>
519
- </PaginationItem>
520
- <PaginationItem>
521
- <PaginationNext href="#" className="h-12 px-4 text-base" />
522
- </PaginationItem>
523
- </PaginationContent>
524
- </Pagination>;
525
- ```
526
-
527
- ## Casos de Uso Comunes
528
-
529
- **Tables**: Paginación de tablas con muchas filas
530
- **Lists**: Listas largas de items (productos, usuarios, posts)
531
- **Search results**: Resultados de búsqueda paginados
532
- **Galleries**: Galerías de imágenes o archivos
533
- **Admin panels**: Dashboards con data paginada
534
- **API results**: Navegación de resultados de API con offset/limit
535
-
536
- ## Estados y Data Attributes
537
-
538
- ### PaginationLink
539
-
540
- - **Active**: `isActive` → variant outline, `aria-current="page"`
541
- - **Inactive**: variant ghost
542
-
543
- ### Disabled (Previous/Next)
544
-
545
- No hay prop disabled nativa, se simula con:
546
-
547
- ```tsx
548
- className = "pointer-events-none opacity-50";
549
- ```
550
-
551
- ## Estilos Base
552
-
553
- ### Pagination (Root)
554
-
555
- - **Layout**: `mx-auto flex w-full justify-center`
556
- - **Semantic**: `<nav role="navigation" aria-label="pagination">`
557
-
558
- ### PaginationContent
559
-
560
- - **Layout**: `flex flex-row items-center gap-1`
561
-
562
- ### PaginationLink
563
-
564
- - **Size default**: `size="icon"` (h-9 w-9)
565
- - **Variants**: ghost (inactive), outline (active)
566
-
567
- ### PaginationPrevious/Next
568
-
569
- - **Size**: `size="default"`
570
- - **Padding**: `px-2.5`
571
- - **Responsive**: Texto oculto en mobile (`hidden sm:block`)
572
- - **Gap**: `gap-1` entre icono y texto
573
-
574
- ### PaginationEllipsis
575
-
576
- - **Size**: `size-9`
577
- - **Icon**: more_horiz (Material Symbol) con `text-lg`
578
- - **Layout**: `flex items-center justify-center`
579
- - **ARIA**: `aria-hidden`
580
-
581
- ## Responsive
582
-
583
- - **Mobile**: Previous/Next solo muestran icono
584
- - **Desktop (sm+)**: Previous/Next muestran icono + texto
585
-
586
- ## Accesibilidad
587
-
588
- - ✅ **Semantic**: `<nav role="navigation" aria-label="pagination">`
589
- - ✅ **Current page**: `aria-current="page"` en página activa
590
- - ✅ **Screen readers**: Ellipsis tiene `<span className="sr-only">More pages</span>`
591
- - ✅ **Keyboard**: Navegación con Tab, activación con Enter/Space
592
- - ✅ **Labels**: Previous/Next tienen `aria-label` descriptivo
593
- - ⚠️ **Disabled**: Usa `pointer-events-none opacity-50` + previene click en handler
594
-
595
- ## Notas de Implementación
596
-
597
- - **Links**: PaginationLink es `<a>` no `<button>`, usa Button styles
598
- - **Icons**: chevron_left, chevron_right, more_horiz (Material Symbols)
599
- - **Responsive text**: Previous/Next usan `hidden sm:block` en texto
600
- - **No state**: Componente stateless, maneja estado externamente
601
- - **Ellipsis logic**: Representación negativa (-1, -2) común para identificar ellipsis
602
- - **Disabled**: No hay prop disabled, usa className manual
603
- - **Centering**: Root tiene `mx-auto justify-center`
604
-
605
- ## Algoritmo de Generación de Páginas
606
-
607
- Patrón común para mostrar páginas con ellipsis:
608
-
609
- ```tsx
610
- const generatePageNumbers = (current: number, total: number) => {
611
- const pages = [];
612
-
613
- // Siempre primera página
614
- pages.push(1);
615
-
616
- // Ellipsis izquierda si hay gap
617
- if (current > 4) pages.push(-1);
618
-
619
- // Páginas alrededor de current
620
- const start = Math.max(2, current - 1);
621
- const end = Math.min(total - 1, current + 1);
622
- for (let i = start; i <= end; i++) {
623
- if (i !== 1 && i !== total) pages.push(i);
624
- }
625
-
626
- // Ellipsis derecha si hay gap
627
- if (current < total - 3) pages.push(-2);
628
-
629
- // Siempre última página
630
- if (total > 1) pages.push(total);
631
-
632
- return pages;
633
- };
634
- ```
635
-
636
- ## Troubleshooting
637
-
638
- **Previous/Next no se deshabilitan**: No hay prop disabled, usa `className="pointer-events-none opacity-50"` y previene click en handler
639
- **Active no se ve**: Verifica `isActive` prop y que solo una página lo tenga
640
- **Texto Previous/Next no se ve en mobile**: Comportamiento esperado, usa `hidden sm:block`
641
- **Ellipsis clickeable**: Ellipsis no debe ser clickeable, es `<span>` no link
642
- **Spacing inconsistente**: PaginationContent tiene `gap-1`, ajusta con className
643
- **No centrado**: Root tiene `justify-center`, override con `className="justify-start"` si necesario
644
- **Links no funcionan**: PaginationLink usa `<a>`, asegúrate de pasar `href` o `onClick`
645
- **Too many pages**: Usa ellipsis y lógica de generación de páginas para mostrar subset
646
-
647
- ## Referencias
648
-
649
- - **shadcn/ui Pagination**: <https://ui.shadcn.com/docs/components/pagination>
650
- - **WAI-ARIA Pagination**: <https://www.w3.org/WAI/ARIA/apg/patterns/pagination/>
1
+ # Pagination
2
+
3
+ Controles de navegación para contenido paginado. Incluye Previous/Next, números de página, ellipsis para gaps y estado activo.
4
+
5
+ ## Descripción
6
+
7
+ El componente `Pagination` proporciona controles de navegación para contenido paginado.
8
+
9
+ ## Importación
10
+
11
+ ```typescript
12
+ import {
13
+ Pagination,
14
+ PaginationContent,
15
+ PaginationEllipsis,
16
+ PaginationItem,
17
+ PaginationLink,
18
+ PaginationNext,
19
+ PaginationPrevious,
20
+ } from "@adamosuiteservices/ui/pagination";
21
+ ```
22
+
23
+ ## Anatomía
24
+
25
+ ```tsx
26
+ <Pagination>
27
+ <PaginationContent>
28
+ <PaginationItem>
29
+ <PaginationPrevious href="#" />
30
+ </PaginationItem>
31
+ <PaginationItem>
32
+ <PaginationLink href="#">1</PaginationLink>
33
+ </PaginationItem>
34
+ <PaginationItem>
35
+ <PaginationLink href="#" isActive>
36
+ 2
37
+ </PaginationLink>
38
+ </PaginationItem>
39
+ <PaginationItem>
40
+ <PaginationLink href="#">3</PaginationLink>
41
+ </PaginationItem>
42
+ <PaginationItem>
43
+ <PaginationEllipsis />
44
+ </PaginationItem>
45
+ <PaginationItem>
46
+ <PaginationNext href="#" />
47
+ </PaginationItem>
48
+ </PaginationContent>
49
+ </Pagination>
50
+ ```
51
+
52
+ **Componentes**: 7 (Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis)
53
+
54
+ ## Props Principales
55
+
56
+ ### Pagination (Root)
57
+
58
+ | Prop | Tipo | Descripción |
59
+ | ----------- | -------- | ---------------------- |
60
+ | `className` | `string` | Clases CSS adicionales |
61
+
62
+ **Nota**: `<nav>` con `role="navigation"` y `aria-label="pagination"`
63
+
64
+ ### PaginationContent
65
+
66
+ | Prop | Tipo | Descripción |
67
+ | ----------- | -------- | ---------------------- |
68
+ | `className` | `string` | Clases CSS adicionales |
69
+
70
+ **Nota**: `<ul>` con flex y gap-1
71
+
72
+ ### PaginationItem
73
+
74
+ | Prop | Tipo | Descripción |
75
+ | ----------- | -------- | ---------------------- |
76
+ | `className` | `string` | Clases CSS adicionales |
77
+
78
+ **Nota**: `<li>` wrapper simple
79
+
80
+ ### PaginationLink
81
+
82
+ | Prop | Tipo | Default | Descripción |
83
+ | ----------- | ------------- | -------- | ------------------------ |
84
+ | `isActive` | `boolean` | `false` | Marca página como activa |
85
+ | `size` | Button size | `"icon"` | Tamaño del link |
86
+ | `href` | `string` | - | URL destino |
87
+ | `onClick` | `(e) => void` | - | Click handler |
88
+ | `className` | `string` | - | Clases CSS adicionales |
89
+
90
+ **Nota**: `<a>` estilizado como Button
91
+
92
+ ### PaginationPrevious
93
+
94
+ | Prop | Tipo | Default | Descripción |
95
+ | ----------- | ------------- | ------------ | ---------------------- |
96
+ | `children` | `ReactNode` | `"Previous"` | Texto del botón |
97
+ | `href` | `string` | - | URL destino |
98
+ | `onClick` | `(e) => void` | - | Click handler |
99
+ | `className` | `string` | - | Clases CSS adicionales |
100
+
101
+ **Nota**: PaginationLink con chevron_left icon (Material Symbol), texto "Previous" oculto en mobile
102
+
103
+ ### PaginationNext
104
+
105
+ | Prop | Tipo | Default | Descripción |
106
+ | ----------- | ------------- | -------- | ---------------------- |
107
+ | `children` | `ReactNode` | `"Next"` | Texto del botón |
108
+ | `href` | `string` | - | URL destino |
109
+ | `onClick` | `(e) => void` | - | Click handler |
110
+ | `className` | `string` | - | Clases CSS adicionales |
111
+
112
+ **Nota**: PaginationLink con chevron_right icon (Material Symbol), texto "Next" oculto en mobile
113
+
114
+ ### PaginationEllipsis
115
+
116
+ | Prop | Tipo | Descripción |
117
+ | ----------- | -------- | ---------------------- |
118
+ | `className` | `string` | Clases CSS adicionales |
119
+
120
+ **Nota**: `<span>` con more_horiz icon (Material Symbol) y `aria-hidden`
121
+
122
+ ## Patrones de Uso
123
+
124
+ ### Básico
125
+
126
+ ```tsx
127
+ <Pagination>
128
+ <PaginationContent>
129
+ <PaginationItem>
130
+ <PaginationPrevious href="#" />
131
+ </PaginationItem>
132
+ <PaginationItem>
133
+ <PaginationLink href="#">1</PaginationLink>
134
+ </PaginationItem>
135
+ <PaginationItem>
136
+ <PaginationLink href="#" isActive>
137
+ 2
138
+ </PaginationLink>
139
+ </PaginationItem>
140
+ <PaginationItem>
141
+ <PaginationLink href="#">3</PaginationLink>
142
+ </PaginationItem>
143
+ <PaginationItem>
144
+ <PaginationEllipsis />
145
+ </PaginationItem>
146
+ <PaginationItem>
147
+ <PaginationNext href="#" />
148
+ </PaginationItem>
149
+ </PaginationContent>
150
+ </Pagination>
151
+ ```
152
+
153
+ ### Simple (Sin Ellipsis)
154
+
155
+ ```tsx
156
+ <Pagination>
157
+ <PaginationContent>
158
+ <PaginationItem>
159
+ <PaginationPrevious href="#" />
160
+ </PaginationItem>
161
+ <PaginationItem>
162
+ <PaginationLink href="#">1</PaginationLink>
163
+ </PaginationItem>
164
+ <PaginationItem>
165
+ <PaginationLink href="#">2</PaginationLink>
166
+ </PaginationItem>
167
+ <PaginationItem>
168
+ <PaginationLink href="#">3</PaginationLink>
169
+ </PaginationItem>
170
+ <PaginationItem>
171
+ <PaginationNext href="#" />
172
+ </PaginationItem>
173
+ </PaginationContent>
174
+ </Pagination>
175
+ ```
176
+
177
+ ### Con Ellipsis Doble
178
+
179
+ ```tsx
180
+ <Pagination>
181
+ <PaginationContent>
182
+ <PaginationItem>
183
+ <PaginationPrevious href="#" />
184
+ </PaginationItem>
185
+ <PaginationItem>
186
+ <PaginationLink href="#">1</PaginationLink>
187
+ </PaginationItem>
188
+ <PaginationItem>
189
+ <PaginationEllipsis />
190
+ </PaginationItem>
191
+ <PaginationItem>
192
+ <PaginationLink href="#">10</PaginationLink>
193
+ </PaginationItem>
194
+ <PaginationItem>
195
+ <PaginationLink href="#" isActive>
196
+ 11
197
+ </PaginationLink>
198
+ </PaginationItem>
199
+ <PaginationItem>
200
+ <PaginationLink href="#">12</PaginationLink>
201
+ </PaginationItem>
202
+ <PaginationItem>
203
+ <PaginationEllipsis />
204
+ </PaginationItem>
205
+ <PaginationItem>
206
+ <PaginationLink href="#">50</PaginationLink>
207
+ </PaginationItem>
208
+ <PaginationItem>
209
+ <PaginationNext href="#" />
210
+ </PaginationItem>
211
+ </PaginationContent>
212
+ </Pagination>
213
+ ```
214
+
215
+ ### Primera Página (Disabled Previous)
216
+
217
+ ```tsx
218
+ <Pagination>
219
+ <PaginationContent>
220
+ <PaginationItem>
221
+ <PaginationPrevious href="#" className="pointer-events-none opacity-50" />
222
+ </PaginationItem>
223
+ <PaginationItem>
224
+ <PaginationLink href="#" isActive>
225
+ 1
226
+ </PaginationLink>
227
+ </PaginationItem>
228
+ <PaginationItem>
229
+ <PaginationLink href="#">2</PaginationLink>
230
+ </PaginationItem>
231
+ <PaginationItem>
232
+ <PaginationLink href="#">3</PaginationLink>
233
+ </PaginationItem>
234
+ <PaginationItem>
235
+ <PaginationEllipsis />
236
+ </PaginationItem>
237
+ <PaginationItem>
238
+ <PaginationLink href="#">25</PaginationLink>
239
+ </PaginationItem>
240
+ <PaginationItem>
241
+ <PaginationNext href="#" />
242
+ </PaginationItem>
243
+ </PaginationContent>
244
+ </Pagination>
245
+ ```
246
+
247
+ ### Última Página (Disabled Next)
248
+
249
+ ```tsx
250
+ <Pagination>
251
+ <PaginationContent>
252
+ <PaginationItem>
253
+ <PaginationPrevious href="#" />
254
+ </PaginationItem>
255
+ <PaginationItem>
256
+ <PaginationLink href="#">1</PaginationLink>
257
+ </PaginationItem>
258
+ <PaginationItem>
259
+ <PaginationEllipsis />
260
+ </PaginationItem>
261
+ <PaginationItem>
262
+ <PaginationLink href="#">23</PaginationLink>
263
+ </PaginationItem>
264
+ <PaginationItem>
265
+ <PaginationLink href="#">24</PaginationLink>
266
+ </PaginationItem>
267
+ <PaginationItem>
268
+ <PaginationLink href="#" isActive>
269
+ 25
270
+ </PaginationLink>
271
+ </PaginationItem>
272
+ <PaginationItem>
273
+ <PaginationNext href="#" className="pointer-events-none opacity-50" />
274
+ </PaginationItem>
275
+ </PaginationContent>
276
+ </Pagination>
277
+ ```
278
+
279
+ ### Página Única
280
+
281
+ ```tsx
282
+ <Pagination>
283
+ <PaginationContent>
284
+ <PaginationItem>
285
+ <PaginationPrevious href="#" className="pointer-events-none opacity-50" />
286
+ </PaginationItem>
287
+ <PaginationItem>
288
+ <PaginationLink href="#" isActive>
289
+ 1
290
+ </PaginationLink>
291
+ </PaginationItem>
292
+ <PaginationItem>
293
+ <PaginationNext href="#" className="pointer-events-none opacity-50" />
294
+ </PaginationItem>
295
+ </PaginationContent>
296
+ </Pagination>
297
+ ```
298
+
299
+ ### Compacto (Solo Previous/Next)
300
+
301
+ ```tsx
302
+ <Pagination className="justify-start">
303
+ <PaginationContent>
304
+ <PaginationItem>
305
+ <PaginationPrevious href="#" />
306
+ </PaginationItem>
307
+ <PaginationItem>
308
+ <PaginationNext href="#" />
309
+ </PaginationItem>
310
+ </PaginationContent>
311
+ </Pagination>
312
+ ```
313
+
314
+ ### Con Información de Página
315
+
316
+ ```tsx
317
+ <div className="space-y-4">
318
+ <div className="flex items-center justify-between text-sm text-muted-foreground">
319
+ <span>Showing 1-10 of 250 results</span>
320
+ <span>Page 2 of 25</span>
321
+ </div>
322
+
323
+ <Pagination>
324
+ <PaginationContent>
325
+ <PaginationItem>
326
+ <PaginationPrevious href="#" />
327
+ </PaginationItem>
328
+ <PaginationItem>
329
+ <PaginationLink href="#">1</PaginationLink>
330
+ </PaginationItem>
331
+ <PaginationItem>
332
+ <PaginationLink href="#" isActive>
333
+ 2
334
+ </PaginationLink>
335
+ </PaginationItem>
336
+ <PaginationItem>
337
+ <PaginationLink href="#">3</PaginationLink>
338
+ </PaginationItem>
339
+ <PaginationItem>
340
+ <PaginationEllipsis />
341
+ </PaginationItem>
342
+ <PaginationItem>
343
+ <PaginationLink href="#">25</PaginationLink>
344
+ </PaginationItem>
345
+ <PaginationItem>
346
+ <PaginationNext href="#" />
347
+ </PaginationItem>
348
+ </PaginationContent>
349
+ </Pagination>
350
+ </div>
351
+ ```
352
+
353
+ ### Interactivo (Con Estado)
354
+
355
+ ```tsx
356
+ import { useState } from "react";
357
+
358
+ function App() {
359
+ const [currentPage, setCurrentPage] = useState(5);
360
+ const totalPages = 20;
361
+
362
+ const generatePageNumbers = () => {
363
+ const pages = [];
364
+
365
+ // Always show first page
366
+ pages.push(1);
367
+
368
+ // Add ellipsis if there's a gap
369
+ if (currentPage > 4) {
370
+ pages.push(-1); // -1 represents ellipsis
371
+ }
372
+
373
+ // Show pages around current page
374
+ const start = Math.max(2, currentPage - 1);
375
+ const end = Math.min(totalPages - 1, currentPage + 1);
376
+
377
+ for (let i = start; i <= end; i++) {
378
+ if (i !== 1 && i !== totalPages) {
379
+ pages.push(i);
380
+ }
381
+ }
382
+
383
+ // Add ellipsis if there's a gap
384
+ if (currentPage < totalPages - 3) {
385
+ pages.push(-2); // -2 represents ellipsis
386
+ }
387
+
388
+ // Always show last page if more than 1 page
389
+ if (totalPages > 1) {
390
+ pages.push(totalPages);
391
+ }
392
+
393
+ return pages;
394
+ };
395
+
396
+ const pages = generatePageNumbers();
397
+
398
+ const handlePageClick = (page: number) => {
399
+ setCurrentPage(page);
400
+ };
401
+
402
+ const handlePrevious = () => {
403
+ if (currentPage > 1) {
404
+ setCurrentPage(currentPage - 1);
405
+ }
406
+ };
407
+
408
+ const handleNext = () => {
409
+ if (currentPage < totalPages) {
410
+ setCurrentPage(currentPage + 1);
411
+ }
412
+ };
413
+
414
+ return (
415
+ <div className="space-y-4">
416
+ <p className="text-center text-sm text-muted-foreground">
417
+ Page {currentPage} of {totalPages}
418
+ </p>
419
+
420
+ <Pagination>
421
+ <PaginationContent>
422
+ <PaginationItem>
423
+ <PaginationPrevious
424
+ href="#"
425
+ onClick={(e) => {
426
+ e.preventDefault();
427
+ handlePrevious();
428
+ }}
429
+ className={
430
+ currentPage === 1 ? "pointer-events-none opacity-50" : ""
431
+ }
432
+ />
433
+ </PaginationItem>
434
+
435
+ {pages.map((page, index) => (
436
+ <PaginationItem key={index}>
437
+ {page < 0 ? (
438
+ <PaginationEllipsis />
439
+ ) : (
440
+ <PaginationLink
441
+ href="#"
442
+ isActive={page === currentPage}
443
+ onClick={(e) => {
444
+ e.preventDefault();
445
+ handlePageClick(page);
446
+ }}
447
+ >
448
+ {page}
449
+ </PaginationLink>
450
+ )}
451
+ </PaginationItem>
452
+ ))}
453
+
454
+ <PaginationItem>
455
+ <PaginationNext
456
+ href="#"
457
+ onClick={(e) => {
458
+ e.preventDefault();
459
+ handleNext();
460
+ }}
461
+ className={
462
+ currentPage === totalPages
463
+ ? "pointer-events-none opacity-50"
464
+ : ""
465
+ }
466
+ />
467
+ </PaginationItem>
468
+ </PaginationContent>
469
+ </Pagination>
470
+ </div>
471
+ );
472
+ }
473
+ ```
474
+
475
+ ### Tamaños Personalizados
476
+
477
+ ```tsx
478
+ {
479
+ /* Small */
480
+ }
481
+ <Pagination>
482
+ <PaginationContent className="gap-0.5">
483
+ <PaginationItem>
484
+ <PaginationPrevious href="#" className="h-8 px-2 text-xs" />
485
+ </PaginationItem>
486
+ <PaginationItem>
487
+ <PaginationLink href="#" className="h-8 w-8 text-xs">
488
+ 1
489
+ </PaginationLink>
490
+ </PaginationItem>
491
+ <PaginationItem>
492
+ <PaginationLink href="#" isActive className="h-8 w-8 text-xs">
493
+ 2
494
+ </PaginationLink>
495
+ </PaginationItem>
496
+ <PaginationItem>
497
+ <PaginationNext href="#" className="h-8 px-2 text-xs" />
498
+ </PaginationItem>
499
+ </PaginationContent>
500
+ </Pagination>;
501
+
502
+ {
503
+ /* Large */
504
+ }
505
+ <Pagination>
506
+ <PaginationContent className="gap-2">
507
+ <PaginationItem>
508
+ <PaginationPrevious href="#" className="h-12 px-4 text-base" />
509
+ </PaginationItem>
510
+ <PaginationItem>
511
+ <PaginationLink href="#" className="h-12 w-12 text-base">
512
+ 1
513
+ </PaginationLink>
514
+ </PaginationItem>
515
+ <PaginationItem>
516
+ <PaginationLink href="#" isActive className="h-12 w-12 text-base">
517
+ 2
518
+ </PaginationLink>
519
+ </PaginationItem>
520
+ <PaginationItem>
521
+ <PaginationNext href="#" className="h-12 px-4 text-base" />
522
+ </PaginationItem>
523
+ </PaginationContent>
524
+ </Pagination>;
525
+ ```
526
+
527
+ ## Casos de Uso Comunes
528
+
529
+ **Tables**: Paginación de tablas con muchas filas
530
+ **Lists**: Listas largas de items (productos, usuarios, posts)
531
+ **Search results**: Resultados de búsqueda paginados
532
+ **Galleries**: Galerías de imágenes o archivos
533
+ **Admin panels**: Dashboards con data paginada
534
+ **API results**: Navegación de resultados de API con offset/limit
535
+
536
+ ## Estados y Data Attributes
537
+
538
+ ### PaginationLink
539
+
540
+ - **Active**: `isActive` → variant outline, `aria-current="page"`
541
+ - **Inactive**: variant ghost
542
+
543
+ ### Disabled (Previous/Next)
544
+
545
+ No hay prop disabled nativa, se simula con:
546
+
547
+ ```tsx
548
+ className = "pointer-events-none opacity-50";
549
+ ```
550
+
551
+ ## Estilos Base
552
+
553
+ ### Pagination (Root)
554
+
555
+ - **Layout**: `mx-auto flex w-full justify-center`
556
+ - **Semantic**: `<nav role="navigation" aria-label="pagination">`
557
+
558
+ ### PaginationContent
559
+
560
+ - **Layout**: `flex flex-row items-center gap-1`
561
+
562
+ ### PaginationLink
563
+
564
+ - **Size default**: `size="icon"` (h-9 w-9)
565
+ - **Variants**: ghost (inactive), outline (active)
566
+
567
+ ### PaginationPrevious/Next
568
+
569
+ - **Size**: `size="default"`
570
+ - **Padding**: `px-2.5`
571
+ - **Responsive**: Texto oculto en mobile (`hidden sm:block`)
572
+ - **Gap**: `gap-1` entre icono y texto
573
+
574
+ ### PaginationEllipsis
575
+
576
+ - **Size**: `size-9`
577
+ - **Icon**: more_horiz (Material Symbol) con `text-lg`
578
+ - **Layout**: `flex items-center justify-center`
579
+ - **ARIA**: `aria-hidden`
580
+
581
+ ## Responsive
582
+
583
+ - **Mobile**: Previous/Next solo muestran icono
584
+ - **Desktop (sm+)**: Previous/Next muestran icono + texto
585
+
586
+ ## Accesibilidad
587
+
588
+ - ✅ **Semantic**: `<nav role="navigation" aria-label="pagination">`
589
+ - ✅ **Current page**: `aria-current="page"` en página activa
590
+ - ✅ **Screen readers**: Ellipsis tiene `<span className="sr-only">More pages</span>`
591
+ - ✅ **Keyboard**: Navegación con Tab, activación con Enter/Space
592
+ - ✅ **Labels**: Previous/Next tienen `aria-label` descriptivo
593
+ - ⚠️ **Disabled**: Usa `pointer-events-none opacity-50` + previene click en handler
594
+
595
+ ## Notas de Implementación
596
+
597
+ - **Links**: PaginationLink es `<a>` no `<button>`, usa Button styles
598
+ - **Icons**: chevron_left, chevron_right, more_horiz (Material Symbols)
599
+ - **Responsive text**: Previous/Next usan `hidden sm:block` en texto
600
+ - **No state**: Componente stateless, maneja estado externamente
601
+ - **Ellipsis logic**: Representación negativa (-1, -2) común para identificar ellipsis
602
+ - **Disabled**: No hay prop disabled, usa className manual
603
+ - **Centering**: Root tiene `mx-auto justify-center`
604
+
605
+ ## Algoritmo de Generación de Páginas
606
+
607
+ Patrón común para mostrar páginas con ellipsis:
608
+
609
+ ```tsx
610
+ const generatePageNumbers = (current: number, total: number) => {
611
+ const pages = [];
612
+
613
+ // Siempre primera página
614
+ pages.push(1);
615
+
616
+ // Ellipsis izquierda si hay gap
617
+ if (current > 4) pages.push(-1);
618
+
619
+ // Páginas alrededor de current
620
+ const start = Math.max(2, current - 1);
621
+ const end = Math.min(total - 1, current + 1);
622
+ for (let i = start; i <= end; i++) {
623
+ if (i !== 1 && i !== total) pages.push(i);
624
+ }
625
+
626
+ // Ellipsis derecha si hay gap
627
+ if (current < total - 3) pages.push(-2);
628
+
629
+ // Siempre última página
630
+ if (total > 1) pages.push(total);
631
+
632
+ return pages;
633
+ };
634
+ ```
635
+
636
+ ## Troubleshooting
637
+
638
+ **Previous/Next no se deshabilitan**: No hay prop disabled, usa `className="pointer-events-none opacity-50"` y previene click en handler
639
+ **Active no se ve**: Verifica `isActive` prop y que solo una página lo tenga
640
+ **Texto Previous/Next no se ve en mobile**: Comportamiento esperado, usa `hidden sm:block`
641
+ **Ellipsis clickeable**: Ellipsis no debe ser clickeable, es `<span>` no link
642
+ **Spacing inconsistente**: PaginationContent tiene `gap-1`, ajusta con className
643
+ **No centrado**: Root tiene `justify-center`, override con `className="justify-start"` si necesario
644
+ **Links no funcionan**: PaginationLink usa `<a>`, asegúrate de pasar `href` o `onClick`
645
+ **Too many pages**: Usa ellipsis y lógica de generación de páginas para mostrar subset
646
+
647
+ ## Referencias
648
+
649
+ - **shadcn/ui Pagination**: <https://ui.shadcn.com/docs/components/pagination>
650
+ - **WAI-ARIA Pagination**: <https://www.w3.org/WAI/ARIA/apg/patterns/pagination/>