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,337 +1,337 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { ButtonProps } from './Button.types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Componente Button del sistema de diseño Siesa
|
|
6
|
-
*
|
|
7
|
-
* Implementación pixel-perfect basada en Figma (node 4001-17240)
|
|
8
|
-
* con soporte completo para todos los estados, tamaños y variantes.
|
|
9
|
-
*
|
|
10
|
-
* **Variantes (type):**
|
|
11
|
-
* - `default`: Botón primario con fondo sólido (#0e79fd) y borde (#3c9bf6)
|
|
12
|
-
* - Sombra interna para efecto de profundidad
|
|
13
|
-
* - Usar para acciones principales (Guardar, Enviar, Confirmar)
|
|
14
|
-
* - `outline`: Botón secundario con borde (#93d1fd) y shadow-sm
|
|
15
|
-
* - Usar para acciones secundarias (Cancelar, Volver)
|
|
16
|
-
* - `plain`: Botón terciario sin borde visible
|
|
17
|
-
* - Hover overlay sutil
|
|
18
|
-
* - Usar para acciones sutiles (Cerrar, Ver más, Links)
|
|
19
|
-
*
|
|
20
|
-
* **Tamaños (size):**
|
|
21
|
-
* - `xs` (24px): Espacios muy compactos, inline actions. Padding: 8px h, 4px v
|
|
22
|
-
* - `sm` (28px): Barras de herramientas, acciones secundarias. Padding: 8px h, 4px v
|
|
23
|
-
* - `base` (32px): Tamaño estándar para la mayoría de casos. Padding: 10px h, 6px v
|
|
24
|
-
* - `l` (36px): Botones destacados, CTAs. Padding: 12px h, 8px v
|
|
25
|
-
* - `xl` (40px): Heroes, landing pages. Padding: 16px h, 8px v
|
|
26
|
-
*
|
|
27
|
-
* **Estados:**
|
|
28
|
-
* - `default`: Estado normal con colores base
|
|
29
|
-
* - `hover`: Overlay visual sutil (bg-primary-custom-500 para default)
|
|
30
|
-
* - `focus`: Focus ring de 4px (#60b6fa) con offset de 2px (#dbeefe)
|
|
31
|
-
* - `active`: Scale animation (scale-95) para feedback táctil
|
|
32
|
-
* - `disabled`: Opacity 50% con pointer-events-none
|
|
33
|
-
*
|
|
34
|
-
* **Badges de notificación:**
|
|
35
|
-
* - `badge`: Muestra un dot de notificación en la esquina superior derecha
|
|
36
|
-
* - `badgeCount`: Muestra un badge con número (99+ para >99)
|
|
37
|
-
* - `badgeColor`: Color del badge (por defecto: red - #b91c1c)
|
|
38
|
-
*
|
|
39
|
-
* **Especificaciones de Figma:**
|
|
40
|
-
* - Border radius: 6px (rounded-md)
|
|
41
|
-
* - Tipografía: Label Small (14px Bold) para sm/base/l/xl, Label Tiny (12px Bold) para xs
|
|
42
|
-
* - Iconos: 16x16px en todos los tamaños
|
|
43
|
-
* - Gap entre elementos: 8px (xs/sm/base), 12px (l/xl)
|
|
44
|
-
*
|
|
45
|
-
* **Dark Mode:**
|
|
46
|
-
* Los botones invierten colores en dark mode:
|
|
47
|
-
* - Default: fondo celeste claro (#bfe2fe), texto azul (#0e79fd), borde celeste (#93d1fd)
|
|
48
|
-
* - Outline: texto celeste (#93d1fd), borde azul (#0f6ae3)
|
|
49
|
-
* - Plain: texto celeste (#93d1fd), hover overlay blanco 20%
|
|
50
|
-
* - Focus ring adaptativo con offset oscuro
|
|
51
|
-
*
|
|
52
|
-
* @see docs/colors.md - Sistema de colores
|
|
53
|
-
* @see docs/shadows.md - Sistema de sombras (shadow-button-inset, shadow-sm)
|
|
54
|
-
* @see docs/typography.md - Sistema tipográfico (Label Small/Tiny)
|
|
55
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
56
|
-
* @see https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4001-17240 - Diseño Figma
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```tsx
|
|
60
|
-
* // Botón primario con icono
|
|
61
|
-
* <Button type="default" size="base" leftIcon={<SaveIcon />}>
|
|
62
|
-
* Guardar
|
|
63
|
-
* </Button>
|
|
64
|
-
*
|
|
65
|
-
* // Botón secundario
|
|
66
|
-
* <Button type="outline" size="base">
|
|
67
|
-
* Cancelar
|
|
68
|
-
* </Button>
|
|
69
|
-
*
|
|
70
|
-
* // Botón solo icono para barra de herramientas
|
|
71
|
-
* <Button type="plain" size="sm" iconOnly leftIcon={<CloseIcon />} ariaLabel="Cerrar" />
|
|
72
|
-
*
|
|
73
|
-
* // Botón con badge de notificación (dot)
|
|
74
|
-
* <Button type="default" size="base" badge>
|
|
75
|
-
* Notificaciones
|
|
76
|
-
* </Button>
|
|
77
|
-
*
|
|
78
|
-
* // Botón con badge contador
|
|
79
|
-
* <Button type="default" size="base" badgeCount={5} badgeColor="red">
|
|
80
|
-
* Mensajes
|
|
81
|
-
* </Button>
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
export const Button: React.FC<ButtonProps> = ({
|
|
85
|
-
type = 'default',
|
|
86
|
-
size = 'base',
|
|
87
|
-
iconOnly = false,
|
|
88
|
-
leftIcon,
|
|
89
|
-
rightIcon,
|
|
90
|
-
children,
|
|
91
|
-
disabled = false,
|
|
92
|
-
className = '',
|
|
93
|
-
onClick,
|
|
94
|
-
htmlType = 'button',
|
|
95
|
-
fullWidth = false,
|
|
96
|
-
ariaLabel,
|
|
97
|
-
badge = false,
|
|
98
|
-
badgeCount,
|
|
99
|
-
badgeColor = 'red',
|
|
100
|
-
...rest
|
|
101
|
-
}) => {
|
|
102
|
-
// ===== CLASES DE TAMAÑO =====
|
|
103
|
-
const sizeClasses = {
|
|
104
|
-
xs: iconOnly ? 'h-6 w-6 p-1' : 'h-6 py-1 px-2 gap-2',
|
|
105
|
-
sm: iconOnly ? 'h-7 w-7 p-1.5' : 'h-7 py-1 px-2 gap-2',
|
|
106
|
-
base: iconOnly ? 'h-8 w-8 p-2' : 'h-8 py-1.5 px-2.5 gap-2',
|
|
107
|
-
l: iconOnly ? 'h-9 w-9 p-2.5' : 'h-9 py-2 px-3 gap-3',
|
|
108
|
-
xl: iconOnly ? 'h-10 w-10 p-3' : 'h-10 py-2 px-4 gap-3',
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// ===== CLASES DE TAMAÑO DE ICONO =====
|
|
112
|
-
const iconSizeClasses = {
|
|
113
|
-
xs: 'w-4 h-4',
|
|
114
|
-
sm: 'w-4 h-4',
|
|
115
|
-
base: 'w-4 h-4',
|
|
116
|
-
l: 'w-4 h-4',
|
|
117
|
-
xl: 'w-4 h-4',
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// ===== CLASES DE TAMAÑO DE TEXTO =====
|
|
121
|
-
// Usando el sistema de tipografía Label del design system (typography.md)
|
|
122
|
-
const textSizeClasses = {
|
|
123
|
-
xs: 'text-xs', // Label Tiny - 12px
|
|
124
|
-
sm: 'text-sm', // Label Small - 14px
|
|
125
|
-
base: 'text-sm', // Label Small - 14px (default para base)
|
|
126
|
-
l: 'text-sm', // Label Small - 14px (corregido según Figma)
|
|
127
|
-
xl: 'text-sm', // Label Small - 14px (corregido según Figma)
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// ===== CLASES DE TIPO (Default, Outline, Plain) =====
|
|
131
|
-
// Especificaciones de Figma node 4001-17240
|
|
132
|
-
// Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
133
|
-
// Dark mode: Los botones invierten colores (fondo claro, texto oscuro)
|
|
134
|
-
// - Light: fondo #0e79fd (azul), texto #eff8ff (blanco)
|
|
135
|
-
// - Dark: fondo #bfe2fe (celeste claro), texto #0e79fd (azul)
|
|
136
|
-
const typeClasses = {
|
|
137
|
-
default: `
|
|
138
|
-
bg-primary-custom-600
|
|
139
|
-
text-primary-inverse-content
|
|
140
|
-
border
|
|
141
|
-
border-primary-inverse-border
|
|
142
|
-
shadow-button-inset
|
|
143
|
-
hover:bg-primary-custom-500
|
|
144
|
-
active:scale-95
|
|
145
|
-
transition-all
|
|
146
|
-
duration-150
|
|
147
|
-
dark:bg-dark-bg-inverse
|
|
148
|
-
dark:text-dark-content-inverse
|
|
149
|
-
dark:border-dark-border-inverse
|
|
150
|
-
dark:hover:bg-dark-bg-inverse/90
|
|
151
|
-
`,
|
|
152
|
-
outline: `
|
|
153
|
-
bg-transparent
|
|
154
|
-
text-primary-custom-600
|
|
155
|
-
border
|
|
156
|
-
border-primary-custom-300
|
|
157
|
-
shadow-sm
|
|
158
|
-
hover:bg-primary-custom-100
|
|
159
|
-
active:scale-95
|
|
160
|
-
transition-all
|
|
161
|
-
duration-150
|
|
162
|
-
dark:text-dark-content-custom
|
|
163
|
-
dark:border-dark-border-custom
|
|
164
|
-
dark:hover:bg-dark-bg-custom/20
|
|
165
|
-
`,
|
|
166
|
-
plain: `
|
|
167
|
-
bg-transparent
|
|
168
|
-
text-primary-custom-600
|
|
169
|
-
border
|
|
170
|
-
border-transparent
|
|
171
|
-
hover:bg-hover-overlay
|
|
172
|
-
active:scale-95
|
|
173
|
-
transition-all
|
|
174
|
-
duration-150
|
|
175
|
-
dark:text-dark-content-custom
|
|
176
|
-
dark:hover:bg-hover-overlay-dark
|
|
177
|
-
`,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// ===== CLASES BASE =====
|
|
181
|
-
// Especificaciones de Figma: Focus ring = 4px spread primary-custom-400 + 2px offset primary-custom-100
|
|
182
|
-
// Dark mode: Focus ring adaptativo con offset oscuro
|
|
183
|
-
const baseClasses = `
|
|
184
|
-
inline-flex
|
|
185
|
-
items-center
|
|
186
|
-
justify-center
|
|
187
|
-
rounded-md
|
|
188
|
-
font-bold
|
|
189
|
-
whitespace-nowrap
|
|
190
|
-
focus:outline-none
|
|
191
|
-
focus:ring-4
|
|
192
|
-
focus:ring-primary-custom-400
|
|
193
|
-
focus:ring-offset-2
|
|
194
|
-
focus:ring-offset-primary-custom-100
|
|
195
|
-
dark:focus:ring-dark-border-custom
|
|
196
|
-
dark:focus:ring-offset-dark-bg-primary
|
|
197
|
-
disabled:opacity-50
|
|
198
|
-
disabled:cursor-not-allowed
|
|
199
|
-
disabled:pointer-events-none
|
|
200
|
-
`;
|
|
201
|
-
|
|
202
|
-
// ===== CLASE FULL WIDTH =====
|
|
203
|
-
const widthClass = fullWidth ? 'w-full' : '';
|
|
204
|
-
|
|
205
|
-
// ===== COMBINAR TODAS LAS CLASES =====
|
|
206
|
-
const buttonClasses = [
|
|
207
|
-
baseClasses,
|
|
208
|
-
sizeClasses[size],
|
|
209
|
-
typeClasses[type],
|
|
210
|
-
widthClass,
|
|
211
|
-
className,
|
|
212
|
-
]
|
|
213
|
-
.join(' ')
|
|
214
|
-
.replace(/\s+/g, ' ')
|
|
215
|
-
.trim();
|
|
216
|
-
|
|
217
|
-
// ===== RENDERIZAR ICONO =====
|
|
218
|
-
const renderIcon = (icon: React.ReactNode) => {
|
|
219
|
-
if (!icon) return null;
|
|
220
|
-
return (
|
|
221
|
-
<span className={`inline-flex items-center justify-center ${iconSizeClasses[size]}`}>
|
|
222
|
-
{icon}
|
|
223
|
-
</span>
|
|
224
|
-
);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// ===== RENDERIZAR CONTENIDO =====
|
|
228
|
-
const renderContent = () => {
|
|
229
|
-
// Si es iconOnly, solo mostrar leftIcon
|
|
230
|
-
if (iconOnly) {
|
|
231
|
-
return renderIcon(leftIcon);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Si tiene texto y/o iconos
|
|
235
|
-
return (
|
|
236
|
-
<>
|
|
237
|
-
{leftIcon && renderIcon(leftIcon)}
|
|
238
|
-
{children && <span className={textSizeClasses[size]}>{children}</span>}
|
|
239
|
-
{rightIcon && renderIcon(rightIcon)}
|
|
240
|
-
</>
|
|
241
|
-
);
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
// ===== MAPA DE COLORES DE BADGE =====
|
|
245
|
-
// Basado en el componente Badge existente
|
|
246
|
-
const badgeColorClasses: Record<string, { bg: string; text: string }> = {
|
|
247
|
-
zinc: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
248
|
-
red: { bg: 'bg-red-700', text: 'text-white' },
|
|
249
|
-
orange: { bg: 'bg-orange-700', text: 'text-white' },
|
|
250
|
-
amber: { bg: 'bg-amber-700', text: 'text-white' },
|
|
251
|
-
yellow: { bg: 'bg-yellow-700', text: 'text-white' },
|
|
252
|
-
lime: { bg: 'bg-lime-700', text: 'text-white' },
|
|
253
|
-
green: { bg: 'bg-green-700', text: 'text-white' },
|
|
254
|
-
emerald: { bg: 'bg-emerald-700', text: 'text-white' },
|
|
255
|
-
teal: { bg: 'bg-teal-700', text: 'text-white' },
|
|
256
|
-
cyan: { bg: 'bg-cyan-700', text: 'text-white' },
|
|
257
|
-
sky: { bg: 'bg-sky-700', text: 'text-white' },
|
|
258
|
-
blue: { bg: 'bg-blue-700', text: 'text-white' },
|
|
259
|
-
indigo: { bg: 'bg-indigo-700', text: 'text-white' },
|
|
260
|
-
violet: { bg: 'bg-violet-700', text: 'text-white' },
|
|
261
|
-
purple: { bg: 'bg-purple-700', text: 'text-white' },
|
|
262
|
-
fuchsia: { bg: 'bg-fuchsia-700', text: 'text-white' },
|
|
263
|
-
pink: { bg: 'bg-pink-700', text: 'text-white' },
|
|
264
|
-
rose: { bg: 'bg-rose-700', text: 'text-white' },
|
|
265
|
-
primary: { bg: 'bg-primary-custom-600', text: 'text-white' },
|
|
266
|
-
secondary: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
267
|
-
tertiary: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const badgeColors = badgeColorClasses[badgeColor] || badgeColorClasses.red;
|
|
271
|
-
|
|
272
|
-
// ===== RENDERIZAR BADGE =====
|
|
273
|
-
const renderBadge = () => {
|
|
274
|
-
// Si no hay badge ni badgeCount, no renderizar nada
|
|
275
|
-
if (!badge && badgeCount === undefined) return null;
|
|
276
|
-
|
|
277
|
-
// Si hay badgeCount, renderizar badge con número
|
|
278
|
-
if (badgeCount !== undefined) {
|
|
279
|
-
return (
|
|
280
|
-
<span
|
|
281
|
-
className={`
|
|
282
|
-
absolute
|
|
283
|
-
-top-1
|
|
284
|
-
-right-1
|
|
285
|
-
flex
|
|
286
|
-
items-center
|
|
287
|
-
justify-center
|
|
288
|
-
min-w-[16px]
|
|
289
|
-
h-4
|
|
290
|
-
px-1
|
|
291
|
-
rounded-full
|
|
292
|
-
text-[10px]
|
|
293
|
-
font-bold
|
|
294
|
-
leading-none
|
|
295
|
-
${badgeColors.bg}
|
|
296
|
-
${badgeColors.text}
|
|
297
|
-
pointer-events-none
|
|
298
|
-
`.trim().replace(/\s+/g, ' ')}
|
|
299
|
-
aria-label={`${badgeCount} notificaciones`}
|
|
300
|
-
>
|
|
301
|
-
{badgeCount > 99 ? '99+' : badgeCount}
|
|
302
|
-
</span>
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Si solo hay badge (sin número), renderizar dot
|
|
307
|
-
return (
|
|
308
|
-
<span
|
|
309
|
-
className={`
|
|
310
|
-
absolute
|
|
311
|
-
-top-1
|
|
312
|
-
-right-1
|
|
313
|
-
w-2
|
|
314
|
-
h-2
|
|
315
|
-
rounded-full
|
|
316
|
-
${badgeColors.bg}
|
|
317
|
-
pointer-events-none
|
|
318
|
-
`.trim().replace(/\s+/g, ' ')}
|
|
319
|
-
aria-label="Notificación"
|
|
320
|
-
/>
|
|
321
|
-
);
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
return (
|
|
325
|
-
<button
|
|
326
|
-
type={htmlType}
|
|
327
|
-
className={`${buttonClasses} ${(badge || badgeCount !== undefined) ? 'relative' : ''}`}
|
|
328
|
-
disabled={disabled}
|
|
329
|
-
onClick={onClick}
|
|
330
|
-
aria-label={ariaLabel}
|
|
331
|
-
{...rest}
|
|
332
|
-
>
|
|
333
|
-
{renderContent()}
|
|
334
|
-
{renderBadge()}
|
|
335
|
-
</button>
|
|
336
|
-
);
|
|
337
|
-
};
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ButtonProps } from './Button.types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Componente Button del sistema de diseño Siesa
|
|
6
|
+
*
|
|
7
|
+
* Implementación pixel-perfect basada en Figma (node 4001-17240)
|
|
8
|
+
* con soporte completo para todos los estados, tamaños y variantes.
|
|
9
|
+
*
|
|
10
|
+
* **Variantes (type):**
|
|
11
|
+
* - `default`: Botón primario con fondo sólido (#0e79fd) y borde (#3c9bf6)
|
|
12
|
+
* - Sombra interna para efecto de profundidad
|
|
13
|
+
* - Usar para acciones principales (Guardar, Enviar, Confirmar)
|
|
14
|
+
* - `outline`: Botón secundario con borde (#93d1fd) y shadow-sm
|
|
15
|
+
* - Usar para acciones secundarias (Cancelar, Volver)
|
|
16
|
+
* - `plain`: Botón terciario sin borde visible
|
|
17
|
+
* - Hover overlay sutil
|
|
18
|
+
* - Usar para acciones sutiles (Cerrar, Ver más, Links)
|
|
19
|
+
*
|
|
20
|
+
* **Tamaños (size):**
|
|
21
|
+
* - `xs` (24px): Espacios muy compactos, inline actions. Padding: 8px h, 4px v
|
|
22
|
+
* - `sm` (28px): Barras de herramientas, acciones secundarias. Padding: 8px h, 4px v
|
|
23
|
+
* - `base` (32px): Tamaño estándar para la mayoría de casos. Padding: 10px h, 6px v
|
|
24
|
+
* - `l` (36px): Botones destacados, CTAs. Padding: 12px h, 8px v
|
|
25
|
+
* - `xl` (40px): Heroes, landing pages. Padding: 16px h, 8px v
|
|
26
|
+
*
|
|
27
|
+
* **Estados:**
|
|
28
|
+
* - `default`: Estado normal con colores base
|
|
29
|
+
* - `hover`: Overlay visual sutil (bg-primary-custom-500 para default)
|
|
30
|
+
* - `focus`: Focus ring de 4px (#60b6fa) con offset de 2px (#dbeefe)
|
|
31
|
+
* - `active`: Scale animation (scale-95) para feedback táctil
|
|
32
|
+
* - `disabled`: Opacity 50% con pointer-events-none
|
|
33
|
+
*
|
|
34
|
+
* **Badges de notificación:**
|
|
35
|
+
* - `badge`: Muestra un dot de notificación en la esquina superior derecha
|
|
36
|
+
* - `badgeCount`: Muestra un badge con número (99+ para >99)
|
|
37
|
+
* - `badgeColor`: Color del badge (por defecto: red - #b91c1c)
|
|
38
|
+
*
|
|
39
|
+
* **Especificaciones de Figma:**
|
|
40
|
+
* - Border radius: 6px (rounded-md)
|
|
41
|
+
* - Tipografía: Label Small (14px Bold) para sm/base/l/xl, Label Tiny (12px Bold) para xs
|
|
42
|
+
* - Iconos: 16x16px en todos los tamaños
|
|
43
|
+
* - Gap entre elementos: 8px (xs/sm/base), 12px (l/xl)
|
|
44
|
+
*
|
|
45
|
+
* **Dark Mode:**
|
|
46
|
+
* Los botones invierten colores en dark mode:
|
|
47
|
+
* - Default: fondo celeste claro (#bfe2fe), texto azul (#0e79fd), borde celeste (#93d1fd)
|
|
48
|
+
* - Outline: texto celeste (#93d1fd), borde azul (#0f6ae3)
|
|
49
|
+
* - Plain: texto celeste (#93d1fd), hover overlay blanco 20%
|
|
50
|
+
* - Focus ring adaptativo con offset oscuro
|
|
51
|
+
*
|
|
52
|
+
* @see docs/colors.md - Sistema de colores
|
|
53
|
+
* @see docs/shadows.md - Sistema de sombras (shadow-button-inset, shadow-sm)
|
|
54
|
+
* @see docs/typography.md - Sistema tipográfico (Label Small/Tiny)
|
|
55
|
+
* @see docs/spacing.md - Sistema de espaciado
|
|
56
|
+
* @see https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4001-17240 - Diseño Figma
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* // Botón primario con icono
|
|
61
|
+
* <Button type="default" size="base" leftIcon={<SaveIcon />}>
|
|
62
|
+
* Guardar
|
|
63
|
+
* </Button>
|
|
64
|
+
*
|
|
65
|
+
* // Botón secundario
|
|
66
|
+
* <Button type="outline" size="base">
|
|
67
|
+
* Cancelar
|
|
68
|
+
* </Button>
|
|
69
|
+
*
|
|
70
|
+
* // Botón solo icono para barra de herramientas
|
|
71
|
+
* <Button type="plain" size="sm" iconOnly leftIcon={<CloseIcon />} ariaLabel="Cerrar" />
|
|
72
|
+
*
|
|
73
|
+
* // Botón con badge de notificación (dot)
|
|
74
|
+
* <Button type="default" size="base" badge>
|
|
75
|
+
* Notificaciones
|
|
76
|
+
* </Button>
|
|
77
|
+
*
|
|
78
|
+
* // Botón con badge contador
|
|
79
|
+
* <Button type="default" size="base" badgeCount={5} badgeColor="red">
|
|
80
|
+
* Mensajes
|
|
81
|
+
* </Button>
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const Button: React.FC<ButtonProps> = ({
|
|
85
|
+
type = 'default',
|
|
86
|
+
size = 'base',
|
|
87
|
+
iconOnly = false,
|
|
88
|
+
leftIcon,
|
|
89
|
+
rightIcon,
|
|
90
|
+
children,
|
|
91
|
+
disabled = false,
|
|
92
|
+
className = '',
|
|
93
|
+
onClick,
|
|
94
|
+
htmlType = 'button',
|
|
95
|
+
fullWidth = false,
|
|
96
|
+
ariaLabel,
|
|
97
|
+
badge = false,
|
|
98
|
+
badgeCount,
|
|
99
|
+
badgeColor = 'red',
|
|
100
|
+
...rest
|
|
101
|
+
}) => {
|
|
102
|
+
// ===== CLASES DE TAMAÑO =====
|
|
103
|
+
const sizeClasses = {
|
|
104
|
+
xs: iconOnly ? 'h-6 w-6 p-1' : 'h-6 py-1 px-2 gap-2',
|
|
105
|
+
sm: iconOnly ? 'h-7 w-7 p-1.5' : 'h-7 py-1 px-2 gap-2',
|
|
106
|
+
base: iconOnly ? 'h-8 w-8 p-2' : 'h-8 py-1.5 px-2.5 gap-2',
|
|
107
|
+
l: iconOnly ? 'h-9 w-9 p-2.5' : 'h-9 py-2 px-3 gap-3',
|
|
108
|
+
xl: iconOnly ? 'h-10 w-10 p-3' : 'h-10 py-2 px-4 gap-3',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ===== CLASES DE TAMAÑO DE ICONO =====
|
|
112
|
+
const iconSizeClasses = {
|
|
113
|
+
xs: 'w-4 h-4',
|
|
114
|
+
sm: 'w-4 h-4',
|
|
115
|
+
base: 'w-4 h-4',
|
|
116
|
+
l: 'w-4 h-4',
|
|
117
|
+
xl: 'w-4 h-4',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// ===== CLASES DE TAMAÑO DE TEXTO =====
|
|
121
|
+
// Usando el sistema de tipografía Label del design system (typography.md)
|
|
122
|
+
const textSizeClasses = {
|
|
123
|
+
xs: 'text-xs', // Label Tiny - 12px
|
|
124
|
+
sm: 'text-sm', // Label Small - 14px
|
|
125
|
+
base: 'text-sm', // Label Small - 14px (default para base)
|
|
126
|
+
l: 'text-sm', // Label Small - 14px (corregido según Figma)
|
|
127
|
+
xl: 'text-sm', // Label Small - 14px (corregido según Figma)
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// ===== CLASES DE TIPO (Default, Outline, Plain) =====
|
|
131
|
+
// Especificaciones de Figma node 4001-17240
|
|
132
|
+
// Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
133
|
+
// Dark mode: Los botones invierten colores (fondo claro, texto oscuro)
|
|
134
|
+
// - Light: fondo #0e79fd (azul), texto #eff8ff (blanco)
|
|
135
|
+
// - Dark: fondo #bfe2fe (celeste claro), texto #0e79fd (azul)
|
|
136
|
+
const typeClasses = {
|
|
137
|
+
default: `
|
|
138
|
+
bg-primary-custom-600
|
|
139
|
+
text-primary-inverse-content
|
|
140
|
+
border
|
|
141
|
+
border-primary-inverse-border
|
|
142
|
+
shadow-button-inset
|
|
143
|
+
hover:bg-primary-custom-500
|
|
144
|
+
active:scale-95
|
|
145
|
+
transition-all
|
|
146
|
+
duration-150
|
|
147
|
+
dark:bg-dark-bg-inverse
|
|
148
|
+
dark:text-dark-content-inverse
|
|
149
|
+
dark:border-dark-border-inverse
|
|
150
|
+
dark:hover:bg-dark-bg-inverse/90
|
|
151
|
+
`,
|
|
152
|
+
outline: `
|
|
153
|
+
bg-transparent
|
|
154
|
+
text-primary-custom-600
|
|
155
|
+
border
|
|
156
|
+
border-primary-custom-300
|
|
157
|
+
shadow-sm
|
|
158
|
+
hover:bg-primary-custom-100
|
|
159
|
+
active:scale-95
|
|
160
|
+
transition-all
|
|
161
|
+
duration-150
|
|
162
|
+
dark:text-dark-content-custom
|
|
163
|
+
dark:border-dark-border-custom
|
|
164
|
+
dark:hover:bg-dark-bg-custom/20
|
|
165
|
+
`,
|
|
166
|
+
plain: `
|
|
167
|
+
bg-transparent
|
|
168
|
+
text-primary-custom-600
|
|
169
|
+
border
|
|
170
|
+
border-transparent
|
|
171
|
+
hover:bg-hover-overlay
|
|
172
|
+
active:scale-95
|
|
173
|
+
transition-all
|
|
174
|
+
duration-150
|
|
175
|
+
dark:text-dark-content-custom
|
|
176
|
+
dark:hover:bg-hover-overlay-dark
|
|
177
|
+
`,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// ===== CLASES BASE =====
|
|
181
|
+
// Especificaciones de Figma: Focus ring = 4px spread primary-custom-400 + 2px offset primary-custom-100
|
|
182
|
+
// Dark mode: Focus ring adaptativo con offset oscuro
|
|
183
|
+
const baseClasses = `
|
|
184
|
+
inline-flex
|
|
185
|
+
items-center
|
|
186
|
+
justify-center
|
|
187
|
+
rounded-md
|
|
188
|
+
font-bold
|
|
189
|
+
whitespace-nowrap
|
|
190
|
+
focus:outline-none
|
|
191
|
+
focus:ring-4
|
|
192
|
+
focus:ring-primary-custom-400
|
|
193
|
+
focus:ring-offset-2
|
|
194
|
+
focus:ring-offset-primary-custom-100
|
|
195
|
+
dark:focus:ring-dark-border-custom
|
|
196
|
+
dark:focus:ring-offset-dark-bg-primary
|
|
197
|
+
disabled:opacity-50
|
|
198
|
+
disabled:cursor-not-allowed
|
|
199
|
+
disabled:pointer-events-none
|
|
200
|
+
`;
|
|
201
|
+
|
|
202
|
+
// ===== CLASE FULL WIDTH =====
|
|
203
|
+
const widthClass = fullWidth ? 'w-full' : '';
|
|
204
|
+
|
|
205
|
+
// ===== COMBINAR TODAS LAS CLASES =====
|
|
206
|
+
const buttonClasses = [
|
|
207
|
+
baseClasses,
|
|
208
|
+
sizeClasses[size],
|
|
209
|
+
typeClasses[type],
|
|
210
|
+
widthClass,
|
|
211
|
+
className,
|
|
212
|
+
]
|
|
213
|
+
.join(' ')
|
|
214
|
+
.replace(/\s+/g, ' ')
|
|
215
|
+
.trim();
|
|
216
|
+
|
|
217
|
+
// ===== RENDERIZAR ICONO =====
|
|
218
|
+
const renderIcon = (icon: React.ReactNode) => {
|
|
219
|
+
if (!icon) return null;
|
|
220
|
+
return (
|
|
221
|
+
<span className={`inline-flex items-center justify-center ${iconSizeClasses[size]}`}>
|
|
222
|
+
{icon}
|
|
223
|
+
</span>
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// ===== RENDERIZAR CONTENIDO =====
|
|
228
|
+
const renderContent = () => {
|
|
229
|
+
// Si es iconOnly, solo mostrar leftIcon
|
|
230
|
+
if (iconOnly) {
|
|
231
|
+
return renderIcon(leftIcon);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Si tiene texto y/o iconos
|
|
235
|
+
return (
|
|
236
|
+
<>
|
|
237
|
+
{leftIcon && renderIcon(leftIcon)}
|
|
238
|
+
{children && <span className={textSizeClasses[size]}>{children}</span>}
|
|
239
|
+
{rightIcon && renderIcon(rightIcon)}
|
|
240
|
+
</>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// ===== MAPA DE COLORES DE BADGE =====
|
|
245
|
+
// Basado en el componente Badge existente
|
|
246
|
+
const badgeColorClasses: Record<string, { bg: string; text: string }> = {
|
|
247
|
+
zinc: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
248
|
+
red: { bg: 'bg-red-700', text: 'text-white' },
|
|
249
|
+
orange: { bg: 'bg-orange-700', text: 'text-white' },
|
|
250
|
+
amber: { bg: 'bg-amber-700', text: 'text-white' },
|
|
251
|
+
yellow: { bg: 'bg-yellow-700', text: 'text-white' },
|
|
252
|
+
lime: { bg: 'bg-lime-700', text: 'text-white' },
|
|
253
|
+
green: { bg: 'bg-green-700', text: 'text-white' },
|
|
254
|
+
emerald: { bg: 'bg-emerald-700', text: 'text-white' },
|
|
255
|
+
teal: { bg: 'bg-teal-700', text: 'text-white' },
|
|
256
|
+
cyan: { bg: 'bg-cyan-700', text: 'text-white' },
|
|
257
|
+
sky: { bg: 'bg-sky-700', text: 'text-white' },
|
|
258
|
+
blue: { bg: 'bg-blue-700', text: 'text-white' },
|
|
259
|
+
indigo: { bg: 'bg-indigo-700', text: 'text-white' },
|
|
260
|
+
violet: { bg: 'bg-violet-700', text: 'text-white' },
|
|
261
|
+
purple: { bg: 'bg-purple-700', text: 'text-white' },
|
|
262
|
+
fuchsia: { bg: 'bg-fuchsia-700', text: 'text-white' },
|
|
263
|
+
pink: { bg: 'bg-pink-700', text: 'text-white' },
|
|
264
|
+
rose: { bg: 'bg-rose-700', text: 'text-white' },
|
|
265
|
+
primary: { bg: 'bg-primary-custom-600', text: 'text-white' },
|
|
266
|
+
secondary: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
267
|
+
tertiary: { bg: 'bg-zinc-600', text: 'text-white' },
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const badgeColors = badgeColorClasses[badgeColor] || badgeColorClasses.red;
|
|
271
|
+
|
|
272
|
+
// ===== RENDERIZAR BADGE =====
|
|
273
|
+
const renderBadge = () => {
|
|
274
|
+
// Si no hay badge ni badgeCount, no renderizar nada
|
|
275
|
+
if (!badge && badgeCount === undefined) return null;
|
|
276
|
+
|
|
277
|
+
// Si hay badgeCount, renderizar badge con número
|
|
278
|
+
if (badgeCount !== undefined) {
|
|
279
|
+
return (
|
|
280
|
+
<span
|
|
281
|
+
className={`
|
|
282
|
+
absolute
|
|
283
|
+
-top-1
|
|
284
|
+
-right-1
|
|
285
|
+
flex
|
|
286
|
+
items-center
|
|
287
|
+
justify-center
|
|
288
|
+
min-w-[16px]
|
|
289
|
+
h-4
|
|
290
|
+
px-1
|
|
291
|
+
rounded-full
|
|
292
|
+
text-[10px]
|
|
293
|
+
font-bold
|
|
294
|
+
leading-none
|
|
295
|
+
${badgeColors.bg}
|
|
296
|
+
${badgeColors.text}
|
|
297
|
+
pointer-events-none
|
|
298
|
+
`.trim().replace(/\s+/g, ' ')}
|
|
299
|
+
aria-label={`${badgeCount} notificaciones`}
|
|
300
|
+
>
|
|
301
|
+
{badgeCount > 99 ? '99+' : badgeCount}
|
|
302
|
+
</span>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Si solo hay badge (sin número), renderizar dot
|
|
307
|
+
return (
|
|
308
|
+
<span
|
|
309
|
+
className={`
|
|
310
|
+
absolute
|
|
311
|
+
-top-1
|
|
312
|
+
-right-1
|
|
313
|
+
w-2
|
|
314
|
+
h-2
|
|
315
|
+
rounded-full
|
|
316
|
+
${badgeColors.bg}
|
|
317
|
+
pointer-events-none
|
|
318
|
+
`.trim().replace(/\s+/g, ' ')}
|
|
319
|
+
aria-label="Notificación"
|
|
320
|
+
/>
|
|
321
|
+
);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
return (
|
|
325
|
+
<button
|
|
326
|
+
type={htmlType}
|
|
327
|
+
className={`${buttonClasses} ${(badge || badgeCount !== undefined) ? 'relative' : ''}`}
|
|
328
|
+
disabled={disabled}
|
|
329
|
+
onClick={onClick}
|
|
330
|
+
aria-label={ariaLabel}
|
|
331
|
+
{...rest}
|
|
332
|
+
>
|
|
333
|
+
{renderContent()}
|
|
334
|
+
{renderBadge()}
|
|
335
|
+
</button>
|
|
336
|
+
);
|
|
337
|
+
};
|