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.
Files changed (189) hide show
  1. package/README.md +115 -115
  2. package/bin/install.cjs +502 -502
  3. package/bin/prepare-publish.cjs +28 -28
  4. package/bin/restore-folders.cjs +28 -28
  5. package/claude/agents/siesa-ui-kit-specialist.md +2445 -0
  6. package/claude/prompts/component-template.md +121 -0
  7. package/claude/prompts/siesa-ui-kit.md +28 -0
  8. package/claude/settings.local.json +67 -2
  9. package/dist/components/Button/icons.d.ts +6 -5
  10. package/dist/components/Button/icons.d.ts.map +1 -1
  11. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.d.ts.map +1 -1
  12. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts +21 -0
  13. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts.map +1 -1
  14. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts +122 -0
  15. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts.map +1 -0
  16. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts +139 -0
  17. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts.map +1 -0
  18. package/dist/components/NavigationRailCommercial/icons.d.ts +33 -0
  19. package/dist/components/NavigationRailCommercial/icons.d.ts.map +1 -0
  20. package/dist/components/NavigationRailCommercial/index.d.ts +4 -0
  21. package/dist/components/NavigationRailCommercial/index.d.ts.map +1 -0
  22. package/dist/components/NavigationRailItem/NavigationRailItem.d.ts.map +1 -1
  23. package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts +7 -0
  24. package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts.map +1 -1
  25. package/dist/components/NavigationRailTypes/NavigationRailTypes.d.ts.map +1 -1
  26. package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts +41 -0
  27. package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts.map +1 -1
  28. package/dist/components/NavigationRailTypes/icons.d.ts +15 -29
  29. package/dist/components/NavigationRailTypes/icons.d.ts.map +1 -1
  30. package/dist/components/Select/Select.d.ts.map +1 -1
  31. package/dist/components/Select/icons.d.ts +6 -2
  32. package/dist/components/Select/icons.d.ts.map +1 -1
  33. package/dist/index.d.ts +32 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/siesa-ui-kit.cjs +404 -190
  36. package/dist/siesa-ui-kit.cjs.map +1 -1
  37. package/dist/siesa-ui-kit.mjs +6590 -1506
  38. package/dist/siesa-ui-kit.mjs.map +1 -1
  39. package/dist/views/LayoutCommercial/LayoutCommercial.d.ts +48 -0
  40. package/dist/views/LayoutCommercial/LayoutCommercial.d.ts.map +1 -0
  41. package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts +49 -0
  42. package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts.map +1 -0
  43. package/dist/views/LayoutCommercial/index.d.ts +3 -0
  44. package/dist/views/LayoutCommercial/index.d.ts.map +1 -0
  45. package/docs/icons.md +12 -31
  46. package/package.json +111 -110
  47. package/src/components/Avatar/Avatar.stories.tsx +494 -494
  48. package/src/components/Button/Button.stories.tsx +950 -950
  49. package/src/components/Button/Button.tsx +337 -337
  50. package/src/components/Button/Button.types.ts +180 -180
  51. package/src/components/Button/icons.tsx +23 -62
  52. package/src/components/DescriptionList/DescriptionList.stories.tsx +250 -250
  53. package/src/components/Divider/Divider.stories.tsx +263 -263
  54. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.stories.tsx +317 -317
  55. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.tsx +307 -287
  56. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.types.ts +136 -111
  57. package/src/components/DropdownItemCollapsible/README.md +264 -264
  58. package/src/components/DropdownItemCollapsible/icons.tsx +57 -57
  59. package/src/components/DropdownItemCollapsible/index.ts +12 -12
  60. package/src/components/DropdownItemHeading/DropdownItemHeading.stories.tsx +386 -386
  61. package/src/components/DropdownItemHeading/DropdownItemHeading.tsx +216 -216
  62. package/src/components/DropdownItemHeading/DropdownItemHeading.types.ts +93 -93
  63. package/src/components/DropdownItemHeading/README.md +573 -573
  64. package/src/components/DropdownItemHeading/icons.tsx +125 -125
  65. package/src/components/DropdownItemHeading/index.ts +3 -3
  66. package/src/components/Input/Input.stories.tsx +583 -583
  67. package/src/components/LoginView/LoginView.stories.tsx +148 -148
  68. package/src/components/LoginView/LoginView.tsx +426 -426
  69. package/src/components/LoginView/LoginView.types.ts +52 -52
  70. package/src/components/LoginView/README.md +396 -396
  71. package/src/components/LoginView/icons.tsx +85 -85
  72. package/src/components/LoginView/index.ts +3 -3
  73. package/src/components/Navbar/Navbar.stories.tsx +810 -810
  74. package/src/components/Navbar/Navbar.tsx +755 -755
  75. package/src/components/Navbar/Navbar.types.ts +219 -219
  76. package/src/components/Navbar/README.md +279 -279
  77. package/src/components/Navbar/index.ts +8 -8
  78. package/src/components/NavigationRailCommercial/NavigationRailCommercial.stories.tsx +464 -0
  79. package/src/components/NavigationRailCommercial/NavigationRailCommercial.tsx +301 -0
  80. package/src/components/NavigationRailCommercial/NavigationRailCommercial.types.ts +162 -0
  81. package/src/components/NavigationRailCommercial/README.md +251 -0
  82. package/src/components/NavigationRailCommercial/icons.tsx +54 -0
  83. package/src/components/NavigationRailCommercial/index.ts +6 -0
  84. package/src/components/NavigationRailItem/NavigationRailItem.stories.tsx +667 -667
  85. package/src/components/NavigationRailItem/NavigationRailItem.tsx +314 -313
  86. package/src/components/NavigationRailItem/NavigationRailItem.types.ts +175 -167
  87. package/src/components/NavigationRailItem/README.md +476 -476
  88. package/src/components/NavigationRailItem/index.ts +2 -2
  89. package/src/components/NavigationRailPanel/NavigationRailPanel.stories.tsx +462 -462
  90. package/src/components/NavigationRailPanel/NavigationRailPanel.tsx +332 -332
  91. package/src/components/NavigationRailPanel/NavigationRailPanel.types.ts +178 -178
  92. package/src/components/NavigationRailPanel/README.md +461 -461
  93. package/src/components/NavigationRailPanel/index.ts +6 -6
  94. package/src/components/NavigationRailTypes/NavigationRailTypes.stories.tsx +682 -528
  95. package/src/components/NavigationRailTypes/NavigationRailTypes.tsx +363 -378
  96. package/src/components/NavigationRailTypes/NavigationRailTypes.types.ts +178 -130
  97. package/src/components/NavigationRailTypes/README.md +573 -573
  98. package/src/components/NavigationRailTypes/icons.tsx +76 -141
  99. package/src/components/NavigationRailTypes/index.ts +7 -7
  100. package/src/components/Notification/Notification.stories.tsx +513 -513
  101. package/src/components/Notification/Notification.tsx +145 -145
  102. package/src/components/Notification/Notification.types.ts +142 -142
  103. package/src/components/Notification/README.md +409 -409
  104. package/src/components/POSConvention/POSConvention.stories.tsx +235 -235
  105. package/src/components/POSConvention/POSConvention.tsx +129 -129
  106. package/src/components/POSConvention/POSConvention.types.ts +38 -38
  107. package/src/components/POSConvention/README.md +123 -123
  108. package/src/components/POSConvention/icons.tsx +45 -45
  109. package/src/components/POSConvention/index.ts +3 -3
  110. package/src/components/POSLocationButton/POSLocationButton.stories.tsx +531 -531
  111. package/src/components/POSLocationButton/POSLocationButton.tsx +247 -247
  112. package/src/components/POSLocationButton/POSLocationButton.types.ts +87 -87
  113. package/src/components/POSLocationButton/README.md +253 -253
  114. package/src/components/POSLocationButton/icons.tsx +120 -120
  115. package/src/components/POSLocationButton/index.ts +14 -14
  116. package/src/components/POSNumberButton/POSNumberButton.stories.tsx +415 -415
  117. package/src/components/POSNumberButton/POSNumberButton.tsx +179 -179
  118. package/src/components/POSNumberButton/POSNumberButton.types.ts +51 -51
  119. package/src/components/POSNumberButton/README.md +321 -321
  120. package/src/components/POSNumberButton/index.ts +3 -3
  121. package/src/components/POSProductButton/POSProductButton.stories.tsx +318 -318
  122. package/src/components/POSProductCard/POSProductCard.stories.tsx +642 -642
  123. package/src/components/POSProductCard/POSProductCard.tsx +208 -208
  124. package/src/components/POSProductCard/POSProductCard.types.ts +76 -76
  125. package/src/components/POSProductCard/README.md +179 -179
  126. package/src/components/POSProductCard/icons.tsx +26 -26
  127. package/src/components/POSProductCard/index.ts +2 -2
  128. package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +753 -753
  129. package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +332 -332
  130. package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +119 -119
  131. package/src/components/POSProductSidebarItems/README.md +198 -198
  132. package/src/components/POSProductSidebarItems/icons.tsx +21 -21
  133. package/src/components/POSProductSidebarItems/index.ts +3 -3
  134. package/src/components/POSTable/POSTable.stories.tsx +737 -737
  135. package/src/components/POSTable/POSTable.tsx +401 -401
  136. package/src/components/POSTable/README.md +286 -286
  137. package/src/components/Quantity/Quantity.stories.tsx +457 -457
  138. package/src/components/Radio/Radio.stories.tsx +523 -523
  139. package/src/components/Radio/Radio.tsx +1 -1
  140. package/src/components/Select/Select.stories.tsx +32 -0
  141. package/src/components/Select/Select.tsx +457 -454
  142. package/src/components/Select/icons.tsx +16 -41
  143. package/src/components/SignUpView/SignUpView.stories.tsx +129 -129
  144. package/src/components/SignUpView/SignUpView.tsx +503 -503
  145. package/src/components/SignUpView/SignUpView.types.ts +58 -58
  146. package/src/components/SignUpView/icons.tsx +71 -71
  147. package/src/components/SignUpView/index.ts +3 -3
  148. package/src/components/Switch/README.md +112 -112
  149. package/src/components/Switch/Switch.stories.tsx +550 -550
  150. package/src/components/Switch/Switch.tsx +246 -246
  151. package/src/components/Switch/Switch.types.ts +67 -67
  152. package/src/components/Table/Table.stories.tsx +805 -805
  153. package/src/components/Tabs/README.md +201 -201
  154. package/src/components/Tabs/Tabs.stories.tsx +580 -580
  155. package/src/components/Tabs/Tabs.tsx +356 -356
  156. package/src/components/Tabs/Tabs.types.ts +127 -127
  157. package/src/components/Tabs/icons.tsx +129 -129
  158. package/src/components/Tabs/index.ts +11 -11
  159. package/src/components/Textarea/Textarea.stories.tsx +535 -535
  160. package/src/index.ts +133 -102
  161. package/src/views/LayoutCommercial/LayoutCommercial.stories.tsx +374 -0
  162. package/src/views/LayoutCommercial/LayoutCommercial.tsx +125 -0
  163. package/src/views/LayoutCommercial/LayoutCommercial.types.ts +54 -0
  164. package/src/views/LayoutCommercial/README.md +286 -0
  165. package/src/views/LayoutCommercial/index.ts +2 -0
  166. package/src/views/ListView/ListView.stories.tsx +329 -329
  167. package/src/views/ListView/ListView.tsx +570 -570
  168. package/src/views/ListView/ListView.types.ts +211 -211
  169. package/src/views/ListView/icons.tsx +282 -282
  170. package/src/views/ListView/index.ts +11 -11
  171. package/src/views/LoginView/LoginView.tsx +426 -426
  172. package/src/views/ProductsView/ProductsView.stories.tsx +344 -344
  173. package/src/views/ProductsView/ProductsView.tsx +480 -480
  174. package/src/views/ProductsView/ProductsView.types.ts +238 -238
  175. package/src/views/ProductsView/README.md +312 -312
  176. package/src/views/ProductsView/icons.tsx +38 -38
  177. package/src/views/ProductsView/index.ts +8 -8
  178. package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +376 -376
  179. package/src/views/SignUpView/SignUpView.tsx +503 -503
  180. package/src/views/TableLayoutView/README.md +268 -268
  181. package/src/views/TableLayoutView/TableLayoutView.stories.tsx +235 -235
  182. package/src/views/TableLayoutView/TableLayoutView.tsx +461 -461
  183. package/src/views/TableLayoutView/TableLayoutView.types.ts +209 -209
  184. package/src/views/TableLayoutView/icons.tsx +113 -113
  185. package/src/views/TableLayoutView/index.ts +6 -6
  186. package/storybook/main.ts +19 -19
  187. package/storybook/preview.tsx +84 -84
  188. package/storybook/vitest.setup.ts +6 -6
  189. package/tailwind.config.js +128 -128
@@ -1,313 +1,314 @@
1
- import React from 'react';
2
- import type { NavigationRailItemProps } from './NavigationRailItem.types';
3
-
4
- /**
5
- * NavigationRailItem - Componente independiente para items de NavigationRail
6
- *
7
- * Item individual de navegación vertical (rail) para aplicaciones. Proporciona
8
- * un punto de acceso a destinos mediante un icono, label opcional y badges.
9
- *
10
- * **Características principales:**
11
- * - Ancho fijo de 56px según Figma
12
- * - Estados completos: normal, hover, selected, focus, disabled
13
- * - Badges de notificación con contador opcional
14
- * - Tipografía Label XXSmall (10px Bold) según sistema de diseño
15
- * - Dark mode completo con estrategia 'class'
16
- * - Focus rings adaptativos para accesibilidad
17
- * - Soporte para iconos 16x16px
18
- *
19
- * **Especificaciones de Figma (node 4294-22931):**
20
- * - Ancho: 56px (w-14)
21
- * - Icon container: 32px altura, px-4 py-1, rounded-full
22
- * - Label: 10px Bold, leading 12px (Label XXSmall)
23
- * - Gap: 4px (gap-1)
24
- * - Badge: 13x13px, posición absolute
25
- *
26
- * **Estados visuales (según Figma):**
27
- * - **Enabled**: Icon transparent, label text-content-primary
28
- * - **Hover**: Icon container bg-primary-custom-100 (#dbeefe), icon text-primary-custom-600
29
- * - **Selected**: Icon container bg-primary-custom-100 con overlay, icon text-primary-custom-600
30
- * - **Focus**: Focus ring 2px (#60b6fa) con offset 2px (#dbeefe), sombras combinadas
31
- * - **Disabled**: Opacity 55%, no interactivo
32
- *
33
- * **Dark Mode:**
34
- * - Background: dark-bg-primary
35
- * - Hover icon container: dark-bg-primary/20
36
- * - Selected icon container: dark-primary-custom-600/30
37
- * - Icon colors: dark-content-primary, dark-white on selected
38
- * - Focus ring: dark-border-custom
39
- *
40
- * **Mejores prácticas implementadas:**
41
- * - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
42
- * - Tokens de color consistentes con documentación
43
- * - Accesibilidad ARIA completa
44
- * - Focus visible para keyboard navigation
45
- * - Badge positioning absoluto para no afectar layout
46
- *
47
- * @see docs/colors.md - Sistema de colores (primary-custom, dark-*, content-*)
48
- * @see docs/typography.md - Tipografía (Label XXSmall)
49
- * @see docs/spacing.md - Sistema de espaciado
50
- * @see docs/shadows.md - Sistema de sombras y focus rings
51
- * @see https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4294-22931
52
- *
53
- * @example
54
- * ```tsx
55
- * // Item básico
56
- * <NavigationRailItem
57
- * id="home"
58
- * icon={<HomeIcon />}
59
- * label="Inicio"
60
- * selected={true}
61
- * onClick={() => navigate('/')}
62
- * />
63
- *
64
- * // Con badge de notificación
65
- * <NavigationRailItem
66
- * id="notifications"
67
- * icon={<BellIcon />}
68
- * label="Notificaciones"
69
- * badgeCount={5}
70
- * onClick={() => navigate('/notifications')}
71
- * />
72
- *
73
- * // Item deshabilitado
74
- * <NavigationRailItem
75
- * id="premium"
76
- * icon={<StarIcon />}
77
- * label="Premium"
78
- * disabled={true}
79
- * />
80
- *
81
- * // Solo icono (sin label)
82
- * <NavigationRailItem
83
- * id="home"
84
- * icon={<HomeIcon />}
85
- * label="Inicio"
86
- * showLabelText={false}
87
- * />
88
- * ```
89
- */
90
- export const NavigationRailItem: React.FC<NavigationRailItemProps> = ({
91
- icon,
92
- label,
93
- selected = false,
94
- disabled = false,
95
- badge = false,
96
- badgeCount,
97
- onClick,
98
- ariaLabel,
99
- id,
100
- showLabelText = true,
101
- showIcon = true,
102
- className = '',
103
- }) => {
104
- // ===== CLASES DEL ICON CONTAINER =====
105
- // Según Figma node 4294-22931, el icon container tiene comportamiento diferente
106
- // según el estado:
107
- // - Enabled: bg-transparent, hover:bg-primary-custom-100
108
- // - Hover: bg-primary-custom-100
109
- // - Selected: bg-primary-custom-100 con overlay (rgba(0,0,0,0.024))
110
- // - Focus: bg-primary-custom-100 con focus ring y sombras
111
- // - Disabled: opacidad 55%
112
- const iconContainerClasses = [
113
- // Base layout
114
- 'flex',
115
- 'flex-col',
116
- 'items-center',
117
- 'justify-center',
118
- 'px-4', // 16px horizontal según Figma
119
- 'py-1', // 4px vertical según Figma
120
- 'rounded-full',
121
- 'overflow-hidden',
122
- 'shrink-0',
123
-
124
- // Estados - Background
125
- selected
126
- ? // Selected: bg-primary-custom-100 con overlay
127
- 'bg-primary-custom-100 dark:bg-primary-custom-600/30'
128
- : // Default/Hover: transparent → hover:bg-primary-custom-100
129
- 'bg-transparent hover:bg-primary-custom-100 dark:hover:bg-primary-custom-600/20',
130
-
131
- // Transiciones
132
- 'transition-colors',
133
- 'duration-150',
134
- ].join(' ');
135
-
136
- // ===== CLASES DEL ICONO =====
137
- // Tamaño fijo 16x16px según Figma
138
- // Colores según estado:
139
- // - Default: text-content-primary
140
- // - Hover: text-primary-custom-600
141
- // - Selected: text-primary-custom-600
142
- // - Disabled: text-content-tertiary
143
- // Dark mode: invierte colores
144
- const iconClasses = [
145
- 'w-4', // 16px
146
- 'h-4', // 16px
147
- 'shrink-0',
148
-
149
- // Colores según estado
150
- selected
151
- ? // Selected: primary-custom-600 (azul oscuro)
152
- 'text-primary-custom-600 dark:text-white'
153
- : disabled
154
- ? // Disabled: content-tertiary
155
- 'text-content-tertiary dark:text-content-tertiary'
156
- : // Default/Hover: content-primary → hover:primary-custom-600
157
- 'text-content-primary hover:text-primary-custom-600 dark:text-dark-content-primary dark:hover:text-white',
158
-
159
- 'transition-colors',
160
- 'duration-150',
161
- ].join(' ');
162
-
163
- // ===== CLASES DEL LABEL =====
164
- // Tipografía: Label XXSmall (10px Bold, leading 12px) según Figma
165
- // Color: text-content-primary, dark:text-dark-content-primary
166
- const labelClasses = [
167
- // Tipografía: Label XXSmall (10px Bold)
168
- 'text-[10px]',
169
- 'leading-3', // 12px
170
- 'font-bold',
171
- 'text-center',
172
- 'w-full',
173
- 'min-w-full',
174
-
175
- // Colores
176
- 'text-content-primary',
177
- 'dark:text-dark-content-primary',
178
-
179
- // Para multiline labels, agregar truncate o text-nowrap si es necesario
180
- 'break-words',
181
- ].join(' ');
182
-
183
- // ===== CLASES DEL CONTENEDOR PRINCIPAL =====
184
- // Según Figma: ancho 56px, flex-col, gap-1, items-center
185
- const containerClasses = [
186
- 'relative',
187
- 'flex',
188
- 'flex-col',
189
- 'items-center',
190
- 'gap-1', // 4px según Figma
191
- 'px-0.5', // 2px horizontal según Figma
192
- 'py-0',
193
- 'w-14', // 56px según Figma
194
- 'shrink-0',
195
-
196
- // Interactividad
197
- disabled
198
- ? 'cursor-not-allowed opacity-55 pointer-events-none'
199
- : 'cursor-pointer',
200
-
201
- // Focus visible (accesibilidad)
202
- 'focus:outline-none',
203
- 'focus-visible:ring-2',
204
- 'focus-visible:ring-primary-custom-400', // 4px ring
205
- 'focus-visible:ring-offset-2',
206
- 'focus-visible:ring-offset-primary-custom-100', // offset #dbeefe según Figma
207
- 'dark:focus-visible:ring-dark-border-custom',
208
- 'dark:focus-visible:ring-offset-dark-bg-primary',
209
-
210
- // Focus sombras adicionales según Figma:
211
- // shadow-[0px_0px_0px_2px_var(--background\/backgroundcustomprimary,#dbeefe),0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_var(--primarycustom\/400,#60b6fa)]
212
- 'focus-visible:shadow-[0px_0px_0px_2px_rgba(219,238,254,1),0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_rgba(96,182,250,1)]',
213
- 'dark:focus-visible:shadow-[0px_0px_0px_2px_rgba(59,130,246,0.3),0px_1px_2px_0px_rgba(0,0,0,0.05),0px_0px_0px_4px_rgba(96,182,250,0.4)]',
214
-
215
- 'rounded-lg',
216
-
217
- // Clases personalizadas
218
- className,
219
- ].join(' ');
220
-
221
- // ===== RENDERIZAR BADGE =====
222
- const renderBadge = () => {
223
- if (!badge && badgeCount === undefined) return null;
224
-
225
- // Badge con número
226
- if (badgeCount !== undefined) {
227
- return (
228
- <span
229
- className="
230
- absolute
231
- -top-0.5
232
- left-[34px]
233
- flex
234
- items-center
235
- justify-center
236
- min-w-[13px]
237
- h-[13px]
238
- px-1
239
- rounded-full
240
- text-[10px]
241
- font-bold
242
- leading-none
243
- bg-red-700
244
- text-white
245
- dark:bg-red-700
246
- dark:text-white
247
- pointer-events-none
248
- select-none
249
- "
250
- aria-label={`${badgeCount} notificaciones`}
251
- data-badge
252
- >
253
- {badgeCount > 99 ? '99+' : badgeCount}
254
- </span>
255
- );
256
- }
257
-
258
- // Badge dot simple
259
- return (
260
- <span
261
- className="
262
- absolute
263
- -top-0.5
264
- left-[34px]
265
- w-[13px]
266
- h-[13px]
267
- rounded-full
268
- bg-red-700
269
- dark:bg-red-700
270
- pointer-events-none
271
- select-none
272
- "
273
- aria-label="Notificación"
274
- data-badge
275
- />
276
- );
277
- };
278
-
279
- return (
280
- <button
281
- type="button"
282
- className={containerClasses}
283
- disabled={disabled}
284
- onClick={onClick}
285
- aria-label={ariaLabel || label}
286
- aria-current={selected ? 'page' : undefined}
287
- aria-disabled={disabled}
288
- data-item-id={id}
289
- data-testid={`navigation-rail-item-${id}`}
290
- >
291
- {/* Icon Container */}
292
- {showIcon && (
293
- <div className={iconContainerClasses}>
294
- <span className={iconClasses}>
295
- {icon}
296
- </span>
297
- </div>
298
- )}
299
-
300
- {/* Label */}
301
- {showLabelText && (
302
- <p className={labelClasses}>
303
- {label}
304
- </p>
305
- )}
306
-
307
- {/* Badge */}
308
- {renderBadge()}
309
- </button>
310
- );
311
- };
312
-
313
- NavigationRailItem.displayName = 'NavigationRailItem';
1
+ import React from 'react';
2
+ import type { NavigationRailItemProps } from './NavigationRailItem.types';
3
+
4
+ /**
5
+ * NavigationRailItem - Componente independiente para items de NavigationRail
6
+ *
7
+ * Item individual de navegación vertical (rail) para aplicaciones. Proporciona
8
+ * un punto de acceso a destinos mediante un icono, label opcional y badges.
9
+ *
10
+ * **Características principales:**
11
+ * - Ancho fijo de 56px según Figma
12
+ * - Estados completos: normal, hover, selected, focus, disabled
13
+ * - Badges de notificación con contador opcional
14
+ * - Tipografía Label XXSmall (10px Bold) según sistema de diseño
15
+ * - Dark mode completo con estrategia 'class'
16
+ * - Focus rings adaptativos para accesibilidad
17
+ * - Soporte para iconos 16x16px
18
+ *
19
+ * **Especificaciones de Figma (node 4294-22931):**
20
+ * - Ancho: 56px (w-14)
21
+ * - Icon container: 32px altura, px-4 py-1, rounded-full
22
+ * - Label: 10px Bold, leading 12px (Label XXSmall)
23
+ * - Gap: 4px (gap-1)
24
+ * - Badge: 13x13px, posición absolute
25
+ *
26
+ * **Estados visuales (según Figma):**
27
+ * - **Enabled**: Icon transparent, label text-content-primary
28
+ * - **Hover**: Icon container bg-primary-custom-100 (#dbeefe), icon text-primary-custom-600
29
+ * - **Selected**: Icon container bg-primary-custom-100 con overlay, icon text-primary-custom-600
30
+ * - **Focus**: Focus ring 2px (#60b6fa) con offset 2px (#dbeefe), sombras combinadas
31
+ * - **Disabled**: Opacity 55%, no interactivo
32
+ *
33
+ * **Dark Mode:**
34
+ * - Background: dark-bg-primary
35
+ * - Hover icon container: dark-bg-primary/20
36
+ * - Selected icon container: dark-primary-custom-600/30
37
+ * - Icon colors: dark-content-primary, dark-white on selected
38
+ * - Focus ring: dark-border-custom
39
+ *
40
+ * **Mejores prácticas implementadas:**
41
+ * - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
42
+ * - Tokens de color consistentes con documentación
43
+ * - Accesibilidad ARIA completa
44
+ * - Focus visible para keyboard navigation
45
+ * - Badge positioning absoluto para no afectar layout
46
+ *
47
+ * @see docs/colors.md - Sistema de colores (primary-custom, dark-*, content-*)
48
+ * @see docs/typography.md - Tipografía (Label XXSmall)
49
+ * @see docs/spacing.md - Sistema de espaciado
50
+ * @see docs/shadows.md - Sistema de sombras y focus rings
51
+ * @see https://www.figma.com/design/5XNqf2YTxvwemxwo1LMQ6j/Siesa-UI-Kit?node-id=4294-22931
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * // Item básico
56
+ * <NavigationRailItem
57
+ * id="home"
58
+ * icon={<HomeIcon />}
59
+ * label="Inicio"
60
+ * selected={true}
61
+ * onClick={() => navigate('/')}
62
+ * />
63
+ *
64
+ * // Con badge de notificación
65
+ * <NavigationRailItem
66
+ * id="notifications"
67
+ * icon={<BellIcon />}
68
+ * label="Notificaciones"
69
+ * badgeCount={5}
70
+ * onClick={() => navigate('/notifications')}
71
+ * />
72
+ *
73
+ * // Item deshabilitado
74
+ * <NavigationRailItem
75
+ * id="premium"
76
+ * icon={<StarIcon />}
77
+ * label="Premium"
78
+ * disabled={true}
79
+ * />
80
+ *
81
+ * // Solo icono (sin label)
82
+ * <NavigationRailItem
83
+ * id="home"
84
+ * icon={<HomeIcon />}
85
+ * label="Inicio"
86
+ * showLabelText={false}
87
+ * />
88
+ * ```
89
+ */
90
+ export const NavigationRailItem: React.FC<NavigationRailItemProps> = ({
91
+ icon,
92
+ label,
93
+ selected = false,
94
+ disabled = false,
95
+ badge = false,
96
+ badgeCount,
97
+ onClick,
98
+ onMouseEnter,
99
+ ariaLabel,
100
+ id,
101
+ showLabelText = true,
102
+ showIcon = true,
103
+ className = '',
104
+ }) => {
105
+ // ===== CLASES DEL ICON CONTAINER =====
106
+ // Según Figma node 4294-22931, el icon container tiene comportamiento diferente
107
+ // según el estado:
108
+ // - Enabled: bg-transparent, hover:bg-primary-custom-100
109
+ // - Hover: bg-primary-custom-100
110
+ // - Selected: bg-primary-custom-100 con overlay (rgba(0,0,0,0.024))
111
+ // - Focus: bg-primary-custom-100 con focus ring y sombras
112
+ // - Disabled: opacidad 55%
113
+ const iconContainerClasses = [
114
+ // Base layout
115
+ 'flex',
116
+ 'flex-col',
117
+ 'items-center',
118
+ 'justify-center',
119
+ 'px-4', // 16px horizontal según Figma
120
+ 'py-1', // 4px vertical según Figma
121
+ 'rounded-full',
122
+ 'overflow-hidden',
123
+ 'shrink-0',
124
+
125
+ // Estados - Background
126
+ selected
127
+ ? // Selected: bg-primary-custom-100 con overlay
128
+ 'bg-primary-custom-100 dark:bg-primary-custom-600/30'
129
+ : // Default/Hover: transparent hover:bg-primary-custom-100
130
+ 'bg-transparent hover:bg-primary-custom-100 dark:hover:bg-primary-custom-600/20',
131
+
132
+ // Transiciones
133
+ 'transition-colors',
134
+ 'duration-150',
135
+ ].join(' ');
136
+
137
+ // ===== CLASES DEL ICONO =====
138
+ // Tamaño fijo 16x16px según Figma
139
+ // Colores según estado:
140
+ // - Default: text-content-primary
141
+ // - Hover: text-primary-custom-600
142
+ // - Selected: text-primary-custom-600
143
+ // - Disabled: text-content-tertiary
144
+ // Dark mode: invierte colores
145
+ const iconClasses = [
146
+ 'w-4', // 16px
147
+ 'h-4', // 16px
148
+ 'shrink-0',
149
+
150
+ // Colores según estado
151
+ selected
152
+ ? // Selected: primary-custom-600 (azul oscuro)
153
+ 'text-primary-custom-600 dark:text-white'
154
+ : disabled
155
+ ? // Disabled: content-tertiary
156
+ 'text-content-tertiary dark:text-content-tertiary'
157
+ : // Default/Hover: content-primary hover:primary-custom-600
158
+ 'text-content-primary hover:text-primary-custom-600 dark:text-dark-content-primary dark:hover:text-white',
159
+
160
+ 'transition-colors',
161
+ 'duration-150',
162
+ ].join(' ');
163
+
164
+ // ===== CLASES DEL LABEL =====
165
+ // Tipografía: Label XXSmall (10px Bold, leading 12px) según Figma
166
+ // Color: text-content-primary, dark:text-dark-content-primary
167
+ const labelClasses = [
168
+ // Tipografía: Label XXSmall (10px Bold)
169
+ 'text-xs', // 12px en lugar de text-[10px]
170
+ 'leading-3', // 12px
171
+ 'font-bold',
172
+ 'text-center',
173
+ 'w-full',
174
+ 'min-w-full',
175
+
176
+ // Colores
177
+ 'text-content-primary',
178
+ 'dark:text-dark-content-primary',
179
+
180
+ // Para multiline labels, agregar truncate o text-nowrap si es necesario
181
+ 'break-words',
182
+ ].join(' ');
183
+
184
+ // ===== CLASES DEL CONTENEDOR PRINCIPAL =====
185
+ // Según Figma: ancho 56px, flex-col, gap-1, items-center
186
+ const containerClasses = [
187
+ 'relative',
188
+ 'flex',
189
+ 'flex-col',
190
+ 'items-center',
191
+ 'gap-1', // 4px según Figma
192
+ 'px-0.5', // 2px horizontal según Figma
193
+ 'py-0',
194
+ 'w-14', // 56px según Figma
195
+ 'shrink-0',
196
+
197
+ // Interactividad
198
+ disabled
199
+ ? 'cursor-not-allowed opacity-55 pointer-events-none'
200
+ : 'cursor-pointer',
201
+
202
+ // Focus visible (accesibilidad)
203
+ 'focus:outline-none',
204
+ 'focus-visible:ring-2',
205
+ 'focus-visible:ring-primary-custom-400', // 4px ring
206
+ 'focus-visible:ring-offset-2',
207
+ 'focus-visible:ring-offset-primary-custom-100', // offset #dbeefe según Figma
208
+ 'dark:focus-visible:ring-dark-border-custom',
209
+ 'dark:focus-visible:ring-offset-dark-bg-primary',
210
+
211
+ // Focus sombras adicionales según Figma:
212
+ 'focus-visible:shadow-lg',
213
+ 'dark:focus-visible:shadow-2xl',
214
+
215
+ 'rounded-lg',
216
+
217
+ // Clases personalizadas
218
+ className,
219
+ ].join(' ');
220
+
221
+ // ===== RENDERIZAR BADGE =====
222
+ const renderBadge = () => {
223
+ if (!badge && badgeCount === undefined) return null;
224
+
225
+ // Badge con número
226
+ if (badgeCount !== undefined) {
227
+ return (
228
+ <span
229
+ className="
230
+ absolute
231
+ -top-0.5
232
+ left-8
233
+ flex
234
+ items-center
235
+ justify-center
236
+ min-w-3
237
+ h-3
238
+ px-1
239
+ rounded-full
240
+ text-xs
241
+ font-bold
242
+ leading-none
243
+ bg-red-700
244
+ text-white
245
+ dark:bg-red-700
246
+ dark:text-white
247
+ pointer-events-none
248
+ select-none
249
+ "
250
+ aria-label={`${badgeCount} notificaciones`}
251
+ data-badge
252
+ >
253
+ {badgeCount > 99 ? '99+' : badgeCount}
254
+ </span>
255
+ );
256
+ }
257
+
258
+ // Badge dot simple
259
+ return (
260
+ <span
261
+ className="
262
+ absolute
263
+ -top-0.5
264
+ left-8
265
+ w-3
266
+ h-3
267
+ rounded-full
268
+ bg-red-700
269
+ dark:bg-red-700
270
+ pointer-events-none
271
+ select-none
272
+ "
273
+ aria-label="Notificación"
274
+ data-badge
275
+ />
276
+ );
277
+ };
278
+
279
+ return (
280
+ <button
281
+ type="button"
282
+ className={containerClasses}
283
+ disabled={disabled}
284
+ onClick={onClick}
285
+ onMouseEnter={onMouseEnter}
286
+ aria-label={ariaLabel || label}
287
+ aria-current={selected ? 'page' : undefined}
288
+ aria-disabled={disabled}
289
+ data-item-id={id}
290
+ data-testid={`navigation-rail-item-${id}`}
291
+ >
292
+ {/* Icon Container */}
293
+ {showIcon && (
294
+ <div className={iconContainerClasses}>
295
+ <span className={iconClasses}>
296
+ {icon}
297
+ </span>
298
+ </div>
299
+ )}
300
+
301
+ {/* Label */}
302
+ {showLabelText && (
303
+ <p className={labelClasses}>
304
+ {label}
305
+ </p>
306
+ )}
307
+
308
+ {/* Badge */}
309
+ {renderBadge()}
310
+ </button>
311
+ );
312
+ };
313
+
314
+ NavigationRailItem.displayName = 'NavigationRailItem';