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,356 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import type { TabProps, TabsProps } from './Tabs.types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Componente Tab individual del sistema de diseño Siesa
|
|
6
|
-
*
|
|
7
|
-
* Representa un único tab dentro de un grupo de tabs. Incluye soporte para:
|
|
8
|
-
* - Estado activo/inactivo con indicador visual (línea inferior)
|
|
9
|
-
* - Icono opcional a la izquierda del texto
|
|
10
|
-
* - Badge de notificación con contador
|
|
11
|
-
* - Estados hover, focus y disabled
|
|
12
|
-
* - Dark mode completo
|
|
13
|
-
*
|
|
14
|
-
* Mejores prácticas implementadas:
|
|
15
|
-
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
16
|
-
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
17
|
-
* - Tokens de color consistentes con la documentación
|
|
18
|
-
* - Type safety con TypeScript estricto
|
|
19
|
-
* - Accesibilidad con ARIA labels
|
|
20
|
-
*
|
|
21
|
-
* @see docs/colors.md - Sistema de colores
|
|
22
|
-
* @see docs/typography.md - Sistema tipográfico
|
|
23
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```tsx
|
|
27
|
-
* <Tab
|
|
28
|
-
* label="Inicio"
|
|
29
|
-
* active={true}
|
|
30
|
-
* icon={<HomeIcon />}
|
|
31
|
-
* badge={3}
|
|
32
|
-
* onClick={() => setActiveTab('home')}
|
|
33
|
-
* />
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export const Tab: React.FC<TabProps> = ({
|
|
37
|
-
label,
|
|
38
|
-
active = false,
|
|
39
|
-
icon,
|
|
40
|
-
badge,
|
|
41
|
-
disabled = false,
|
|
42
|
-
onClick,
|
|
43
|
-
className = '',
|
|
44
|
-
ariaLabel,
|
|
45
|
-
}) => {
|
|
46
|
-
// ===== CLASES BASE DEL BOTÓN =====
|
|
47
|
-
// El contenedor es relativo para posicionar el indicador absoluto
|
|
48
|
-
const baseClasses = `
|
|
49
|
-
relative
|
|
50
|
-
flex
|
|
51
|
-
flex-col
|
|
52
|
-
items-center
|
|
53
|
-
cursor-pointer
|
|
54
|
-
outline-none
|
|
55
|
-
transition-all
|
|
56
|
-
duration-150
|
|
57
|
-
p-0
|
|
58
|
-
border-0
|
|
59
|
-
bg-transparent
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
// ===== CLASES DE FOCUS =====
|
|
63
|
-
// El focus outline se desactiva aquí, el shadow se aplica al Content div
|
|
64
|
-
const focusClasses = '';
|
|
65
|
-
|
|
66
|
-
// ===== CLASES DE DISABLED =====
|
|
67
|
-
const disabledClasses = disabled
|
|
68
|
-
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
|
69
|
-
: '';
|
|
70
|
-
|
|
71
|
-
// ===== CLASES DEL CONTENIDO INTERIOR =====
|
|
72
|
-
// El hover y focus background se aplica aquí
|
|
73
|
-
const contentBaseClasses = `
|
|
74
|
-
flex
|
|
75
|
-
items-center
|
|
76
|
-
justify-center
|
|
77
|
-
gap-1
|
|
78
|
-
p-2
|
|
79
|
-
rounded-lg
|
|
80
|
-
overflow-hidden
|
|
81
|
-
transition-all
|
|
82
|
-
duration-150
|
|
83
|
-
w-full
|
|
84
|
-
`;
|
|
85
|
-
|
|
86
|
-
// ===== CLASES DE ESTADO HOVER Y FOCUS PARA CONTENIDO =====
|
|
87
|
-
// Light: bg-[rgba(0,0,0,0.03)], Dark: bg-white/5
|
|
88
|
-
// Según Figma, tanto hover como focus tienen el background overlay
|
|
89
|
-
// El shadow de focus también se aplica aquí (al Content div), no al botón
|
|
90
|
-
const contentStateClasses = disabled
|
|
91
|
-
? ''
|
|
92
|
-
: `
|
|
93
|
-
group-hover:bg-[rgba(0,0,0,0.03)]
|
|
94
|
-
group-focus-visible:bg-[rgba(0,0,0,0.03)]
|
|
95
|
-
group-focus-visible:shadow-[0px_0px_0px_2px_#dbeefe,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#60b6fa]
|
|
96
|
-
dark:group-hover:bg-white/5
|
|
97
|
-
dark:group-focus-visible:bg-white/5
|
|
98
|
-
dark:group-focus-visible:shadow-[0px_0px_0px_2px_#1e3a5f,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#0f6ae3]
|
|
99
|
-
`;
|
|
100
|
-
|
|
101
|
-
// ===== CLASES DE TEXTO =====
|
|
102
|
-
// Activo: primary-custom-600 (#0e79fd), Inactivo: content-primary (#18181b)
|
|
103
|
-
const textClasses = active
|
|
104
|
-
? `
|
|
105
|
-
text-primary-custom-600
|
|
106
|
-
dark:text-primary-custom-600
|
|
107
|
-
`
|
|
108
|
-
: `
|
|
109
|
-
text-content-primary
|
|
110
|
-
dark:text-dark-content-primary
|
|
111
|
-
`;
|
|
112
|
-
|
|
113
|
-
// ===== CLASES DE ICONO =====
|
|
114
|
-
// El icono hereda el color del texto
|
|
115
|
-
const iconClasses = `
|
|
116
|
-
w-3
|
|
117
|
-
h-3
|
|
118
|
-
flex-shrink-0
|
|
119
|
-
`;
|
|
120
|
-
|
|
121
|
-
// ===== CLASES DEL BADGE =====
|
|
122
|
-
// Activo: bg-primary-custom-600 (#0e79fd), Inactivo: bg-content-primary (#18181b)
|
|
123
|
-
const badgeClasses = active
|
|
124
|
-
? `
|
|
125
|
-
bg-primary-custom-600
|
|
126
|
-
dark:bg-primary-custom-600
|
|
127
|
-
`
|
|
128
|
-
: `
|
|
129
|
-
bg-content-primary
|
|
130
|
-
dark:bg-dark-content-primary
|
|
131
|
-
`;
|
|
132
|
-
|
|
133
|
-
// ===== CLASES DEL INDICADOR INFERIOR =====
|
|
134
|
-
// Solo visible cuando está activo, 2px de alto, rounded-full
|
|
135
|
-
// Posicionado al fondo del contenedor de Tabs (alineado con el border)
|
|
136
|
-
const indicatorClasses = active
|
|
137
|
-
? `
|
|
138
|
-
absolute
|
|
139
|
-
-bottom-2.5
|
|
140
|
-
left-0
|
|
141
|
-
right-0
|
|
142
|
-
h-0.5
|
|
143
|
-
rounded-full
|
|
144
|
-
bg-primary-custom-600
|
|
145
|
-
dark:bg-primary-custom-600
|
|
146
|
-
z-10
|
|
147
|
-
`
|
|
148
|
-
: 'hidden';
|
|
149
|
-
|
|
150
|
-
// ===== COMBINAR CLASES DEL BOTÓN =====
|
|
151
|
-
const finalClasses = [
|
|
152
|
-
baseClasses,
|
|
153
|
-
focusClasses,
|
|
154
|
-
disabledClasses,
|
|
155
|
-
'group', // Para usar group-hover y group-focus-visible
|
|
156
|
-
className,
|
|
157
|
-
]
|
|
158
|
-
.join(' ')
|
|
159
|
-
.replace(/\s+/g, ' ')
|
|
160
|
-
.trim();
|
|
161
|
-
|
|
162
|
-
// ===== COMBINAR CLASES DEL CONTENIDO =====
|
|
163
|
-
const contentFinalClasses = [
|
|
164
|
-
contentBaseClasses,
|
|
165
|
-
contentStateClasses,
|
|
166
|
-
]
|
|
167
|
-
.join(' ')
|
|
168
|
-
.replace(/\s+/g, ' ')
|
|
169
|
-
.trim();
|
|
170
|
-
|
|
171
|
-
return (
|
|
172
|
-
<button
|
|
173
|
-
type="button"
|
|
174
|
-
role="tab"
|
|
175
|
-
aria-selected={active}
|
|
176
|
-
aria-disabled={disabled}
|
|
177
|
-
aria-label={ariaLabel || label}
|
|
178
|
-
tabIndex={disabled ? -1 : 0}
|
|
179
|
-
className={finalClasses}
|
|
180
|
-
onClick={disabled ? undefined : onClick}
|
|
181
|
-
disabled={disabled}
|
|
182
|
-
>
|
|
183
|
-
{/* Contenido del Tab */}
|
|
184
|
-
<div className={contentFinalClasses}>
|
|
185
|
-
{/* Icono (opcional) */}
|
|
186
|
-
{icon && (
|
|
187
|
-
<span className={`${iconClasses} ${textClasses}`.trim()}>
|
|
188
|
-
{icon}
|
|
189
|
-
</span>
|
|
190
|
-
)}
|
|
191
|
-
|
|
192
|
-
{/* Texto */}
|
|
193
|
-
<span
|
|
194
|
-
className={`
|
|
195
|
-
text-sm
|
|
196
|
-
font-bold
|
|
197
|
-
leading-5
|
|
198
|
-
whitespace-nowrap
|
|
199
|
-
${textClasses}
|
|
200
|
-
`
|
|
201
|
-
.replace(/\s+/g, ' ')
|
|
202
|
-
.trim()}
|
|
203
|
-
>
|
|
204
|
-
{label}
|
|
205
|
-
</span>
|
|
206
|
-
|
|
207
|
-
{/* Badge de notificación (opcional) */}
|
|
208
|
-
{badge !== undefined && badge > 0 && (
|
|
209
|
-
<span
|
|
210
|
-
className={`
|
|
211
|
-
flex
|
|
212
|
-
items-center
|
|
213
|
-
justify-center
|
|
214
|
-
h-3
|
|
215
|
-
min-w-[12px]
|
|
216
|
-
px-0.5
|
|
217
|
-
rounded-sm
|
|
218
|
-
text-xs
|
|
219
|
-
font-normal
|
|
220
|
-
leading-4
|
|
221
|
-
text-primary-inverse-content
|
|
222
|
-
dark:text-dark-bg-primary
|
|
223
|
-
${badgeClasses}
|
|
224
|
-
`
|
|
225
|
-
.replace(/\s+/g, ' ')
|
|
226
|
-
.trim()}
|
|
227
|
-
aria-label={`${badge} notificaciones`}
|
|
228
|
-
>
|
|
229
|
-
{badge > 99 ? '99+' : badge}
|
|
230
|
-
</span>
|
|
231
|
-
)}
|
|
232
|
-
</div>
|
|
233
|
-
|
|
234
|
-
{/* Indicador inferior (línea activa) */}
|
|
235
|
-
<div className={indicatorClasses} />
|
|
236
|
-
</button>
|
|
237
|
-
);
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Componente Tabs contenedor del sistema de diseño Siesa
|
|
242
|
-
*
|
|
243
|
-
* Agrupa múltiples tabs permitiendo navegación entre diferentes secciones.
|
|
244
|
-
* Soporta modo controlado (con activeId y onChange) y no controlado (con defaultActiveId).
|
|
245
|
-
*
|
|
246
|
-
* Mejores prácticas implementadas:
|
|
247
|
-
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
248
|
-
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
249
|
-
* - Tokens de color consistentes con la documentación
|
|
250
|
-
* - Type safety con TypeScript estricto
|
|
251
|
-
* - Accesibilidad con ARIA roles y keyboard navigation
|
|
252
|
-
*
|
|
253
|
-
* @see docs/colors.md - Sistema de colores
|
|
254
|
-
* @see docs/typography.md - Sistema tipográfico
|
|
255
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
256
|
-
*
|
|
257
|
-
* @example
|
|
258
|
-
* ```tsx
|
|
259
|
-
* // Modo controlado
|
|
260
|
-
* const [activeTab, setActiveTab] = useState('home');
|
|
261
|
-
*
|
|
262
|
-
* <Tabs
|
|
263
|
-
* items={[
|
|
264
|
-
* { id: 'home', label: 'Inicio', icon: <HomeIcon /> },
|
|
265
|
-
* { id: 'profile', label: 'Perfil', badge: 3 },
|
|
266
|
-
* { id: 'settings', label: 'Configuración' },
|
|
267
|
-
* ]}
|
|
268
|
-
* activeId={activeTab}
|
|
269
|
-
* onChange={setActiveTab}
|
|
270
|
-
* />
|
|
271
|
-
*
|
|
272
|
-
* // Modo no controlado
|
|
273
|
-
* <Tabs
|
|
274
|
-
* items={[...]}
|
|
275
|
-
* defaultActiveId="home"
|
|
276
|
-
* />
|
|
277
|
-
* ```
|
|
278
|
-
*/
|
|
279
|
-
export const Tabs: React.FC<TabsProps> = ({
|
|
280
|
-
items,
|
|
281
|
-
activeId,
|
|
282
|
-
defaultActiveId,
|
|
283
|
-
onChange,
|
|
284
|
-
className = '',
|
|
285
|
-
fullWidth = false,
|
|
286
|
-
size = 'base',
|
|
287
|
-
showBorder = true,
|
|
288
|
-
}) => {
|
|
289
|
-
// Estado interno para modo no controlado
|
|
290
|
-
const [internalActiveId, setInternalActiveId] = useState(
|
|
291
|
-
defaultActiveId || (items.length > 0 ? items[0].id : '')
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
// Determinar si es controlado o no controlado
|
|
295
|
-
const isControlled = activeId !== undefined;
|
|
296
|
-
const currentActiveId = isControlled ? activeId : internalActiveId;
|
|
297
|
-
|
|
298
|
-
// Handler para cambio de tab
|
|
299
|
-
const handleTabClick = (id: string) => {
|
|
300
|
-
if (!isControlled) {
|
|
301
|
-
setInternalActiveId(id);
|
|
302
|
-
}
|
|
303
|
-
onChange?.(id);
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// ===== CLASES DE TAMAÑO =====
|
|
307
|
-
const sizeClasses = {
|
|
308
|
-
sm: 'gap-0',
|
|
309
|
-
base: 'gap-1',
|
|
310
|
-
lg: 'gap-2',
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
// ===== CLASES DEL CONTENEDOR =====
|
|
314
|
-
// El padding-bottom (pb-2.5 = 10px) deja espacio para el indicador del tab activo
|
|
315
|
-
// El border-b se dibuja debajo del padding, alineándose con el indicador
|
|
316
|
-
const containerClasses = `
|
|
317
|
-
flex
|
|
318
|
-
items-end
|
|
319
|
-
pb-2.5
|
|
320
|
-
${sizeClasses[size]}
|
|
321
|
-
${fullWidth ? 'w-full' : ''}
|
|
322
|
-
${showBorder ? 'border-b border-border-primary dark:border-dark-border-primary' : ''}
|
|
323
|
-
`;
|
|
324
|
-
|
|
325
|
-
// ===== CLASES FULL WIDTH PARA ITEMS =====
|
|
326
|
-
const itemWidthClass = fullWidth ? 'flex-1' : '';
|
|
327
|
-
|
|
328
|
-
const finalClasses = [
|
|
329
|
-
containerClasses,
|
|
330
|
-
className,
|
|
331
|
-
]
|
|
332
|
-
.join(' ')
|
|
333
|
-
.replace(/\s+/g, ' ')
|
|
334
|
-
.trim();
|
|
335
|
-
|
|
336
|
-
return (
|
|
337
|
-
<div
|
|
338
|
-
role="tablist"
|
|
339
|
-
aria-label="Pestañas de navegación"
|
|
340
|
-
className={finalClasses}
|
|
341
|
-
>
|
|
342
|
-
{items.map((item) => (
|
|
343
|
-
<Tab
|
|
344
|
-
key={item.id}
|
|
345
|
-
label={item.label}
|
|
346
|
-
active={currentActiveId === item.id}
|
|
347
|
-
icon={item.icon}
|
|
348
|
-
badge={item.badge}
|
|
349
|
-
disabled={item.disabled}
|
|
350
|
-
onClick={() => handleTabClick(item.id)}
|
|
351
|
-
className={itemWidthClass}
|
|
352
|
-
/>
|
|
353
|
-
))}
|
|
354
|
-
</div>
|
|
355
|
-
);
|
|
356
|
-
};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Configuración de un item de Tab individual
|
|
5
|
-
*/
|
|
6
|
-
export interface TabItem {
|
|
7
|
-
/**
|
|
8
|
-
* Identificador único del tab
|
|
9
|
-
*/
|
|
10
|
-
id: string;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Texto a mostrar en el tab
|
|
14
|
-
*/
|
|
15
|
-
label: string;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Icono a mostrar a la izquierda del texto (opcional)
|
|
19
|
-
*/
|
|
20
|
-
icon?: ReactNode;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Número de notificaciones a mostrar en el badge (opcional)
|
|
24
|
-
*/
|
|
25
|
-
badge?: number;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Si el tab está deshabilitado
|
|
29
|
-
* @default false
|
|
30
|
-
*/
|
|
31
|
-
disabled?: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Props del componente Tab individual (interno)
|
|
36
|
-
*/
|
|
37
|
-
export interface TabProps {
|
|
38
|
-
/**
|
|
39
|
-
* Texto a mostrar en el tab
|
|
40
|
-
*/
|
|
41
|
-
label: string;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Si el tab está activo/seleccionado
|
|
45
|
-
* @default false
|
|
46
|
-
*/
|
|
47
|
-
active?: boolean;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Icono a mostrar a la izquierda del texto (opcional)
|
|
51
|
-
*/
|
|
52
|
-
icon?: ReactNode;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Número de notificaciones a mostrar en el badge (opcional)
|
|
56
|
-
*/
|
|
57
|
-
badge?: number;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Si el tab está deshabilitado
|
|
61
|
-
* @default false
|
|
62
|
-
*/
|
|
63
|
-
disabled?: boolean;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Función callback ejecutada al hacer click
|
|
67
|
-
*/
|
|
68
|
-
onClick?: () => void;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Clases CSS adicionales
|
|
72
|
-
*/
|
|
73
|
-
className?: string;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Etiqueta accesible para lectores de pantalla
|
|
77
|
-
*/
|
|
78
|
-
ariaLabel?: string;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Props del componente Tabs (contenedor)
|
|
83
|
-
*/
|
|
84
|
-
export interface TabsProps {
|
|
85
|
-
/**
|
|
86
|
-
* Lista de tabs a mostrar
|
|
87
|
-
*/
|
|
88
|
-
items: TabItem[];
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* ID del tab actualmente seleccionado
|
|
92
|
-
*/
|
|
93
|
-
activeId?: string;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* ID del tab seleccionado por defecto (para componente no controlado)
|
|
97
|
-
*/
|
|
98
|
-
defaultActiveId?: string;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Callback ejecutado cuando cambia el tab seleccionado
|
|
102
|
-
*/
|
|
103
|
-
onChange?: (id: string) => void;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Clases CSS adicionales para el contenedor
|
|
107
|
-
*/
|
|
108
|
-
className?: string;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Si los tabs deben ocupar todo el ancho disponible
|
|
112
|
-
* @default false
|
|
113
|
-
*/
|
|
114
|
-
fullWidth?: boolean;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Tamaño de los tabs
|
|
118
|
-
* @default 'base'
|
|
119
|
-
*/
|
|
120
|
-
size?: 'sm' | 'base' | 'lg';
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Si se muestra la línea inferior del contenedor de tabs
|
|
124
|
-
* @default true
|
|
125
|
-
*/
|
|
126
|
-
showBorder?: boolean;
|
|
127
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Icono de check (Heroicons Micro 16x16)
|
|
5
|
-
* Usado como icono de ejemplo en los tabs
|
|
6
|
-
*/
|
|
7
|
-
export const CheckIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
8
|
-
<svg
|
|
9
|
-
className={className}
|
|
10
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
-
viewBox="0 0 16 16"
|
|
12
|
-
fill="currentColor"
|
|
13
|
-
aria-hidden="true"
|
|
14
|
-
>
|
|
15
|
-
<path
|
|
16
|
-
fillRule="evenodd"
|
|
17
|
-
d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
|
|
18
|
-
clipRule="evenodd"
|
|
19
|
-
/>
|
|
20
|
-
</svg>
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Icono de inicio/home (Heroicons Micro 16x16)
|
|
25
|
-
*/
|
|
26
|
-
export const HomeIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
27
|
-
<svg
|
|
28
|
-
className={className}
|
|
29
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
30
|
-
viewBox="0 0 16 16"
|
|
31
|
-
fill="currentColor"
|
|
32
|
-
aria-hidden="true"
|
|
33
|
-
>
|
|
34
|
-
<path
|
|
35
|
-
d="M8.543 2.232a.75.75 0 0 0-1.085 0l-5.25 5.5A.75.75 0 0 0 2.75 9H4v4a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1v-1a1 1 0 1 1 2 0v1a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V9h1.25a.75.75 0 0 0 .543-1.268l-5.25-5.5Z"
|
|
36
|
-
/>
|
|
37
|
-
</svg>
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Icono de usuario (Heroicons Micro 16x16)
|
|
42
|
-
*/
|
|
43
|
-
export const UserIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
44
|
-
<svg
|
|
45
|
-
className={className}
|
|
46
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
47
|
-
viewBox="0 0 16 16"
|
|
48
|
-
fill="currentColor"
|
|
49
|
-
aria-hidden="true"
|
|
50
|
-
>
|
|
51
|
-
<path
|
|
52
|
-
d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z"
|
|
53
|
-
/>
|
|
54
|
-
</svg>
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Icono de configuración/engranaje (Heroicons Micro 16x16)
|
|
59
|
-
*/
|
|
60
|
-
export const CogIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
61
|
-
<svg
|
|
62
|
-
className={className}
|
|
63
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
64
|
-
viewBox="0 0 16 16"
|
|
65
|
-
fill="currentColor"
|
|
66
|
-
aria-hidden="true"
|
|
67
|
-
>
|
|
68
|
-
<path
|
|
69
|
-
fillRule="evenodd"
|
|
70
|
-
d="M6.455 1.45A.5.5 0 0 1 6.952 1h2.096a.5.5 0 0 1 .497.45l.186 1.858a4.996 4.996 0 0 1 1.466.848l1.703-.769a.5.5 0 0 1 .639.206l1.047 1.814a.5.5 0 0 1-.14.656l-1.517 1.09a5.026 5.026 0 0 1 0 1.694l1.516 1.09a.5.5 0 0 1 .141.656l-1.047 1.814a.5.5 0 0 1-.639.206l-1.703-.768c-.433.36-.928.649-1.466.847l-.186 1.858a.5.5 0 0 1-.497.45H6.952a.5.5 0 0 1-.497-.45l-.186-1.858a4.993 4.993 0 0 1-1.466-.848l-1.703.769a.5.5 0 0 1-.639-.206l-1.047-1.814a.5.5 0 0 1 .14-.656l1.517-1.09a5.033 5.033 0 0 1 0-1.694l-1.516-1.09a.5.5 0 0 1-.141-.656L2.46 3.593a.5.5 0 0 1 .639-.206l1.703.769c.433-.36.928-.65 1.466-.848l.186-1.858ZM8 10.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z"
|
|
71
|
-
clipRule="evenodd"
|
|
72
|
-
/>
|
|
73
|
-
</svg>
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Icono de campana/notificaciones (Heroicons Micro 16x16)
|
|
78
|
-
*/
|
|
79
|
-
export const BellIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
80
|
-
<svg
|
|
81
|
-
className={className}
|
|
82
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
83
|
-
viewBox="0 0 16 16"
|
|
84
|
-
fill="currentColor"
|
|
85
|
-
aria-hidden="true"
|
|
86
|
-
>
|
|
87
|
-
<path
|
|
88
|
-
fillRule="evenodd"
|
|
89
|
-
d="M12 5a4 4 0 0 0-8 0v2.379a1.5 1.5 0 0 1-.44 1.06L2.294 9.707a1 1 0 0 0-.294.707V11a1 1 0 0 0 1 1h2a3 3 0 1 0 6 0h2a1 1 0 0 0 1-1v-.586a1 1 0 0 0-.293-.707L12.44 8.44A1.5 1.5 0 0 1 12 7.38V5Zm-4 9a1 1 0 0 1-1-1h2a1 1 0 0 1-1 1Z"
|
|
90
|
-
clipRule="evenodd"
|
|
91
|
-
/>
|
|
92
|
-
</svg>
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Icono de documento (Heroicons Micro 16x16)
|
|
97
|
-
*/
|
|
98
|
-
export const DocumentIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
99
|
-
<svg
|
|
100
|
-
className={className}
|
|
101
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
102
|
-
viewBox="0 0 16 16"
|
|
103
|
-
fill="currentColor"
|
|
104
|
-
aria-hidden="true"
|
|
105
|
-
>
|
|
106
|
-
<path
|
|
107
|
-
fillRule="evenodd"
|
|
108
|
-
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm1 5.75A.75.75 0 0 1 5.75 7h4.5a.75.75 0 0 1 0 1.5h-4.5A.75.75 0 0 1 5 7.75Zm0 3a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Z"
|
|
109
|
-
clipRule="evenodd"
|
|
110
|
-
/>
|
|
111
|
-
</svg>
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Icono de gráfico/estadísticas (Heroicons Micro 16x16)
|
|
116
|
-
*/
|
|
117
|
-
export const ChartIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
118
|
-
<svg
|
|
119
|
-
className={className}
|
|
120
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
121
|
-
viewBox="0 0 16 16"
|
|
122
|
-
fill="currentColor"
|
|
123
|
-
aria-hidden="true"
|
|
124
|
-
>
|
|
125
|
-
<path
|
|
126
|
-
d="M13 6.5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5ZM10 4a.5.5 0 0 1 .5.5v7.5a.5.5 0 0 1-1 0V4.5A.5.5 0 0 1 10 4ZM7 8a.5.5 0 0 1 .5.5V12a.5.5 0 0 1-1 0V8.5A.5.5 0 0 1 7 8ZM4 10.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5Z"
|
|
127
|
-
/>
|
|
128
|
-
</svg>
|
|
129
|
-
);
|