siesa-ui-kit 1.0.5 → 1.0.6
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/index.cjs +1479 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1479 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -14
- package/claude/agents/siesa-ui-kit-specialist.md +0 -2401
- package/claude/prompts/component-template.md +0 -121
- package/claude/settings.local.json +0 -61
- package/docs/border-radius.md +0 -1261
- package/docs/colors.md +0 -832
- package/docs/dark-mode-guide.md +0 -1426
- package/docs/filters.md +0 -1243
- package/docs/icons.md +0 -1283
- package/docs/shadows.md +0 -1377
- package/docs/spacing.md +0 -1684
- package/docs/typography.md +0 -1268
- package/postcss.config.cjs +0 -6
- package/src/App.css +0 -42
- package/src/App.tsx +0 -8
- package/src/ButtonTest.tsx +0 -147
- package/src/assets/fonts/README.md +0 -261
- package/src/assets/fonts/SiesaBT/SiesaBT-Bold.otf +0 -0
- package/src/assets/fonts/SiesaBT/SiesaBT-Light.otf +0 -0
- package/src/assets/fonts/SiesaBT/SiesaBT-Regular.otf +0 -0
- package/src/assets/react.svg +0 -1
- package/src/components/Alert/Alert.stories.tsx +0 -332
- package/src/components/Alert/Alert.tsx +0 -106
- package/src/components/Alert/Alert.types.ts +0 -54
- package/src/components/Avatar/Avatar.stories.tsx +0 -494
- package/src/components/Avatar/Avatar.tsx +0 -143
- package/src/components/Avatar/Avatar.types.ts +0 -53
- package/src/components/Badge/Badge.stories.tsx +0 -339
- package/src/components/Badge/Badge.tsx +0 -278
- package/src/components/Badge/Badge.types.ts +0 -58
- package/src/components/Button/Button.stories.tsx +0 -950
- package/src/components/Button/Button.tsx +0 -337
- package/src/components/Button/Button.types.ts +0 -180
- package/src/components/Button/icons.tsx +0 -87
- package/src/components/Button/index.ts +0 -3
- package/src/components/Checkbox/Checkbox.stories.tsx +0 -453
- package/src/components/Checkbox/Checkbox.tsx +0 -208
- package/src/components/Checkbox/Checkbox.types.ts +0 -61
- package/src/components/DescriptionList/DescriptionList.stories.tsx +0 -250
- package/src/components/DescriptionList/DescriptionList.tsx +0 -96
- package/src/components/DescriptionList/DescriptionList.types.ts +0 -29
- package/src/components/Divider/Divider.stories.tsx +0 -263
- package/src/components/Divider/Divider.tsx +0 -80
- package/src/components/Divider/Divider.types.ts +0 -24
- package/src/components/Dropdown/Dropdown.stories.tsx +0 -552
- package/src/components/Dropdown/Dropdown.tsx +0 -422
- package/src/components/Dropdown/Dropdown.types.ts +0 -146
- package/src/components/Dropdown/README.md +0 -266
- package/src/components/Dropdown/icons.tsx +0 -72
- package/src/components/Dropdown/index.ts +0 -8
- package/src/components/Input/Input.stories.tsx +0 -583
- package/src/components/Input/Input.tsx +0 -204
- package/src/components/Input/Input.types.ts +0 -80
- package/src/components/Input/icons.tsx +0 -145
- package/src/components/Input/index.ts +0 -2
- package/src/components/LoginView/LoginView.stories.tsx +0 -148
- package/src/components/LoginView/LoginView.tsx +0 -426
- package/src/components/LoginView/LoginView.types.ts +0 -52
- package/src/components/LoginView/README.md +0 -396
- package/src/components/LoginView/icons.tsx +0 -85
- package/src/components/LoginView/index.ts +0 -3
- package/src/components/Navbar/Navbar.stories.tsx +0 -810
- package/src/components/Navbar/Navbar.tsx +0 -755
- package/src/components/Navbar/Navbar.types.ts +0 -219
- package/src/components/Navbar/README.md +0 -279
- package/src/components/Navbar/icons.tsx +0 -102
- package/src/components/Navbar/index.ts +0 -8
- package/src/components/NavigationBar/NavigationBar.stories.tsx +0 -406
- package/src/components/NavigationBar/NavigationBar.tsx +0 -246
- package/src/components/NavigationBar/NavigationBar.types.ts +0 -74
- package/src/components/NavigationBar/README.md +0 -469
- package/src/components/NavigationBar/index.ts +0 -2
- package/src/components/NavigationRail/NavigationRail.stories.tsx +0 -417
- package/src/components/NavigationRail/NavigationRail.tsx +0 -418
- package/src/components/NavigationRail/NavigationRail.types.ts +0 -109
- package/src/components/NavigationRail/README.md +0 -224
- package/src/components/NavigationRail/index.ts +0 -2
- package/src/components/Notification/Notification.stories.tsx +0 -513
- package/src/components/Notification/Notification.tsx +0 -145
- package/src/components/Notification/Notification.types.ts +0 -142
- package/src/components/Notification/README.md +0 -409
- package/src/components/Notification/index.ts +0 -3
- package/src/components/POSConvention/POSConvention.stories.tsx +0 -235
- package/src/components/POSConvention/POSConvention.tsx +0 -129
- package/src/components/POSConvention/POSConvention.types.ts +0 -38
- package/src/components/POSConvention/README.md +0 -123
- package/src/components/POSConvention/icons.tsx +0 -45
- package/src/components/POSConvention/index.ts +0 -3
- package/src/components/POSLocationButton/POSLocationButton.stories.tsx +0 -531
- package/src/components/POSLocationButton/POSLocationButton.tsx +0 -247
- package/src/components/POSLocationButton/POSLocationButton.types.ts +0 -87
- package/src/components/POSLocationButton/README.md +0 -253
- package/src/components/POSLocationButton/icons.tsx +0 -120
- package/src/components/POSLocationButton/index.ts +0 -14
- package/src/components/POSNumberButton/POSNumberButton.stories.tsx +0 -415
- package/src/components/POSNumberButton/POSNumberButton.tsx +0 -179
- package/src/components/POSNumberButton/POSNumberButton.types.ts +0 -51
- package/src/components/POSNumberButton/README.md +0 -321
- package/src/components/POSNumberButton/index.ts +0 -3
- package/src/components/POSProductButton/POSProductButton.stories.tsx +0 -318
- package/src/components/POSProductButton/POSProductButton.tsx +0 -152
- package/src/components/POSProductButton/POSProductButton.types.ts +0 -46
- package/src/components/POSProductButton/README.md +0 -269
- package/src/components/POSProductButton/index.ts +0 -2
- package/src/components/POSProductCard/POSProductCard.stories.tsx +0 -642
- package/src/components/POSProductCard/POSProductCard.tsx +0 -208
- package/src/components/POSProductCard/POSProductCard.types.ts +0 -76
- package/src/components/POSProductCard/README.md +0 -179
- package/src/components/POSProductCard/icons.tsx +0 -26
- package/src/components/POSProductCard/index.ts +0 -2
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +0 -753
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +0 -332
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +0 -119
- package/src/components/POSProductSidebarItems/README.md +0 -198
- package/src/components/POSProductSidebarItems/icons.tsx +0 -21
- package/src/components/POSProductSidebarItems/index.ts +0 -3
- package/src/components/POSTable/POSTable.stories.tsx +0 -737
- package/src/components/POSTable/POSTable.tsx +0 -401
- package/src/components/POSTable/POSTable.types.ts +0 -83
- package/src/components/POSTable/README.md +0 -286
- package/src/components/POSTable/index.ts +0 -7
- package/src/components/Pagination/Pagination.stories.tsx +0 -555
- package/src/components/Pagination/Pagination.tsx +0 -286
- package/src/components/Pagination/Pagination.types.ts +0 -93
- package/src/components/Pagination/README.md +0 -298
- package/src/components/Pagination/icons.tsx +0 -47
- package/src/components/Pagination/index.ts +0 -3
- package/src/components/Quantity/Quantity.stories.tsx +0 -457
- package/src/components/Quantity/Quantity.tsx +0 -289
- package/src/components/Quantity/Quantity.types.ts +0 -70
- package/src/components/Radio/Radio.stories.tsx +0 -523
- package/src/components/Radio/Radio.tsx +0 -170
- package/src/components/Radio/Radio.types.ts +0 -122
- package/src/components/Select/README.md +0 -299
- package/src/components/Select/Select.stories.tsx +0 -673
- package/src/components/Select/Select.tsx +0 -454
- package/src/components/Select/Select.types.ts +0 -148
- package/src/components/Select/icons.tsx +0 -50
- package/src/components/Select/index.ts +0 -3
- package/src/components/SignUpView/SignUpView.stories.tsx +0 -129
- package/src/components/SignUpView/SignUpView.tsx +0 -503
- package/src/components/SignUpView/SignUpView.types.ts +0 -58
- package/src/components/SignUpView/icons.tsx +0 -71
- package/src/components/SignUpView/index.ts +0 -3
- package/src/components/Switch/README.md +0 -112
- package/src/components/Switch/Switch.stories.tsx +0 -550
- package/src/components/Switch/Switch.tsx +0 -246
- package/src/components/Switch/Switch.types.ts +0 -67
- package/src/components/Table/README.md +0 -369
- package/src/components/Table/Table.stories.tsx +0 -805
- package/src/components/Table/Table.tsx +0 -688
- package/src/components/Table/Table.types.ts +0 -204
- package/src/components/Table/index.ts +0 -9
- package/src/components/Tabs/README.md +0 -201
- package/src/components/Tabs/Tabs.stories.tsx +0 -580
- package/src/components/Tabs/Tabs.tsx +0 -356
- package/src/components/Tabs/Tabs.types.ts +0 -127
- package/src/components/Tabs/icons.tsx +0 -129
- package/src/components/Tabs/index.ts +0 -11
- package/src/components/Textarea/Textarea.stories.tsx +0 -535
- package/src/components/Textarea/Textarea.tsx +0 -188
- package/src/components/Textarea/Textarea.types.ts +0 -54
- package/src/context/ThemeContext.tsx +0 -99
- package/src/context/index.ts +0 -1
- package/src/index.css +0 -29
- package/src/index.ts +0 -39
- package/src/main.tsx +0 -10
- package/src/views/ProductsView/ProductsView.stories.tsx +0 -344
- package/src/views/ProductsView/ProductsView.tsx +0 -480
- package/src/views/ProductsView/ProductsView.types.ts +0 -238
- package/src/views/ProductsView/README.md +0 -312
- package/src/views/ProductsView/icons.tsx +0 -38
- package/src/views/ProductsView/index.ts +0 -8
- package/src/views/RecoverPasswordView/README.md +0 -269
- package/src/views/RecoverPasswordView/RecoverPasswordView.stories.tsx +0 -131
- package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +0 -376
- package/src/views/RecoverPasswordView/RecoverPasswordView.types.ts +0 -56
- package/src/views/RecoverPasswordView/icons.tsx +0 -17
- package/src/views/RecoverPasswordView/index.ts +0 -2
- package/src/views/TableLayoutView/README.md +0 -268
- package/src/views/TableLayoutView/TableLayoutView.stories.tsx +0 -235
- package/src/views/TableLayoutView/TableLayoutView.tsx +0 -461
- package/src/views/TableLayoutView/TableLayoutView.types.ts +0 -209
- package/src/views/TableLayoutView/icons.tsx +0 -113
- package/src/views/TableLayoutView/index.ts +0 -6
- package/storybook/main.ts +0 -20
- package/storybook/preview.tsx +0 -84
- package/storybook/vitest.setup.ts +0 -7
- package/tailwind.config.js +0 -128
- /package/{public → dist}/,Business Logo.png +0 -0
- /package/{public → dist}/.Siesa Logo.png +0 -0
- /package/{public → dist}/bg_siesa.png +0 -0
- /package/{public → dist}/siesa_logo_mobile.png +0 -0
- /package/{public → dist}/vite.svg +0 -0
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import type { PaginationProps, PageButtonProps } from './Pagination.types';
|
|
3
|
-
import { ArrowLeftIcon, ArrowRightIcon } from './icons';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Componente Pagination del sistema de diseño Siesa
|
|
7
|
-
* Basado en especificaciones de Figma con Tailwind CSS
|
|
8
|
-
*
|
|
9
|
-
* Mejores prácticas implementadas:
|
|
10
|
-
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
11
|
-
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
12
|
-
* - Tokens de color consistentes con la documentación
|
|
13
|
-
* - Type safety con TypeScript estricto
|
|
14
|
-
* - Accesibilidad con ARIA labels
|
|
15
|
-
* - Algoritmo de paginación inteligente
|
|
16
|
-
*
|
|
17
|
-
* @see docs/colors.md - Sistema de colores
|
|
18
|
-
* @see docs/typography.md - Sistema tipográfico (Label Small)
|
|
19
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* <Pagination
|
|
24
|
-
* currentPage={1}
|
|
25
|
-
* totalPages={66}
|
|
26
|
-
* onPageChange={(page) => console.log(page)}
|
|
27
|
-
* />
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const Pagination: React.FC<PaginationProps> = ({
|
|
31
|
-
currentPage = 1,
|
|
32
|
-
totalPages,
|
|
33
|
-
onPageChange,
|
|
34
|
-
backText = 'Atrás',
|
|
35
|
-
nextText = 'Siguiente',
|
|
36
|
-
siblingCount = 1,
|
|
37
|
-
showBackButton = true,
|
|
38
|
-
showNextButton = true,
|
|
39
|
-
className = '',
|
|
40
|
-
ariaLabel = 'Paginación',
|
|
41
|
-
}) => {
|
|
42
|
-
/**
|
|
43
|
-
* Genera el array de páginas a mostrar
|
|
44
|
-
* Algoritmo: [1] ... [currentPage-1] [currentPage] [currentPage+1] ... [totalPages]
|
|
45
|
-
*/
|
|
46
|
-
const paginationRange = useMemo(() => {
|
|
47
|
-
const totalNumbers = siblingCount * 2 + 3; // Página actual + siblings + primera + última
|
|
48
|
-
const totalBlocks = totalNumbers + 2; // + 2 separadores "..."
|
|
49
|
-
|
|
50
|
-
// Si el total de páginas es menor que el total de bloques, mostrar todas
|
|
51
|
-
if (totalPages <= totalBlocks) {
|
|
52
|
-
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
56
|
-
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
57
|
-
|
|
58
|
-
const shouldShowLeftDots = leftSiblingIndex > 2;
|
|
59
|
-
const shouldShowRightDots = rightSiblingIndex < totalPages - 1;
|
|
60
|
-
|
|
61
|
-
const firstPageIndex = 1;
|
|
62
|
-
const lastPageIndex = totalPages;
|
|
63
|
-
|
|
64
|
-
// No mostrar dots a la izquierda
|
|
65
|
-
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
66
|
-
const leftItemCount = 3 + 2 * siblingCount;
|
|
67
|
-
const leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
|
|
68
|
-
return [...leftRange, '...', totalPages];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// No mostrar dots a la derecha
|
|
72
|
-
if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
73
|
-
const rightItemCount = 3 + 2 * siblingCount;
|
|
74
|
-
const rightRange = Array.from(
|
|
75
|
-
{ length: rightItemCount },
|
|
76
|
-
(_, i) => totalPages - rightItemCount + i + 1
|
|
77
|
-
);
|
|
78
|
-
return [firstPageIndex, '...', ...rightRange];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Mostrar ambos dots
|
|
82
|
-
if (shouldShowLeftDots && shouldShowRightDots) {
|
|
83
|
-
const middleRange = Array.from(
|
|
84
|
-
{ length: rightSiblingIndex - leftSiblingIndex + 1 },
|
|
85
|
-
(_, i) => leftSiblingIndex + i
|
|
86
|
-
);
|
|
87
|
-
return [firstPageIndex, '...', ...middleRange, '...', lastPageIndex];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return [];
|
|
91
|
-
}, [currentPage, totalPages, siblingCount]);
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Handler para cambio de página
|
|
95
|
-
*/
|
|
96
|
-
const handlePageChange = (page: number) => {
|
|
97
|
-
if (page >= 1 && page <= totalPages && page !== currentPage) {
|
|
98
|
-
onPageChange?.(page);
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Componente interno: Botón de página
|
|
104
|
-
*/
|
|
105
|
-
const PageButton: React.FC<PageButtonProps> = ({
|
|
106
|
-
page,
|
|
107
|
-
isActive = false,
|
|
108
|
-
disabled = false,
|
|
109
|
-
onClick,
|
|
110
|
-
ariaLabel: buttonAriaLabel,
|
|
111
|
-
}) => {
|
|
112
|
-
const isDots = page === '...';
|
|
113
|
-
|
|
114
|
-
// ===== CLASES BASE =====
|
|
115
|
-
const baseClasses = `
|
|
116
|
-
inline-flex
|
|
117
|
-
items-center
|
|
118
|
-
justify-center
|
|
119
|
-
gap-3
|
|
120
|
-
px-3
|
|
121
|
-
py-2
|
|
122
|
-
rounded-md
|
|
123
|
-
font-bold
|
|
124
|
-
text-sm
|
|
125
|
-
whitespace-nowrap
|
|
126
|
-
transition-all
|
|
127
|
-
duration-150
|
|
128
|
-
`;
|
|
129
|
-
|
|
130
|
-
// ===== CLASES DE ESTADO =====
|
|
131
|
-
const stateClasses = isDots
|
|
132
|
-
? `
|
|
133
|
-
text-primary-custom-600
|
|
134
|
-
dark:text-primary-custom-600
|
|
135
|
-
cursor-default
|
|
136
|
-
pointer-events-none
|
|
137
|
-
`
|
|
138
|
-
: isActive
|
|
139
|
-
? `
|
|
140
|
-
bg-primary-custom-100
|
|
141
|
-
text-primary-custom-600
|
|
142
|
-
dark:bg-primary-custom-100
|
|
143
|
-
dark:text-primary-custom-600
|
|
144
|
-
`
|
|
145
|
-
: `
|
|
146
|
-
text-primary-custom-600
|
|
147
|
-
hover:bg-primary-custom-100
|
|
148
|
-
active:scale-95
|
|
149
|
-
dark:text-primary-custom-600
|
|
150
|
-
dark:hover:bg-primary-custom-100
|
|
151
|
-
dark:active:scale-95
|
|
152
|
-
`;
|
|
153
|
-
|
|
154
|
-
// ===== CLASES DE DISABLED =====
|
|
155
|
-
const disabledClasses = disabled
|
|
156
|
-
? `
|
|
157
|
-
opacity-50
|
|
158
|
-
cursor-not-allowed
|
|
159
|
-
pointer-events-none
|
|
160
|
-
`
|
|
161
|
-
: '';
|
|
162
|
-
|
|
163
|
-
// ===== COMBINAR CLASES =====
|
|
164
|
-
const buttonClasses = [baseClasses, stateClasses, disabledClasses]
|
|
165
|
-
.join(' ')
|
|
166
|
-
.replace(/\s+/g, ' ')
|
|
167
|
-
.trim();
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<button
|
|
171
|
-
type="button"
|
|
172
|
-
className={buttonClasses}
|
|
173
|
-
onClick={onClick}
|
|
174
|
-
disabled={disabled || isDots}
|
|
175
|
-
aria-label={buttonAriaLabel}
|
|
176
|
-
aria-current={isActive ? 'page' : undefined}
|
|
177
|
-
>
|
|
178
|
-
{page}
|
|
179
|
-
</button>
|
|
180
|
-
);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// ===== CLASES DEL CONTAINER =====
|
|
184
|
-
const containerClasses = `
|
|
185
|
-
inline-flex
|
|
186
|
-
items-center
|
|
187
|
-
justify-between
|
|
188
|
-
gap-0
|
|
189
|
-
${className}
|
|
190
|
-
`
|
|
191
|
-
.replace(/\s+/g, ' ')
|
|
192
|
-
.trim();
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<nav className={containerClasses} aria-label={ariaLabel} role="navigation">
|
|
196
|
-
{/* ===== BOTÓN BACK ===== */}
|
|
197
|
-
{showBackButton && (
|
|
198
|
-
<button
|
|
199
|
-
type="button"
|
|
200
|
-
className={`
|
|
201
|
-
inline-flex
|
|
202
|
-
items-center
|
|
203
|
-
justify-center
|
|
204
|
-
gap-3
|
|
205
|
-
px-3
|
|
206
|
-
py-2
|
|
207
|
-
rounded-md
|
|
208
|
-
font-bold
|
|
209
|
-
text-sm
|
|
210
|
-
text-primary-custom-600
|
|
211
|
-
hover:bg-primary-custom-100
|
|
212
|
-
active:scale-95
|
|
213
|
-
dark:text-primary-custom-600
|
|
214
|
-
dark:hover:bg-primary-custom-100
|
|
215
|
-
dark:active:scale-95
|
|
216
|
-
transition-all
|
|
217
|
-
duration-150
|
|
218
|
-
${currentPage === 1 ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''}
|
|
219
|
-
`
|
|
220
|
-
.replace(/\s+/g, ' ')
|
|
221
|
-
.trim()}
|
|
222
|
-
onClick={() => handlePageChange(currentPage - 1)}
|
|
223
|
-
disabled={currentPage === 1}
|
|
224
|
-
aria-label={`${backText} page`}
|
|
225
|
-
>
|
|
226
|
-
<ArrowLeftIcon className="w-4 h-4" />
|
|
227
|
-
{backText}
|
|
228
|
-
</button>
|
|
229
|
-
)}
|
|
230
|
-
|
|
231
|
-
{/* ===== NÚMEROS DE PÁGINA ===== */}
|
|
232
|
-
<div className="inline-flex items-center">
|
|
233
|
-
{paginationRange.map((page, index) => (
|
|
234
|
-
<PageButton
|
|
235
|
-
key={`${page}-${index}`}
|
|
236
|
-
page={page}
|
|
237
|
-
isActive={page === currentPage}
|
|
238
|
-
onClick={() => typeof page === 'number' && handlePageChange(page)}
|
|
239
|
-
ariaLabel={
|
|
240
|
-
typeof page === 'number' ? `Go to page ${page}` : `Page ${page}`
|
|
241
|
-
}
|
|
242
|
-
/>
|
|
243
|
-
))}
|
|
244
|
-
</div>
|
|
245
|
-
|
|
246
|
-
{/* ===== BOTÓN NEXT ===== */}
|
|
247
|
-
{showNextButton && (
|
|
248
|
-
<button
|
|
249
|
-
type="button"
|
|
250
|
-
className={`
|
|
251
|
-
inline-flex
|
|
252
|
-
items-center
|
|
253
|
-
justify-center
|
|
254
|
-
gap-3
|
|
255
|
-
px-3
|
|
256
|
-
py-2
|
|
257
|
-
rounded-md
|
|
258
|
-
font-bold
|
|
259
|
-
text-sm
|
|
260
|
-
text-primary-custom-600
|
|
261
|
-
hover:bg-primary-custom-100
|
|
262
|
-
active:scale-95
|
|
263
|
-
dark:text-primary-custom-600
|
|
264
|
-
dark:hover:bg-primary-custom-100
|
|
265
|
-
dark:active:scale-95
|
|
266
|
-
transition-all
|
|
267
|
-
duration-150
|
|
268
|
-
${
|
|
269
|
-
currentPage === totalPages
|
|
270
|
-
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
|
271
|
-
: ''
|
|
272
|
-
}
|
|
273
|
-
`
|
|
274
|
-
.replace(/\s+/g, ' ')
|
|
275
|
-
.trim()}
|
|
276
|
-
onClick={() => handlePageChange(currentPage + 1)}
|
|
277
|
-
disabled={currentPage === totalPages}
|
|
278
|
-
aria-label={`${nextText} page`}
|
|
279
|
-
>
|
|
280
|
-
{nextText}
|
|
281
|
-
<ArrowRightIcon className="w-4 h-4" />
|
|
282
|
-
</button>
|
|
283
|
-
)}
|
|
284
|
-
</nav>
|
|
285
|
-
);
|
|
286
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { MouseEvent } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Props del componente Pagination
|
|
5
|
-
*/
|
|
6
|
-
export interface PaginationProps {
|
|
7
|
-
/**
|
|
8
|
-
* Página actual (basada en 1)
|
|
9
|
-
* @default 1
|
|
10
|
-
*/
|
|
11
|
-
currentPage?: number;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Número total de páginas
|
|
15
|
-
* @default 1
|
|
16
|
-
*/
|
|
17
|
-
totalPages: number;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Callback cuando cambia la página
|
|
21
|
-
*/
|
|
22
|
-
onPageChange?: (page: number) => void;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Texto del botón "Atrás"
|
|
26
|
-
* @default 'Atrás'
|
|
27
|
-
*/
|
|
28
|
-
backText?: string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Texto del botón "Siguiente"
|
|
32
|
-
* @default 'Siguiente'
|
|
33
|
-
*/
|
|
34
|
-
nextText?: string;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Número de páginas visibles alrededor de la página actual
|
|
38
|
-
* @default 1
|
|
39
|
-
*/
|
|
40
|
-
siblingCount?: number;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Si se debe mostrar el botón "Atrás"
|
|
44
|
-
* @default true
|
|
45
|
-
*/
|
|
46
|
-
showBackButton?: boolean;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Si se debe mostrar el botón "Siguiente"
|
|
50
|
-
* @default true
|
|
51
|
-
*/
|
|
52
|
-
showNextButton?: boolean;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Clases CSS adicionales
|
|
56
|
-
*/
|
|
57
|
-
className?: string;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Label para accesibilidad (ARIA)
|
|
61
|
-
*/
|
|
62
|
-
ariaLabel?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Props internas para el botón de página
|
|
67
|
-
*/
|
|
68
|
-
export interface PageButtonProps {
|
|
69
|
-
/**
|
|
70
|
-
* Número de página o "..."
|
|
71
|
-
*/
|
|
72
|
-
page: number | string;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Si es la página activa
|
|
76
|
-
*/
|
|
77
|
-
isActive?: boolean;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Si el botón está deshabilitado
|
|
81
|
-
*/
|
|
82
|
-
disabled?: boolean;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Handler para click
|
|
86
|
-
*/
|
|
87
|
-
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Label para accesibilidad
|
|
91
|
-
*/
|
|
92
|
-
ariaLabel?: string;
|
|
93
|
-
}
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
# Pagination Component
|
|
2
|
-
|
|
3
|
-
Componente de paginación del sistema de diseño **Siesa UI Kit**, implementado pixel-perfect según especificaciones de Figma con soporte completo de dark mode.
|
|
4
|
-
|
|
5
|
-
## 📋 Características
|
|
6
|
-
|
|
7
|
-
- ✅ **Soporte completo de dark mode** - Tokens adaptativos para light/dark
|
|
8
|
-
- ✅ **Algoritmo inteligente** - Muestra páginas relevantes con separadores "..."
|
|
9
|
-
- ✅ **TypeScript estricto** - Types completos para máxima seguridad
|
|
10
|
-
- ✅ **Accesibilidad** - ARIA labels y navegación por teclado
|
|
11
|
-
- ✅ **Personalizable** - Textos, siblingCount, botones opcionales
|
|
12
|
-
- ✅ **Pixel-perfect** - Implementado exactamente según Figma
|
|
13
|
-
- ✅ **Transiciones suaves** - Animaciones en hover y active
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 🎨 Especificaciones de Diseño
|
|
18
|
-
|
|
19
|
-
### Tokens Utilizados
|
|
20
|
-
|
|
21
|
-
| Propiedad | Token | Valor Light | Valor Dark |
|
|
22
|
-
|-----------|-------|-------------|------------|
|
|
23
|
-
| **Texto** | `text-primary-custom-600` | `#0e79fd` | `#0e79fd` |
|
|
24
|
-
| **Hover BG** | `hover:bg-primary-custom-100` | `#dbeefe` | `#dbeefe` |
|
|
25
|
-
| **Active BG** | `bg-primary-custom-100` | `#dbeefe` | `#dbeefe` |
|
|
26
|
-
| **Font** | `text-sm font-bold` | 14px Bold | 14px Bold |
|
|
27
|
-
| **Padding** | `px-3 py-2` | 12px 8px | 12px 8px |
|
|
28
|
-
| **Border Radius** | `rounded-md` | 6px | 6px |
|
|
29
|
-
|
|
30
|
-
### Estados
|
|
31
|
-
|
|
32
|
-
- **Default**: Transparente, texto azul
|
|
33
|
-
- **Hover**: Fondo azul claro (`primary-custom-100`)
|
|
34
|
-
- **Active**: Fondo azul claro (página actual)
|
|
35
|
-
- **Disabled**: Opacidad 50% (Back/Next en primera/última página)
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## 📦 Uso Básico
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
import { Pagination } from '@/components/Pagination';
|
|
43
|
-
import { useState } from 'react';
|
|
44
|
-
|
|
45
|
-
function MyComponent() {
|
|
46
|
-
const [currentPage, setCurrentPage] = useState(1);
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<Pagination
|
|
50
|
-
currentPage={currentPage}
|
|
51
|
-
totalPages={66}
|
|
52
|
-
onPageChange={(page) => setCurrentPage(page)}
|
|
53
|
-
/>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## 🔧 Props
|
|
61
|
-
|
|
62
|
-
### PaginationProps
|
|
63
|
-
|
|
64
|
-
| Prop | Tipo | Default | Descripción |
|
|
65
|
-
|------|------|---------|-------------|
|
|
66
|
-
| `currentPage` | `number` | `1` | Página actual (basada en 1) |
|
|
67
|
-
| `totalPages` | `number` | **Requerido** | Número total de páginas |
|
|
68
|
-
| `onPageChange` | `(page: number) => void` | - | Callback cuando cambia la página |
|
|
69
|
-
| `backText` | `string` | `'Back'` | Texto del botón "Back" |
|
|
70
|
-
| `nextText` | `string` | `'Next'` | Texto del botón "Next" |
|
|
71
|
-
| `siblingCount` | `number` | `1` | Páginas visibles alrededor de la actual |
|
|
72
|
-
| `showBackButton` | `boolean` | `true` | Si se muestra el botón "Back" |
|
|
73
|
-
| `showNextButton` | `boolean` | `true` | Si se muestra el botón "Next" |
|
|
74
|
-
| `className` | `string` | `''` | Clases CSS adicionales |
|
|
75
|
-
| `ariaLabel` | `string` | `'Pagination'` | Label para accesibilidad |
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 📖 Ejemplos
|
|
80
|
-
|
|
81
|
-
### 1. Paginación Básica
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
<Pagination
|
|
85
|
-
currentPage={1}
|
|
86
|
-
totalPages={10}
|
|
87
|
-
onPageChange={(page) => console.log(page)}
|
|
88
|
-
/>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 2. Con Textos Personalizados (Español)
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
<Pagination
|
|
95
|
-
currentPage={5}
|
|
96
|
-
totalPages={66}
|
|
97
|
-
backText="Anterior"
|
|
98
|
-
nextText="Siguiente"
|
|
99
|
-
onPageChange={(page) => console.log(page)}
|
|
100
|
-
/>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 3. Más Páginas Visibles (siblingCount)
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
// siblingCount = 1 (default): [1] ... [4] [5] [6] ... [66]
|
|
107
|
-
<Pagination currentPage={5} totalPages={66} siblingCount={1} />
|
|
108
|
-
|
|
109
|
-
// siblingCount = 2: [1] ... [3] [4] [5] [6] [7] ... [66]
|
|
110
|
-
<Pagination currentPage={5} totalPages={66} siblingCount={2} />
|
|
111
|
-
|
|
112
|
-
// siblingCount = 0: [1] ... [5] ... [66]
|
|
113
|
-
<Pagination currentPage={5} totalPages={66} siblingCount={0} />
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### 4. Solo Números (Sin Back/Next)
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
<Pagination
|
|
120
|
-
currentPage={5}
|
|
121
|
-
totalPages={66}
|
|
122
|
-
showBackButton={false}
|
|
123
|
-
showNextButton={false}
|
|
124
|
-
/>
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### 5. Con Tabla
|
|
128
|
-
|
|
129
|
-
```tsx
|
|
130
|
-
function TableWithPagination() {
|
|
131
|
-
const [currentPage, setCurrentPage] = useState(1);
|
|
132
|
-
const itemsPerPage = 10;
|
|
133
|
-
const totalItems = 156;
|
|
134
|
-
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
|
135
|
-
|
|
136
|
-
const startItem = (currentPage - 1) * itemsPerPage + 1;
|
|
137
|
-
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<div>
|
|
141
|
-
{/* Tu tabla aquí */}
|
|
142
|
-
<table>{/* ... */}</table>
|
|
143
|
-
|
|
144
|
-
{/* Footer con info y paginación */}
|
|
145
|
-
<div className="flex items-center justify-between mt-4">
|
|
146
|
-
<p className="text-xs text-gray-500">
|
|
147
|
-
Mostrando {startItem} a {endItem} de {totalItems} resultados
|
|
148
|
-
</p>
|
|
149
|
-
<Pagination
|
|
150
|
-
currentPage={currentPage}
|
|
151
|
-
totalPages={totalPages}
|
|
152
|
-
onPageChange={setCurrentPage}
|
|
153
|
-
/>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## 🧠 Algoritmo de Paginación
|
|
163
|
-
|
|
164
|
-
El componente usa un algoritmo inteligente para mostrar solo las páginas relevantes:
|
|
165
|
-
|
|
166
|
-
### Pocas páginas (≤ 7)
|
|
167
|
-
```
|
|
168
|
-
[1] [2] [3] [4] [5]
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Cerca del inicio
|
|
172
|
-
```
|
|
173
|
-
[1] [2] [3] [4] [5] ... [66]
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Cerca del final
|
|
177
|
-
```
|
|
178
|
-
[1] ... [62] [63] [64] [65] [66]
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### En el medio
|
|
182
|
-
```
|
|
183
|
-
[1] ... [9] [10] [11] ... [66]
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Reglas:**
|
|
187
|
-
- Siempre muestra la **primera** y **última** página
|
|
188
|
-
- Muestra la **página actual** y sus **siblings** (1 a cada lado por default)
|
|
189
|
-
- Usa "**...**" para indicar páginas ocultas
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## ♿ Accesibilidad
|
|
194
|
-
|
|
195
|
-
- ✅ Navegación por teclado (Tab, Enter, Space)
|
|
196
|
-
- ✅ ARIA labels descriptivos (`aria-label`, `aria-current`)
|
|
197
|
-
- ✅ Estados disabled claros
|
|
198
|
-
- ✅ Focus rings visibles
|
|
199
|
-
- ✅ Semantic HTML (`<nav>`, `<button>`)
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
<Pagination
|
|
203
|
-
currentPage={5}
|
|
204
|
-
totalPages={66}
|
|
205
|
-
ariaLabel="Paginación de resultados"
|
|
206
|
-
/>
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## 🌓 Dark Mode
|
|
212
|
-
|
|
213
|
-
El componente se adapta automáticamente al dark mode usando el modificador `dark:`:
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
// Los colores se adaptan automáticamente
|
|
217
|
-
<Pagination currentPage={1} totalPages={66} />
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
**Tokens usados:**
|
|
221
|
-
- Texto: `text-primary-custom-600` (mismo en ambos modos)
|
|
222
|
-
- Hover: `hover:bg-primary-custom-100` / `dark:hover:bg-primary-custom-100`
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## 🎯 Mejores Prácticas
|
|
227
|
-
|
|
228
|
-
### ✅ Hacer
|
|
229
|
-
|
|
230
|
-
```tsx
|
|
231
|
-
// ✅ currentPage basado en 1
|
|
232
|
-
<Pagination currentPage={1} totalPages={10} />
|
|
233
|
-
|
|
234
|
-
// ✅ Mostrar información de contexto
|
|
235
|
-
<div>
|
|
236
|
-
<p>Mostrando 1 a 10 de 156 resultados</p>
|
|
237
|
-
<Pagination currentPage={1} totalPages={16} />
|
|
238
|
-
</div>
|
|
239
|
-
|
|
240
|
-
// ✅ siblingCount responsive
|
|
241
|
-
<Pagination
|
|
242
|
-
currentPage={5}
|
|
243
|
-
totalPages={66}
|
|
244
|
-
siblingCount={window.innerWidth < 768 ? 0 : 1}
|
|
245
|
-
/>
|
|
246
|
-
|
|
247
|
-
// ✅ Implementar onPageChange
|
|
248
|
-
<Pagination
|
|
249
|
-
currentPage={currentPage}
|
|
250
|
-
totalPages={10}
|
|
251
|
-
onPageChange={(page) => {
|
|
252
|
-
setCurrentPage(page);
|
|
253
|
-
fetchData(page);
|
|
254
|
-
window.scrollTo(0, 0); // Scroll to top
|
|
255
|
-
}}
|
|
256
|
-
/>
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### ❌ Evitar
|
|
260
|
-
|
|
261
|
-
```tsx
|
|
262
|
-
// ❌ currentPage basado en 0
|
|
263
|
-
<Pagination currentPage={0} totalPages={10} />
|
|
264
|
-
|
|
265
|
-
// ❌ siblingCount muy alto
|
|
266
|
-
<Pagination currentPage={5} totalPages={66} siblingCount={10} />
|
|
267
|
-
|
|
268
|
-
// ❌ Sin onPageChange
|
|
269
|
-
<Pagination currentPage={1} totalPages={10} />
|
|
270
|
-
// El componente no cambiará páginas solo
|
|
271
|
-
|
|
272
|
-
// ❌ Paginación para muy pocos items
|
|
273
|
-
<Pagination currentPage={1} totalPages={2} />
|
|
274
|
-
// Considera mostrar todo en lugar de paginar
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
## 📚 Referencias
|
|
280
|
-
|
|
281
|
-
- **Figma**: [Pagination Component](https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4168-16023)
|
|
282
|
-
- **Código**: `src/components/Pagination/Pagination.tsx`
|
|
283
|
-
- **Types**: `src/components/Pagination/Pagination.types.ts`
|
|
284
|
-
- **Stories**: `src/components/Pagination/Pagination.stories.tsx`
|
|
285
|
-
- **Docs**: `docs/colors.md`, `docs/typography.md`, `docs/spacing.md`
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## 🔄 Historial de Cambios
|
|
290
|
-
|
|
291
|
-
| Versión | Fecha | Cambios |
|
|
292
|
-
|---------|-------|---------|
|
|
293
|
-
| 1.0.0 | 2025-11-11 | Implementación inicial con dark mode completo |
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
**Última actualización**: 2025-11-11
|
|
298
|
-
**Mantenido por**: Equipo Siesa UI Kit
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Icono de flecha izquierda (ChevronLeft)
|
|
5
|
-
* Usado en el botón "Back"
|
|
6
|
-
* Heroicons Micro (16x16px)
|
|
7
|
-
*/
|
|
8
|
-
export const ArrowLeftIcon: React.FC<{ className?: string }> = ({ className = '' }) => {
|
|
9
|
-
return (
|
|
10
|
-
<svg
|
|
11
|
-
className={className}
|
|
12
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
-
viewBox="0 0 16 16"
|
|
14
|
-
fill="currentColor"
|
|
15
|
-
aria-hidden="true"
|
|
16
|
-
>
|
|
17
|
-
<path
|
|
18
|
-
fillRule="evenodd"
|
|
19
|
-
d="M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z"
|
|
20
|
-
clipRule="evenodd"
|
|
21
|
-
/>
|
|
22
|
-
</svg>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Icono de flecha derecha (ChevronRight)
|
|
28
|
-
* Usado en el botón "Next"
|
|
29
|
-
* Heroicons Micro (16x16px)
|
|
30
|
-
*/
|
|
31
|
-
export const ArrowRightIcon: React.FC<{ className?: string }> = ({ className = '' }) => {
|
|
32
|
-
return (
|
|
33
|
-
<svg
|
|
34
|
-
className={className}
|
|
35
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
-
viewBox="0 0 16 16"
|
|
37
|
-
fill="currentColor"
|
|
38
|
-
aria-hidden="true"
|
|
39
|
-
>
|
|
40
|
-
<path
|
|
41
|
-
fillRule="evenodd"
|
|
42
|
-
d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z"
|
|
43
|
-
clipRule="evenodd"
|
|
44
|
-
/>
|
|
45
|
-
</svg>
|
|
46
|
-
);
|
|
47
|
-
};
|