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,246 +0,0 @@
|
|
|
1
|
-
import { forwardRef } from 'react';
|
|
2
|
-
import type { SwitchProps } from './Switch.types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Switch - Componente de switch del sistema de diseño Siesa
|
|
6
|
-
*
|
|
7
|
-
* Campo de activación/desactivación con soporte para:
|
|
8
|
-
* - Estados: default, hover, focus, disabled
|
|
9
|
-
* - On, off
|
|
10
|
-
* - Label y description opcionales
|
|
11
|
-
* - Posición del label: leading (izquierda) o trailing (derecha)
|
|
12
|
-
* - Animación suave de transición
|
|
13
|
-
* - Dark mode completo
|
|
14
|
-
* - Accesibilidad completa
|
|
15
|
-
*
|
|
16
|
-
* Mejores prácticas implementadas:
|
|
17
|
-
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
18
|
-
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
19
|
-
* - Tokens de color consistentes con la documentación
|
|
20
|
-
* - Focus ring con shadow especial según Figma
|
|
21
|
-
* - Type safety con TypeScript estricto
|
|
22
|
-
* - Accesibilidad con ARIA labels
|
|
23
|
-
*
|
|
24
|
-
* @see docs/colors.md - Sistema de colores
|
|
25
|
-
* @see docs/typography.md - Sistema tipográfico
|
|
26
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```tsx
|
|
30
|
-
* // Label a la izquierda (default)
|
|
31
|
-
* <Switch
|
|
32
|
-
* label="Permitir inserción"
|
|
33
|
-
* description="Permite que otros inserten los detalles de tu evento."
|
|
34
|
-
* checked={true}
|
|
35
|
-
* onChange={(e) => console.log(e.target.checked)}
|
|
36
|
-
* />
|
|
37
|
-
*
|
|
38
|
-
* // Label a la derecha
|
|
39
|
-
* <Switch
|
|
40
|
-
* label="Activar notificaciones"
|
|
41
|
-
* labelPosition="trailing"
|
|
42
|
-
* checked={false}
|
|
43
|
-
* onChange={(e) => console.log(e.target.checked)}
|
|
44
|
-
* />
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
48
|
-
(
|
|
49
|
-
{
|
|
50
|
-
label,
|
|
51
|
-
description,
|
|
52
|
-
labelPosition = 'leading',
|
|
53
|
-
checked = false,
|
|
54
|
-
disabled = false,
|
|
55
|
-
className = '',
|
|
56
|
-
id,
|
|
57
|
-
onChange,
|
|
58
|
-
ariaLabel,
|
|
59
|
-
...props
|
|
60
|
-
},
|
|
61
|
-
ref
|
|
62
|
-
) => {
|
|
63
|
-
// Generar ID único si no se proporciona
|
|
64
|
-
const switchId = id || `switch-${Math.random().toString(36).substr(2, 9)}`;
|
|
65
|
-
|
|
66
|
-
// ===== CLASES BASE DEL TRACK =====
|
|
67
|
-
const baseTrackClasses = `
|
|
68
|
-
w-8
|
|
69
|
-
h-5
|
|
70
|
-
rounded-xl
|
|
71
|
-
border
|
|
72
|
-
overflow-hidden
|
|
73
|
-
relative
|
|
74
|
-
transition-all
|
|
75
|
-
duration-200
|
|
76
|
-
`;
|
|
77
|
-
|
|
78
|
-
// ===== CLASES DE ESTADO DEL TRACK =====
|
|
79
|
-
const getTrackStateClasses = () => {
|
|
80
|
-
if (disabled) {
|
|
81
|
-
return checked
|
|
82
|
-
? `
|
|
83
|
-
bg-primary-custom-600
|
|
84
|
-
border-primary-inverse-border
|
|
85
|
-
opacity-50
|
|
86
|
-
cursor-not-allowed
|
|
87
|
-
dark:bg-primary-custom-600
|
|
88
|
-
dark:border-primary-inverse-border
|
|
89
|
-
`
|
|
90
|
-
: `
|
|
91
|
-
bg-[#fafafa]
|
|
92
|
-
border-border-primary
|
|
93
|
-
opacity-50
|
|
94
|
-
cursor-not-allowed
|
|
95
|
-
dark:bg-dark-bg-primary
|
|
96
|
-
dark:border-dark-border-primary
|
|
97
|
-
`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return checked
|
|
101
|
-
? `
|
|
102
|
-
bg-primary-custom-600
|
|
103
|
-
border-primary-inverse-border
|
|
104
|
-
cursor-pointer
|
|
105
|
-
peer-hover:border-[#307cc5]
|
|
106
|
-
peer-focus:shadow-[0px_0px_0px_2px_#dbeefe,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#60b6fa]
|
|
107
|
-
dark:bg-primary-custom-600
|
|
108
|
-
dark:border-primary-inverse-border
|
|
109
|
-
dark:peer-hover:border-[#307cc5]
|
|
110
|
-
dark:peer-focus:shadow-[0px_0px_0px_2px_#dbeefe,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#60b6fa]
|
|
111
|
-
`
|
|
112
|
-
: `
|
|
113
|
-
bg-[#fafafa]
|
|
114
|
-
border-border-primary
|
|
115
|
-
cursor-pointer
|
|
116
|
-
peer-hover:border-[#b6b6b9]
|
|
117
|
-
peer-focus:shadow-[0px_0px_0px_2px_#dbeefe,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#60b6fa]
|
|
118
|
-
dark:bg-dark-bg-primary
|
|
119
|
-
dark:border-dark-border-primary
|
|
120
|
-
dark:peer-hover:border-[#b6b6b9]
|
|
121
|
-
dark:peer-focus:shadow-[0px_0px_0px_2px_#dbeefe,0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_#60b6fa]
|
|
122
|
-
`;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// ===== CLASES DEL BOTÓN (CÍRCULO) =====
|
|
126
|
-
const getButtonClasses = () => {
|
|
127
|
-
const baseButtonClasses = `
|
|
128
|
-
absolute
|
|
129
|
-
w-4
|
|
130
|
-
h-4
|
|
131
|
-
bg-white
|
|
132
|
-
rounded-[10px]
|
|
133
|
-
border
|
|
134
|
-
top-1/2
|
|
135
|
-
-translate-y-1/2
|
|
136
|
-
transition-all
|
|
137
|
-
duration-200
|
|
138
|
-
`;
|
|
139
|
-
|
|
140
|
-
// Posición: OFF = left-px (1px), ON = left-[13px]
|
|
141
|
-
const positionClass = checked ? 'left-[13px]' : 'left-px';
|
|
142
|
-
|
|
143
|
-
if (disabled) {
|
|
144
|
-
const borderClass = checked
|
|
145
|
-
? 'border-primary-inverse-border dark:border-primary-inverse-border'
|
|
146
|
-
: 'border-border-primary dark:border-dark-border-primary';
|
|
147
|
-
return `${baseButtonClasses} ${positionClass} ${borderClass}`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const borderClass = checked
|
|
151
|
-
? `
|
|
152
|
-
border-primary-inverse-border
|
|
153
|
-
peer-hover:border-[#307cc5]
|
|
154
|
-
dark:border-primary-inverse-border
|
|
155
|
-
dark:peer-hover:border-[#307cc5]
|
|
156
|
-
`
|
|
157
|
-
: `
|
|
158
|
-
border-border-primary
|
|
159
|
-
peer-hover:border-[#b6b6b9]
|
|
160
|
-
dark:border-dark-border-primary
|
|
161
|
-
dark:peer-hover:border-[#b6b6b9]
|
|
162
|
-
`;
|
|
163
|
-
|
|
164
|
-
return `${baseButtonClasses} ${positionClass} ${borderClass}`;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// ===== COMBINAR CLASES =====
|
|
168
|
-
const trackClasses = [
|
|
169
|
-
baseTrackClasses,
|
|
170
|
-
getTrackStateClasses(),
|
|
171
|
-
]
|
|
172
|
-
.join(' ')
|
|
173
|
-
.replace(/\s+/g, ' ')
|
|
174
|
-
.trim();
|
|
175
|
-
|
|
176
|
-
const buttonClasses = getButtonClasses()
|
|
177
|
-
.replace(/\s+/g, ' ')
|
|
178
|
-
.trim();
|
|
179
|
-
|
|
180
|
-
// ===== COMPONENTES REUTILIZABLES =====
|
|
181
|
-
const LabelContent = (label || description) ? (
|
|
182
|
-
<label
|
|
183
|
-
htmlFor={switchId}
|
|
184
|
-
className={`flex-1 flex flex-col gap-1 text-sm leading-5 ${
|
|
185
|
-
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
186
|
-
}`}
|
|
187
|
-
>
|
|
188
|
-
{label && (
|
|
189
|
-
<span className="font-bold text-content-primary dark:text-dark-content-primary">
|
|
190
|
-
{label}
|
|
191
|
-
</span>
|
|
192
|
-
)}
|
|
193
|
-
{description && (
|
|
194
|
-
<span className="font-normal text-content-secondary dark:text-content-secondary">
|
|
195
|
-
{description}
|
|
196
|
-
</span>
|
|
197
|
-
)}
|
|
198
|
-
</label>
|
|
199
|
-
) : null;
|
|
200
|
-
|
|
201
|
-
const SwitchInput = (
|
|
202
|
-
<div className="flex items-center justify-center shrink-0">
|
|
203
|
-
{/* Native Input (hidden but accessible) */}
|
|
204
|
-
<input
|
|
205
|
-
ref={ref}
|
|
206
|
-
type="checkbox"
|
|
207
|
-
id={switchId}
|
|
208
|
-
checked={checked}
|
|
209
|
-
disabled={disabled}
|
|
210
|
-
onChange={onChange}
|
|
211
|
-
className="peer sr-only"
|
|
212
|
-
aria-label={ariaLabel || label}
|
|
213
|
-
{...props}
|
|
214
|
-
/>
|
|
215
|
-
|
|
216
|
-
{/* Custom Switch Visual */}
|
|
217
|
-
<label
|
|
218
|
-
htmlFor={switchId}
|
|
219
|
-
className={trackClasses}
|
|
220
|
-
aria-hidden="true"
|
|
221
|
-
>
|
|
222
|
-
{/* Button (sliding circle) */}
|
|
223
|
-
<div className={buttonClasses} />
|
|
224
|
-
</label>
|
|
225
|
-
</div>
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<div className={`flex gap-2 items-center w-[344px] ${className}`}>
|
|
230
|
-
{labelPosition === 'leading' ? (
|
|
231
|
-
<>
|
|
232
|
-
{LabelContent}
|
|
233
|
-
{SwitchInput}
|
|
234
|
-
</>
|
|
235
|
-
) : (
|
|
236
|
-
<>
|
|
237
|
-
{SwitchInput}
|
|
238
|
-
{LabelContent}
|
|
239
|
-
</>
|
|
240
|
-
)}
|
|
241
|
-
</div>
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
Switch.displayName = 'Switch';
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { InputHTMLAttributes } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Estados visuales del Switch
|
|
5
|
-
*/
|
|
6
|
-
export type SwitchState = 'default' | 'hover' | 'focus' | 'disabled';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Posición del label respecto al switch
|
|
10
|
-
* - `leading`: Label a la izquierda, switch a la derecha
|
|
11
|
-
* - `trailing`: Switch a la izquierda, label a la derecha
|
|
12
|
-
*/
|
|
13
|
-
export type LabelPosition = 'leading' | 'trailing';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Props del componente Switch
|
|
17
|
-
*
|
|
18
|
-
* @see docs/colors.md - Sistema de colores
|
|
19
|
-
* @see docs/typography.md - Sistema tipográfico
|
|
20
|
-
*/
|
|
21
|
-
export interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
|
|
22
|
-
/**
|
|
23
|
-
* Etiqueta del switch
|
|
24
|
-
*/
|
|
25
|
-
label?: string;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Descripción debajo del label
|
|
29
|
-
*/
|
|
30
|
-
description?: string;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Posición del label respecto al switch
|
|
34
|
-
* - `leading`: Label a la izquierda, switch a la derecha (default)
|
|
35
|
-
* - `trailing`: Switch a la izquierda, label a la derecha
|
|
36
|
-
* @default 'leading'
|
|
37
|
-
*/
|
|
38
|
-
labelPosition?: LabelPosition;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Si el switch está activado
|
|
42
|
-
* @default false
|
|
43
|
-
*/
|
|
44
|
-
checked?: boolean;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Si el switch está deshabilitado
|
|
48
|
-
* @default false
|
|
49
|
-
*/
|
|
50
|
-
disabled?: boolean;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Clases CSS adicionales
|
|
54
|
-
*/
|
|
55
|
-
className?: string;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Callback cuando el estado del switch cambia
|
|
59
|
-
*/
|
|
60
|
-
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Label para accesibilidad (ARIA)
|
|
64
|
-
* Si no se proporciona, se usa el valor de `label`
|
|
65
|
-
*/
|
|
66
|
-
ariaLabel?: string;
|
|
67
|
-
}
|
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
# Table Component
|
|
2
|
-
|
|
3
|
-
Componente de tabla flexible y accesible con soporte para sorting, variantes visuales y dark mode completo.
|
|
4
|
-
|
|
5
|
-
## Características
|
|
6
|
-
|
|
7
|
-
- ✅ **Definición de columnas flexible** con `accessor` y `render` custom
|
|
8
|
-
- ✅ **Sorting** en columnas (controlado o no controlado)
|
|
9
|
-
- ✅ **Variantes visuales**: basic, fullWidth, striped, grid
|
|
10
|
-
- ✅ **Dark mode completo** en todos los estados
|
|
11
|
-
- ✅ **Estados de loading y empty**
|
|
12
|
-
- ✅ **Filas clickeables**
|
|
13
|
-
- ✅ **TypeScript** con tipos estrictos y generics
|
|
14
|
-
- ✅ **Accesibilidad** con roles y keyboard navigation
|
|
15
|
-
|
|
16
|
-
## Uso Básico
|
|
17
|
-
|
|
18
|
-
```tsx
|
|
19
|
-
import { Table } from '@/components/Table';
|
|
20
|
-
import type { TableColumn } from '@/components/Table';
|
|
21
|
-
|
|
22
|
-
interface Usuario {
|
|
23
|
-
id: number;
|
|
24
|
-
nombre: string;
|
|
25
|
-
email: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const usuarios: Usuario[] = [
|
|
29
|
-
{ id: 1, nombre: 'Juan Pérez', email: 'juan@example.com' },
|
|
30
|
-
{ id: 2, nombre: 'María García', email: 'maria@example.com' },
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
const columns: TableColumn<Usuario>[] = [
|
|
34
|
-
{ header: 'Nombre', accessor: 'nombre' },
|
|
35
|
-
{ header: 'Email', accessor: 'email' },
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
function App() {
|
|
39
|
-
return (
|
|
40
|
-
<Table
|
|
41
|
-
title="Usuarios"
|
|
42
|
-
columns={columns}
|
|
43
|
-
data={usuarios}
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Variantes
|
|
50
|
-
|
|
51
|
-
### Basic
|
|
52
|
-
Tabla estándar con padding y spacing consistente.
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
<Table
|
|
56
|
-
title="Usuarios"
|
|
57
|
-
columns={columns}
|
|
58
|
-
data={data}
|
|
59
|
-
variant="basic"
|
|
60
|
-
/>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Full Width
|
|
64
|
-
Tabla que ocupa todo el ancho disponible.
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
<Table
|
|
68
|
-
title="Usuarios"
|
|
69
|
-
columns={columns}
|
|
70
|
-
data={data}
|
|
71
|
-
variant="fullWidth"
|
|
72
|
-
/>
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Striped (Filas Alternas)
|
|
76
|
-
Filas con fondo alternado para mejor legibilidad.
|
|
77
|
-
|
|
78
|
-
```tsx
|
|
79
|
-
<Table
|
|
80
|
-
title="Usuarios"
|
|
81
|
-
columns={columns}
|
|
82
|
-
data={data}
|
|
83
|
-
variant="striped"
|
|
84
|
-
/>
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Grid (Con Bordes)
|
|
88
|
-
Tabla con bordes alrededor de las celdas.
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
<Table
|
|
92
|
-
title="Usuarios"
|
|
93
|
-
columns={columns}
|
|
94
|
-
data={data}
|
|
95
|
-
variant="grid"
|
|
96
|
-
/>
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Sorting
|
|
100
|
-
|
|
101
|
-
### Sorting No Controlado (Interno)
|
|
102
|
-
|
|
103
|
-
```tsx
|
|
104
|
-
const columns: TableColumn<Usuario>[] = [
|
|
105
|
-
{
|
|
106
|
-
header: 'Nombre',
|
|
107
|
-
accessor: 'nombre',
|
|
108
|
-
sortable: true, // Habilitar sorting
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
header: 'Email',
|
|
112
|
-
accessor: 'email',
|
|
113
|
-
sortable: true,
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
<Table columns={columns} data={data} />
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Sorting Controlado (Externo)
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
const [sortColumn, setSortColumn] = useState<keyof Usuario | null>(null);
|
|
124
|
-
const [sortDirection, setSortDirection] = useState<SortDirection>(null);
|
|
125
|
-
|
|
126
|
-
const handleSort = (column: keyof Usuario | string, direction: SortDirection) => {
|
|
127
|
-
setSortColumn(column as keyof Usuario);
|
|
128
|
-
setSortDirection(direction);
|
|
129
|
-
|
|
130
|
-
// Ordenar los datos
|
|
131
|
-
const sorted = [...data].sort((a, b) => {
|
|
132
|
-
// Lógica de ordenamiento
|
|
133
|
-
});
|
|
134
|
-
setData(sorted);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
<Table
|
|
138
|
-
columns={columns}
|
|
139
|
-
data={data}
|
|
140
|
-
sortColumn={sortColumn}
|
|
141
|
-
sortDirection={sortDirection}
|
|
142
|
-
onSort={handleSort}
|
|
143
|
-
/>
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Render Custom
|
|
147
|
-
|
|
148
|
-
Puedes personalizar cómo se renderiza el contenido de una celda:
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
const columns: TableColumn<Usuario>[] = [
|
|
152
|
-
{
|
|
153
|
-
header: 'Estado',
|
|
154
|
-
accessor: 'estado',
|
|
155
|
-
render: (value, row, index) => (
|
|
156
|
-
<span
|
|
157
|
-
className={`
|
|
158
|
-
px-2 py-1 rounded-md text-xs font-bold
|
|
159
|
-
${value === 'activo' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}
|
|
160
|
-
`}
|
|
161
|
-
>
|
|
162
|
-
{value}
|
|
163
|
-
</span>
|
|
164
|
-
),
|
|
165
|
-
},
|
|
166
|
-
];
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Accessor Function
|
|
170
|
-
|
|
171
|
-
Puedes usar una función para acceder a valores anidados o calculados:
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
const columns: TableColumn<Usuario>[] = [
|
|
175
|
-
{
|
|
176
|
-
header: 'Nombre Completo',
|
|
177
|
-
accessor: (row) => `${row.nombre} ${row.apellido}`,
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
header: 'Email',
|
|
181
|
-
accessor: (row) => row.contacto.email.toLowerCase(),
|
|
182
|
-
},
|
|
183
|
-
];
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
## Alineación de Columnas
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
const columns: TableColumn<Usuario>[] = [
|
|
190
|
-
{
|
|
191
|
-
header: 'ID',
|
|
192
|
-
accessor: 'id',
|
|
193
|
-
align: 'center', // 'left' | 'center' | 'right'
|
|
194
|
-
width: '80px', // Ancho fijo
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
header: 'Nombre',
|
|
198
|
-
accessor: 'nombre',
|
|
199
|
-
align: 'left',
|
|
200
|
-
},
|
|
201
|
-
];
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Estados
|
|
205
|
-
|
|
206
|
-
### Loading
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
<Table
|
|
210
|
-
title="Usuarios"
|
|
211
|
-
columns={columns}
|
|
212
|
-
data={[]}
|
|
213
|
-
loading={true}
|
|
214
|
-
loadingRows={5}
|
|
215
|
-
/>
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### Empty
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
<Table
|
|
222
|
-
title="Usuarios"
|
|
223
|
-
columns={columns}
|
|
224
|
-
data={[]}
|
|
225
|
-
emptyMessage="No hay usuarios registrados"
|
|
226
|
-
/>
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## Filas Clickeables
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
const handleRowClick = (row: Usuario, index: number) => {
|
|
233
|
-
console.log('Clicked:', row);
|
|
234
|
-
// Navegar a detalle, abrir modal, etc.
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
<Table
|
|
238
|
-
columns={columns}
|
|
239
|
-
data={data}
|
|
240
|
-
onRowClick={handleRowClick}
|
|
241
|
-
/>
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Props
|
|
245
|
-
|
|
246
|
-
### TableProps
|
|
247
|
-
|
|
248
|
-
| Prop | Tipo | Default | Descripción |
|
|
249
|
-
|------|------|---------|-------------|
|
|
250
|
-
| `title` | `string` | `undefined` | Título opcional de la tabla |
|
|
251
|
-
| `columns` | `TableColumn<T>[]` | **Required** | Definición de las columnas |
|
|
252
|
-
| `data` | `T[]` | **Required** | Datos de las filas |
|
|
253
|
-
| `variant` | `'basic' \| 'fullWidth' \| 'striped' \| 'grid'` | `'basic'` | Variante visual |
|
|
254
|
-
| `showBorder` | `boolean` | `true` | Si se muestra el borde del container |
|
|
255
|
-
| `showShadow` | `boolean` | `true` | Si se muestra la sombra del container |
|
|
256
|
-
| `onRowClick` | `(row: T, index: number) => void` | `undefined` | Callback al hacer click en una fila |
|
|
257
|
-
| `onSort` | `(column: string, direction: SortDirection) => void` | `undefined` | Callback al cambiar el sort |
|
|
258
|
-
| `sortColumn` | `keyof T \| string` | `undefined` | Columna ordenada (controlado) |
|
|
259
|
-
| `sortDirection` | `SortDirection` | `undefined` | Dirección de sort (controlado) |
|
|
260
|
-
| `fullWidth` | `boolean` | `false` | Si ocupa todo el ancho |
|
|
261
|
-
| `emptyMessage` | `string` | `'No hay datos disponibles'` | Mensaje cuando no hay datos |
|
|
262
|
-
| `loading` | `boolean` | `false` | Si está cargando |
|
|
263
|
-
| `loadingRows` | `number` | `5` | Número de rows skeleton |
|
|
264
|
-
| `className` | `string` | `''` | Clases CSS adicionales |
|
|
265
|
-
| `id` | `string` | `undefined` | ID único |
|
|
266
|
-
|
|
267
|
-
### TableColumn
|
|
268
|
-
|
|
269
|
-
| Prop | Tipo | Default | Descripción |
|
|
270
|
-
|------|------|---------|-------------|
|
|
271
|
-
| `header` | `string` | **Required** | Título del header |
|
|
272
|
-
| `accessor` | `keyof T \| (row: T) => any` | **Required** | Key o función para obtener valor |
|
|
273
|
-
| `sortable` | `boolean` | `false` | Si la columna es sortable |
|
|
274
|
-
| `render` | `(value: any, row: T, index: number) => ReactNode` | `undefined` | Función custom de render |
|
|
275
|
-
| `align` | `'left' \| 'center' \| 'right'` | `'left'` | Alineación del contenido |
|
|
276
|
-
| `width` | `string` | `undefined` | Ancho de la columna |
|
|
277
|
-
|
|
278
|
-
## Dark Mode
|
|
279
|
-
|
|
280
|
-
El componente Table se adapta automáticamente a dark mode usando los tokens de color del sistema:
|
|
281
|
-
|
|
282
|
-
```tsx
|
|
283
|
-
// Los colores se adaptan automáticamente
|
|
284
|
-
<Table
|
|
285
|
-
columns={columns}
|
|
286
|
-
data={data}
|
|
287
|
-
// En light mode: bg-white, text-content-primary
|
|
288
|
-
// En dark mode: bg-dark-bg-primary, text-dark-content-primary
|
|
289
|
-
/>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
## Accesibilidad
|
|
293
|
-
|
|
294
|
-
- ✅ Headers clickeables tienen `role="button"` y `tabIndex={0}`
|
|
295
|
-
- ✅ Soporta navegación por teclado (Enter y Space)
|
|
296
|
-
- ✅ Estados de sort anunciados visualmente con iconos
|
|
297
|
-
- ✅ Filas clickeables con keyboard support
|
|
298
|
-
|
|
299
|
-
## Ejemplos Avanzados
|
|
300
|
-
|
|
301
|
-
### Tabla de Productos con Render Custom
|
|
302
|
-
|
|
303
|
-
```tsx
|
|
304
|
-
interface Producto {
|
|
305
|
-
id: number;
|
|
306
|
-
nombre: string;
|
|
307
|
-
precio: number;
|
|
308
|
-
stock: number;
|
|
309
|
-
disponible: boolean;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const columns: TableColumn<Producto>[] = [
|
|
313
|
-
{
|
|
314
|
-
header: 'Producto',
|
|
315
|
-
accessor: 'nombre',
|
|
316
|
-
sortable: true,
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
header: 'Precio',
|
|
320
|
-
accessor: 'precio',
|
|
321
|
-
align: 'right',
|
|
322
|
-
render: (value) => `$${value.toFixed(2)}`,
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
header: 'Stock',
|
|
326
|
-
accessor: 'stock',
|
|
327
|
-
align: 'center',
|
|
328
|
-
render: (value) => (
|
|
329
|
-
<span className={value < 10 ? 'text-red-600 font-bold' : ''}>
|
|
330
|
-
{value}
|
|
331
|
-
</span>
|
|
332
|
-
),
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
header: 'Disponibilidad',
|
|
336
|
-
accessor: 'disponible',
|
|
337
|
-
align: 'center',
|
|
338
|
-
render: (value) => (
|
|
339
|
-
<span className={`
|
|
340
|
-
px-2 py-1 rounded text-xs font-bold
|
|
341
|
-
${value ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}
|
|
342
|
-
`}>
|
|
343
|
-
{value ? 'Disponible' : 'Agotado'}
|
|
344
|
-
</span>
|
|
345
|
-
),
|
|
346
|
-
},
|
|
347
|
-
];
|
|
348
|
-
|
|
349
|
-
<Table
|
|
350
|
-
title="Inventario de Productos"
|
|
351
|
-
columns={columns}
|
|
352
|
-
data={productos}
|
|
353
|
-
variant="fullWidth"
|
|
354
|
-
/>
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## Archivos Relacionados
|
|
358
|
-
|
|
359
|
-
- `Table.tsx` - Implementación del componente (561 líneas)
|
|
360
|
-
- `Table.types.ts` - Tipos TypeScript (149 líneas)
|
|
361
|
-
- `Table.stories.tsx` - Stories de Storybook (11 ejemplos)
|
|
362
|
-
- `index.ts` - Exports del módulo
|
|
363
|
-
|
|
364
|
-
## Referencias
|
|
365
|
-
|
|
366
|
-
- [Figma Design](https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4184-15615&m=dev)
|
|
367
|
-
- `docs/colors.md` - Sistema de colores
|
|
368
|
-
- `docs/typography.md` - Sistema tipográfico
|
|
369
|
-
- `docs/spacing.md` - Sistema de espaciado
|