siesa-ui-kit 1.0.2 → 1.0.4
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/README.md +115 -115
- package/bin/install.cjs +502 -502
- package/bin/prepare-publish.cjs +28 -28
- package/bin/restore-folders.cjs +28 -28
- package/claude/agents/siesa-ui-kit-specialist.md +2445 -0
- package/claude/prompts/component-template.md +121 -0
- package/claude/prompts/siesa-ui-kit.md +28 -0
- package/claude/settings.local.json +67 -2
- package/dist/components/Button/icons.d.ts +6 -5
- package/dist/components/Button/icons.d.ts.map +1 -1
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.d.ts.map +1 -1
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts +21 -0
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts.map +1 -1
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts +122 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts +139 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/icons.d.ts +33 -0
- package/dist/components/NavigationRailCommercial/icons.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/index.d.ts +4 -0
- package/dist/components/NavigationRailCommercial/index.d.ts.map +1 -0
- package/dist/components/NavigationRailItem/NavigationRailItem.d.ts.map +1 -1
- package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts +7 -0
- package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/NavigationRailTypes.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts +41 -0
- package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/icons.d.ts +15 -29
- package/dist/components/NavigationRailTypes/icons.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/icons.d.ts +6 -2
- package/dist/components/Select/icons.d.ts.map +1 -1
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/siesa-ui-kit.cjs +404 -190
- package/dist/siesa-ui-kit.cjs.map +1 -1
- package/dist/siesa-ui-kit.mjs +6590 -1506
- package/dist/siesa-ui-kit.mjs.map +1 -1
- package/dist/views/LayoutCommercial/LayoutCommercial.d.ts +48 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.d.ts.map +1 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts +49 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts.map +1 -0
- package/dist/views/LayoutCommercial/index.d.ts +3 -0
- package/dist/views/LayoutCommercial/index.d.ts.map +1 -0
- package/docs/icons.md +12 -31
- package/package.json +111 -110
- package/src/components/Avatar/Avatar.stories.tsx +494 -494
- package/src/components/Button/Button.stories.tsx +950 -950
- package/src/components/Button/Button.tsx +337 -337
- package/src/components/Button/Button.types.ts +180 -180
- package/src/components/Button/icons.tsx +23 -62
- package/src/components/DescriptionList/DescriptionList.stories.tsx +250 -250
- package/src/components/Divider/Divider.stories.tsx +263 -263
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.stories.tsx +317 -317
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.tsx +307 -287
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.types.ts +136 -111
- package/src/components/DropdownItemCollapsible/README.md +264 -264
- package/src/components/DropdownItemCollapsible/icons.tsx +57 -57
- package/src/components/DropdownItemCollapsible/index.ts +12 -12
- package/src/components/DropdownItemHeading/DropdownItemHeading.stories.tsx +386 -386
- package/src/components/DropdownItemHeading/DropdownItemHeading.tsx +216 -216
- package/src/components/DropdownItemHeading/DropdownItemHeading.types.ts +93 -93
- package/src/components/DropdownItemHeading/README.md +573 -573
- package/src/components/DropdownItemHeading/icons.tsx +125 -125
- package/src/components/DropdownItemHeading/index.ts +3 -3
- package/src/components/Input/Input.stories.tsx +583 -583
- package/src/components/LoginView/LoginView.stories.tsx +148 -148
- package/src/components/LoginView/LoginView.tsx +426 -426
- package/src/components/LoginView/LoginView.types.ts +52 -52
- package/src/components/LoginView/README.md +396 -396
- package/src/components/LoginView/icons.tsx +85 -85
- package/src/components/LoginView/index.ts +3 -3
- package/src/components/Navbar/Navbar.stories.tsx +810 -810
- package/src/components/Navbar/Navbar.tsx +755 -755
- package/src/components/Navbar/Navbar.types.ts +219 -219
- package/src/components/Navbar/README.md +279 -279
- package/src/components/Navbar/index.ts +8 -8
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.stories.tsx +464 -0
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.tsx +301 -0
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.types.ts +162 -0
- package/src/components/NavigationRailCommercial/README.md +251 -0
- package/src/components/NavigationRailCommercial/icons.tsx +54 -0
- package/src/components/NavigationRailCommercial/index.ts +6 -0
- package/src/components/NavigationRailItem/NavigationRailItem.stories.tsx +667 -667
- package/src/components/NavigationRailItem/NavigationRailItem.tsx +314 -313
- package/src/components/NavigationRailItem/NavigationRailItem.types.ts +175 -167
- package/src/components/NavigationRailItem/README.md +476 -476
- package/src/components/NavigationRailItem/index.ts +2 -2
- package/src/components/NavigationRailPanel/NavigationRailPanel.stories.tsx +462 -462
- package/src/components/NavigationRailPanel/NavigationRailPanel.tsx +332 -332
- package/src/components/NavigationRailPanel/NavigationRailPanel.types.ts +178 -178
- package/src/components/NavigationRailPanel/README.md +461 -461
- package/src/components/NavigationRailPanel/index.ts +6 -6
- package/src/components/NavigationRailTypes/NavigationRailTypes.stories.tsx +682 -528
- package/src/components/NavigationRailTypes/NavigationRailTypes.tsx +363 -378
- package/src/components/NavigationRailTypes/NavigationRailTypes.types.ts +178 -130
- package/src/components/NavigationRailTypes/README.md +573 -573
- package/src/components/NavigationRailTypes/icons.tsx +76 -141
- package/src/components/NavigationRailTypes/index.ts +7 -7
- package/src/components/Notification/Notification.stories.tsx +513 -513
- package/src/components/Notification/Notification.tsx +145 -145
- package/src/components/Notification/Notification.types.ts +142 -142
- package/src/components/Notification/README.md +409 -409
- package/src/components/POSConvention/POSConvention.stories.tsx +235 -235
- package/src/components/POSConvention/POSConvention.tsx +129 -129
- package/src/components/POSConvention/POSConvention.types.ts +38 -38
- package/src/components/POSConvention/README.md +123 -123
- package/src/components/POSConvention/icons.tsx +45 -45
- package/src/components/POSConvention/index.ts +3 -3
- package/src/components/POSLocationButton/POSLocationButton.stories.tsx +531 -531
- package/src/components/POSLocationButton/POSLocationButton.tsx +247 -247
- package/src/components/POSLocationButton/POSLocationButton.types.ts +87 -87
- package/src/components/POSLocationButton/README.md +253 -253
- package/src/components/POSLocationButton/icons.tsx +120 -120
- package/src/components/POSLocationButton/index.ts +14 -14
- package/src/components/POSNumberButton/POSNumberButton.stories.tsx +415 -415
- package/src/components/POSNumberButton/POSNumberButton.tsx +179 -179
- package/src/components/POSNumberButton/POSNumberButton.types.ts +51 -51
- package/src/components/POSNumberButton/README.md +321 -321
- package/src/components/POSNumberButton/index.ts +3 -3
- package/src/components/POSProductButton/POSProductButton.stories.tsx +318 -318
- package/src/components/POSProductCard/POSProductCard.stories.tsx +642 -642
- package/src/components/POSProductCard/POSProductCard.tsx +208 -208
- package/src/components/POSProductCard/POSProductCard.types.ts +76 -76
- package/src/components/POSProductCard/README.md +179 -179
- package/src/components/POSProductCard/icons.tsx +26 -26
- package/src/components/POSProductCard/index.ts +2 -2
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +753 -753
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +332 -332
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +119 -119
- package/src/components/POSProductSidebarItems/README.md +198 -198
- package/src/components/POSProductSidebarItems/icons.tsx +21 -21
- package/src/components/POSProductSidebarItems/index.ts +3 -3
- package/src/components/POSTable/POSTable.stories.tsx +737 -737
- package/src/components/POSTable/POSTable.tsx +401 -401
- package/src/components/POSTable/README.md +286 -286
- package/src/components/Quantity/Quantity.stories.tsx +457 -457
- package/src/components/Radio/Radio.stories.tsx +523 -523
- package/src/components/Radio/Radio.tsx +1 -1
- package/src/components/Select/Select.stories.tsx +32 -0
- package/src/components/Select/Select.tsx +457 -454
- package/src/components/Select/icons.tsx +16 -41
- package/src/components/SignUpView/SignUpView.stories.tsx +129 -129
- package/src/components/SignUpView/SignUpView.tsx +503 -503
- package/src/components/SignUpView/SignUpView.types.ts +58 -58
- package/src/components/SignUpView/icons.tsx +71 -71
- package/src/components/SignUpView/index.ts +3 -3
- package/src/components/Switch/README.md +112 -112
- package/src/components/Switch/Switch.stories.tsx +550 -550
- package/src/components/Switch/Switch.tsx +246 -246
- package/src/components/Switch/Switch.types.ts +67 -67
- package/src/components/Table/Table.stories.tsx +805 -805
- package/src/components/Tabs/README.md +201 -201
- package/src/components/Tabs/Tabs.stories.tsx +580 -580
- package/src/components/Tabs/Tabs.tsx +356 -356
- package/src/components/Tabs/Tabs.types.ts +127 -127
- package/src/components/Tabs/icons.tsx +129 -129
- package/src/components/Tabs/index.ts +11 -11
- package/src/components/Textarea/Textarea.stories.tsx +535 -535
- package/src/index.ts +133 -102
- package/src/views/LayoutCommercial/LayoutCommercial.stories.tsx +374 -0
- package/src/views/LayoutCommercial/LayoutCommercial.tsx +125 -0
- package/src/views/LayoutCommercial/LayoutCommercial.types.ts +54 -0
- package/src/views/LayoutCommercial/README.md +286 -0
- package/src/views/LayoutCommercial/index.ts +2 -0
- package/src/views/ListView/ListView.stories.tsx +329 -329
- package/src/views/ListView/ListView.tsx +570 -570
- package/src/views/ListView/ListView.types.ts +211 -211
- package/src/views/ListView/icons.tsx +282 -282
- package/src/views/ListView/index.ts +11 -11
- package/src/views/LoginView/LoginView.tsx +426 -426
- package/src/views/ProductsView/ProductsView.stories.tsx +344 -344
- package/src/views/ProductsView/ProductsView.tsx +480 -480
- package/src/views/ProductsView/ProductsView.types.ts +238 -238
- package/src/views/ProductsView/README.md +312 -312
- package/src/views/ProductsView/icons.tsx +38 -38
- package/src/views/ProductsView/index.ts +8 -8
- package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +376 -376
- package/src/views/SignUpView/SignUpView.tsx +503 -503
- package/src/views/TableLayoutView/README.md +268 -268
- package/src/views/TableLayoutView/TableLayoutView.stories.tsx +235 -235
- package/src/views/TableLayoutView/TableLayoutView.tsx +461 -461
- package/src/views/TableLayoutView/TableLayoutView.types.ts +209 -209
- package/src/views/TableLayoutView/icons.tsx +113 -113
- package/src/views/TableLayoutView/index.ts +6 -6
- package/storybook/main.ts +19 -19
- package/storybook/preview.tsx +84 -84
- package/storybook/vitest.setup.ts +6 -6
- package/tailwind.config.js +128 -128
|
@@ -1,246 +1,246 @@
|
|
|
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-
|
|
107
|
-
dark:bg-primary-custom-600
|
|
108
|
-
dark:border-primary-inverse-border
|
|
109
|
-
dark:peer-hover:border-[#307cc5]
|
|
110
|
-
dark:peer-focus:shadow-
|
|
111
|
-
`
|
|
112
|
-
: `
|
|
113
|
-
bg-[#fafafa]
|
|
114
|
-
border-border-primary
|
|
115
|
-
cursor-pointer
|
|
116
|
-
peer-hover:border-[#b6b6b9]
|
|
117
|
-
peer-focus:shadow-
|
|
118
|
-
dark:bg-dark-bg-primary
|
|
119
|
-
dark:border-dark-border-primary
|
|
120
|
-
dark:peer-hover:border-[#b6b6b9]
|
|
121
|
-
dark:peer-focus:shadow-
|
|
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
|
+
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-lg
|
|
107
|
+
dark:bg-primary-custom-600
|
|
108
|
+
dark:border-primary-inverse-border
|
|
109
|
+
dark:peer-hover:border-[#307cc5]
|
|
110
|
+
dark:peer-focus:shadow-lg
|
|
111
|
+
`
|
|
112
|
+
: `
|
|
113
|
+
bg-[#fafafa]
|
|
114
|
+
border-border-primary
|
|
115
|
+
cursor-pointer
|
|
116
|
+
peer-hover:border-[#b6b6b9]
|
|
117
|
+
peer-focus:shadow-lg
|
|
118
|
+
dark:bg-dark-bg-primary
|
|
119
|
+
dark:border-dark-border-primary
|
|
120
|
+
dark:peer-hover:border-[#b6b6b9]
|
|
121
|
+
dark:peer-focus:shadow-lg
|
|
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 +1,67 @@
|
|
|
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
|
+
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
|
+
}
|