@axzydev/axzy_ui_system 1.4.8 → 1.5.1

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 (223) hide show
  1. package/README.md +2 -11
  2. package/dist/index.cjs +1826 -1351
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +1 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.cts +115 -14
  7. package/dist/index.d.ts +115 -14
  8. package/dist/index.js +1481 -1010
  9. package/dist/index.js.map +1 -1
  10. package/dist/layered.css +1 -1
  11. package/package.json +3 -16
  12. package/src/App.tsx +90 -73
  13. package/src/components/atoms/badget/badget.tsx +23 -5
  14. package/src/components/atoms/chip/chip.tsx +3 -1
  15. package/src/components/atoms/image/image.tsx +3 -1
  16. package/src/components/atoms/input/input.tsx +8 -5
  17. package/src/components/atoms/loader/loader.tsx +3 -1
  18. package/src/components/atoms/slide/slide.props.ts +12 -0
  19. package/src/components/atoms/slide/slide.tsx +3 -0
  20. package/src/components/molecules/alert/alert.tsx +3 -1
  21. package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
  22. package/src/components/molecules/calendar/calendar.tsx +9 -6
  23. package/src/components/molecules/card/card.tsx +5 -2
  24. package/src/components/molecules/chip-input/chip-input.tsx +9 -7
  25. package/src/components/molecules/date-picker/datePicker.tsx +9 -9
  26. package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
  27. package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
  28. package/src/components/molecules/field/field.tsx +3 -1
  29. package/src/components/molecules/form-header/form-header.tsx +3 -1
  30. package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
  31. package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
  32. package/src/components/molecules/masked-input/masked-input.tsx +3 -1
  33. package/src/components/molecules/multi-select/multi-select.tsx +12 -9
  34. package/src/components/molecules/pagination/pagination.tsx +11 -5
  35. package/src/components/molecules/search-select/search-select.tsx +11 -9
  36. package/src/components/molecules/select/select.tsx +6 -4
  37. package/src/components/molecules/stepper/stepper.tsx +6 -4
  38. package/src/components/molecules/table/table.tsx +30 -28
  39. package/src/components/molecules/time-picker/timePicker.tsx +8 -6
  40. package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
  41. package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
  42. package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
  43. package/src/components/organisms/data-table/dataTable.tsx +31 -25
  44. package/src/components/organisms/dialog/dialog.tsx +3 -0
  45. package/src/components/organisms/drawer/drawer.tsx +3 -0
  46. package/src/components/organisms/dropfile/dropfile.tsx +19 -17
  47. package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
  48. package/src/components/organisms/navbar/navbar.tsx +4 -2
  49. package/src/components/organisms/page-header/page-header.tsx +3 -1
  50. package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
  51. package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
  52. package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
  53. package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
  54. package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
  55. package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
  56. package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
  57. package/src/components/organisms/searchTable/searchTable.tsx +4 -2
  58. package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
  59. package/src/components/organisms/sidebar/sidebar.tsx +9 -4
  60. package/src/components/organisms/toast/toast.tsx +3 -1
  61. package/src/components/organisms/topbar/topbar.tsx +4 -2
  62. package/src/components/templates/page/page.props.ts +4 -4
  63. package/src/components/templates/page/page.tsx +6 -4
  64. package/src/components/theme-provider/themeProvider.tsx +28 -17
  65. package/src/dev.css +61 -2
  66. package/src/global.d.ts +10 -0
  67. package/src/i18n/context.ts +9 -0
  68. package/src/i18n/index.ts +22 -0
  69. package/src/i18n/locales/en.ts +163 -0
  70. package/src/i18n/locales/es.ts +165 -0
  71. package/src/i18n/provider.tsx +44 -0
  72. package/src/i18n/translator.ts +82 -0
  73. package/src/i18n/types.ts +54 -0
  74. package/src/i18n/useITTranslation.ts +20 -0
  75. package/src/index.css +0 -1
  76. package/src/index.ts +7 -1
  77. package/src/sandbox/LandingShowcase.tsx +224 -580
  78. package/src/sandbox/NotFoundShowcase.tsx +51 -36
  79. package/src/sandbox/TopbarSearch.tsx +6 -4
  80. package/src/sandbox/controls.tsx +180 -0
  81. package/src/sandbox/i18n.ts +1939 -0
  82. package/src/sandbox/icons.tsx +25 -0
  83. package/src/sandbox/locale.tsx +72 -0
  84. package/src/sandbox/ui.tsx +474 -0
  85. package/src/sandbox/useHashRoute.ts +46 -10
  86. package/src/showcases/DataShowcases.tsx +103 -87
  87. package/src/showcases/FeedbackShowcases.tsx +37 -30
  88. package/src/showcases/FormShowcases.tsx +303 -215
  89. package/src/showcases/GettingStartedShowcase.tsx +151 -145
  90. package/src/showcases/HelpShowcase.tsx +126 -128
  91. package/src/showcases/HomeShowcase.tsx +678 -482
  92. package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
  93. package/src/showcases/NavigationShowcases.tsx +45 -37
  94. package/src/showcases/PageShowcases.tsx +171 -152
  95. package/src/showcases/ProductoDetalle.tsx +272 -129
  96. package/src/showcases/ShowcaseLayout.tsx +256 -219
  97. package/src/showcases/SizesShowcase.tsx +123 -64
  98. package/src/showcases/StructureShowcases.tsx +203 -138
  99. package/src/showcases/portfolioUi.tsx +153 -0
  100. package/src/showcases/techIcons.ts +3 -0
  101. package/src/utils/table.utils.ts +10 -2
  102. package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
  103. package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
  104. package/src/components/atoms/badget/badget.doc.mdx +0 -46
  105. package/src/components/atoms/badget/badget.stories.tsx +0 -75
  106. package/src/components/atoms/button/button.doc.mdx +0 -59
  107. package/src/components/atoms/button/button.stories.tsx +0 -173
  108. package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
  109. package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
  110. package/src/components/atoms/chip/chip.doc.mdx +0 -47
  111. package/src/components/atoms/chip/chip.stories.tsx +0 -64
  112. package/src/components/atoms/divider/divider.doc.mdx +0 -42
  113. package/src/components/atoms/divider/divider.stories.tsx +0 -33
  114. package/src/components/atoms/flex/flex.doc.mdx +0 -59
  115. package/src/components/atoms/flex/flex.stories.tsx +0 -70
  116. package/src/components/atoms/grid/grid.doc.mdx +0 -54
  117. package/src/components/atoms/grid/grid.stories.tsx +0 -71
  118. package/src/components/atoms/image/image.doc.mdx +0 -44
  119. package/src/components/atoms/image/image.stories.tsx +0 -48
  120. package/src/components/atoms/input/input.doc.mdx +0 -70
  121. package/src/components/atoms/input/input.stories.tsx +0 -113
  122. package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
  123. package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
  124. package/src/components/atoms/loader/loader.doc.mdx +0 -43
  125. package/src/components/atoms/loader/loader.stories.tsx +0 -104
  126. package/src/components/atoms/popover/popover.doc.mdx +0 -53
  127. package/src/components/atoms/popover/popover.stories.tsx +0 -24
  128. package/src/components/atoms/progress/progress.doc.mdx +0 -48
  129. package/src/components/atoms/progress/progress.stories.tsx +0 -39
  130. package/src/components/atoms/radio/radio.doc.mdx +0 -62
  131. package/src/components/atoms/radio/radio.stories.tsx +0 -48
  132. package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
  133. package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
  134. package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
  135. package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
  136. package/src/components/atoms/slide/slide.doc.mdx +0 -46
  137. package/src/components/atoms/slide/slide.stories.tsx +0 -120
  138. package/src/components/atoms/slider/slider.doc.mdx +0 -49
  139. package/src/components/atoms/slider/slider.stories.tsx +0 -29
  140. package/src/components/atoms/stack/stack.doc.mdx +0 -126
  141. package/src/components/atoms/stack/stack.stories.tsx +0 -283
  142. package/src/components/atoms/text/text.doc.mdx +0 -41
  143. package/src/components/atoms/text/text.stories.tsx +0 -66
  144. package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
  145. package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
  146. package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
  147. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
  148. package/src/components/isolation/isolation.stories.tsx +0 -202
  149. package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
  150. package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
  151. package/src/components/molecules/alert/alert.doc.mdx +0 -48
  152. package/src/components/molecules/alert/alert.stories.tsx +0 -40
  153. package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
  154. package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
  155. package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
  156. package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
  157. package/src/components/molecules/card/card.doc.mdx +0 -54
  158. package/src/components/molecules/card/card.stories.tsx +0 -57
  159. package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
  160. package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
  161. package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
  162. package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
  163. package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
  164. package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
  165. package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
  166. package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
  167. package/src/components/molecules/field/field.doc.mdx +0 -43
  168. package/src/components/molecules/field/field.stories.tsx +0 -42
  169. package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
  170. package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
  171. package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
  172. package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
  173. package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
  174. package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
  175. package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
  176. package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
  177. package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
  178. package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
  179. package/src/components/molecules/select/select.doc.mdx +0 -75
  180. package/src/components/molecules/select/select.stories.tsx +0 -85
  181. package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
  182. package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
  183. package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
  184. package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
  185. package/src/components/molecules/table/table.doc.mdx +0 -140
  186. package/src/components/molecules/table/table.stories.tsx +0 -402
  187. package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
  188. package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
  189. package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
  190. package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
  191. package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
  192. package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
  193. package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
  194. package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -132
  195. package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
  196. package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
  197. package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
  198. package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
  199. package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
  200. package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
  201. package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
  202. package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
  203. package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
  204. package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
  205. package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
  206. package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
  207. package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
  208. package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
  209. package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
  210. package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
  211. package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
  212. package/src/components/organisms/toast/toast.doc.mdx +0 -50
  213. package/src/components/organisms/toast/toast.stories.tsx +0 -137
  214. package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
  215. package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
  216. package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
  217. package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
  218. package/src/components/templates/layout/layout.doc.mdx +0 -53
  219. package/src/components/templates/layout/layout.stories.tsx +0 -113
  220. package/src/components/templates/page/page.doc.mdx +0 -86
  221. package/src/components/templates/page/page.stories.tsx +0 -97
  222. package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
  223. package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.4.8",
7
+ "version": "1.5.1",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
@@ -38,12 +38,11 @@
38
38
  "preview": "vite preview",
39
39
  "bundle": "tsup src/index.ts && node scripts/build-css.mjs",
40
40
  "watch": "tsup src/index.ts --watch",
41
- "storybook": "storybook dev -p 6006",
42
- "build-storybook": "storybook build",
43
41
  "ext:catalog": "node vscode-extension/scripts/generate-catalog.mjs",
44
42
  "ext:build": "node vscode-extension/esbuild.mjs",
45
43
  "ext:typecheck": "tsc --noEmit -p vscode-extension/tsconfig.json",
46
- "ext:package": "node vscode-extension/esbuild.mjs && cd vscode-extension && npx --yes @vscode/vsce package --no-dependencies"
44
+ "ext:package": "node vscode-extension/esbuild.mjs && cd vscode-extension && npx --yes @vscode/vsce package --no-dependencies",
45
+ "audit:sandbox": "node scripts/audit-sandbox.mjs"
47
46
  },
48
47
  "dependencies": {
49
48
  "clsx": "^2.1.1",
@@ -75,18 +74,6 @@
75
74
  "tailwindcss": "^4.0.0",
76
75
  "tsup": "^8.3.6",
77
76
  "vite-plugin-dts": "^4.5.0",
78
- "storybook": "^10.2.0",
79
- "@storybook/react-vite": "^10.2.0",
80
- "@chromatic-com/storybook": "^5.0.0",
81
- "@storybook/addon-vitest": "^10.2.0",
82
- "@storybook/addon-a11y": "^10.2.0",
83
- "@storybook/addon-docs": "^10.2.0",
84
- "@storybook/addon-onboarding": "^10.2.0",
85
- "eslint-plugin-storybook": "^10.2.0",
86
- "vitest": "^4.0.18",
87
- "playwright": "^1.58.0",
88
- "@vitest/browser-playwright": "^4.0.18",
89
- "@vitest/coverage-v8": "^4.0.18",
90
77
  "@types/vscode": "1.85.0",
91
78
  "esbuild": "^0.25.0"
92
79
  }
package/src/App.tsx CHANGED
@@ -1,18 +1,11 @@
1
1
  import React, { useEffect, useMemo, useState } from "react";
2
- import {
3
- FaCreditCard,
4
- FaHome,
5
- FaKeyboard,
6
- FaRegBell,
7
- FaSlidersH,
8
- FaTable,
9
- } from "react-icons/fa";
10
- import { ITLayout, ITThemeProvider, useDebouncedSearch } from "./index";
2
+ import { ITLayout, ITThemeProvider, ITI18nProvider, useITTranslation, useDebouncedSearch } from "./index";
11
3
  import type { ITNavigationItem } from "./index";
12
- import "./index.css";
13
4
 
14
5
  // Import Showcases
15
6
  import { HomeShowcase } from "./showcases/HomeShowcase";
7
+ import ProductoDetalle from "./showcases/ProductoDetalle";
8
+ import { PRODUCTOS } from "./showcases/productos";
16
9
  import { GettingStartedShowcase } from "./showcases/GettingStartedShowcase";
17
10
  import { HelpShowcase } from "./showcases/HelpShowcase";
18
11
  import { SizesShowcase } from "./showcases/SizesShowcase";
@@ -82,18 +75,18 @@ import { useHashRoute } from "./sandbox/useHashRoute";
82
75
  import TopbarSearch from "./sandbox/TopbarSearch";
83
76
  import { LandingShowcase } from "./sandbox/LandingShowcase";
84
77
  import NotFoundShowcase from "./sandbox/NotFoundShowcase";
78
+ import { sandboxMessages, translateLabel } from "./sandbox/i18n";
79
+ import { SandboxLocaleProvider, useSandboxLocale } from "./sandbox/locale";
80
+ import { SelectorIdioma, SelectorTema } from "./sandbox/controls";
81
+ import { GROUP_ICONS } from "./sandbox/icons";
85
82
 
86
- // Icons per sandbox group (icons stay here; navigation.ts is plain data).
87
- const GROUP_ICONS: Record<string, React.ReactNode> = {
88
- general: <FaHome />,
89
- struc: <FaCreditCard />,
90
- forms: <FaKeyboard />,
91
- data: <FaTable />,
92
- nav: <FaSlidersH />,
93
- feed: <FaRegBell />,
94
- };
95
-
96
- function App() {
83
+ /**
84
+ * Dev-only language switcher for the sandbox chrome. Lee el idioma del contexto
85
+ * compartido, así que el mismo control vive igual en la barra del sandbox y en
86
+ * la cabecera del portafolio.
87
+ */
88
+ function AppShell() {
89
+ const { t } = useITTranslation();
97
90
  const { route, goToItem, goToLanding, goToPortfolio } = useHashRoute();
98
91
  const [subitemConnector] = useState<"dot" | "|" | "none">("dot");
99
92
 
@@ -127,13 +120,13 @@ function App() {
127
120
  return SANDBOX_GROUPS.map((group) => {
128
121
  const subitems = group.subitems.map((sub) => ({
129
122
  id: sub.id,
130
- label: sub.label,
123
+ label: translateLabel(sub.id, sub.label, t),
131
124
  isActive: showroomActive === sub.id,
132
125
  action: () => goToItem(group.id, sub.id),
133
126
  }));
134
127
  return {
135
128
  id: group.id,
136
- label: group.label,
129
+ label: translateLabel(group.id, group.label, t),
137
130
  icon: GROUP_ICONS[group.id],
138
131
  isActive: subitems.some((sub) => sub.isActive),
139
132
  subitems,
@@ -143,22 +136,23 @@ function App() {
143
136
 
144
137
  const flat: ITNavigationItem[] = [];
145
138
  SANDBOX_GROUPS.forEach((group) => {
139
+ const groupLabel = translateLabel(group.id, group.label, t);
146
140
  const groupMatches =
147
- group.label.toLowerCase().includes(term) ||
141
+ groupLabel.toLowerCase().includes(term) ||
148
142
  group.id.toLowerCase().includes(term);
149
- const groupBadge = group.label.split(" ")[0];
143
+ const groupBadge = groupLabel.split(" ")[0];
150
144
 
151
145
  group.subitems
152
146
  .filter(
153
147
  (sub) =>
154
148
  groupMatches ||
155
- sub.label.toLowerCase().includes(term) ||
149
+ translateLabel(sub.id, sub.label, t).toLowerCase().includes(term) ||
156
150
  sub.id.toLowerCase().includes(term),
157
151
  )
158
152
  .forEach((sub) => {
159
153
  flat.push({
160
154
  id: sub.id,
161
- label: sub.label,
155
+ label: translateLabel(sub.id, sub.label, t),
162
156
  icon: GROUP_ICONS[group.id],
163
157
  badge: groupBadge,
164
158
  isActive: showroomActive === sub.id,
@@ -167,7 +161,7 @@ function App() {
167
161
  });
168
162
  });
169
163
  return flat;
170
- }, [debouncedTerm, showroomActive, goToItem]);
164
+ }, [debouncedTerm, showroomActive, goToItem, t]);
171
165
 
172
166
  // Sidebar is UNCONTROLLED: ITLayout's internal default is the 88px rail and the
173
167
  // rail expands automatically on hover. No toggle button — the hover is the control.
@@ -188,18 +182,14 @@ function App() {
188
182
  pending={searchValue.trim() !== debouncedTerm.trim()}
189
183
  />
190
184
  ),
191
- // The topbar places `children` in its right area, right before the user menu.
185
+ // The topbar places `children` in its right area: los controles reales del
186
+ // chrome (idioma y tema). Antes había aquí una campana de notificaciones con
187
+ // un punto rojo que no llevaba a ninguna parte.
192
188
  children: (
193
- <button
194
- type="button"
195
- aria-label="Notificaciones"
196
- title="Notificaciones"
197
- className="relative flex items-center justify-center p-2.5 rounded-xl transition-colors duration-200 hover:bg-[var(--it-topbar-user-hover,#f1f5f9)] focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-400"
198
- style={{ color: "var(--it-topbar-icon, #94a3b8)" }}
199
- >
200
- <FaRegBell className="w-5 h-5" aria-hidden="true" />
201
- <span className="absolute top-1.5 right-1.5 w-2 h-2 rounded-full bg-red-500 ring-2 ring-white" />
202
- </button>
189
+ <>
190
+ <SelectorIdioma />
191
+ <SelectorTema />
192
+ </>
203
193
  ),
204
194
  navItems: [
205
195
  {
@@ -209,7 +199,7 @@ function App() {
209
199
  },
210
200
  {
211
201
  id: "portfolio",
212
- label: "Portafolio",
202
+ label: t("sandbox.nav.portfolio"),
213
203
  action: () => goToPortfolio(),
214
204
  },
215
205
  ],
@@ -217,16 +207,8 @@ function App() {
217
207
  if (id === "ui-system") goToLanding();
218
208
  else if (id === "portfolio") goToPortfolio();
219
209
  },
220
- userMenu: {
221
- userName: "Alex Dev",
222
- userEmail: "alex@axzy.dev",
223
- menuItems: [
224
- {
225
- label: "Ir al Portafolio",
226
- onClick: () => goToPortfolio(),
227
- },
228
- ],
229
- },
210
+ // Sin `userMenu`: el sandbox no tiene sesión. Antes mostraba un usuario
211
+ // inventado ("Alex Dev / alex@axzy.dev") en un portafolio real.
230
212
  };
231
213
 
232
214
  // Render correct component based on active showroom navigation
@@ -340,31 +322,66 @@ function App() {
340
322
 
341
323
  return (
342
324
  <ITThemeProvider showFab={false} density={0.98} radius={10} shadow={2}>
343
- {route.view === "home" ? (
344
- <div className="min-h-screen bg-slate-50 dark:bg-slate-950 py-10 px-4 sm:px-6 lg:px-8">
345
- <div className="max-w-6xl mx-auto">
325
+ {route.view === "producto" ? (
326
+ <div className="min-h-[100dvh] bg-[#fafafa] dark:bg-[#0b0d10]">
327
+ {route.valid ? (
328
+ <ProductoDetalle
329
+ producto={PRODUCTOS.find((producto) => producto.id === route.producto)!}
330
+ onBack={() => goToPortfolio()}
331
+ />
332
+ ) : (
333
+ <NotFoundShowcase
334
+ attempted={route.raw}
335
+ onGoLanding={() => goToLanding()}
336
+ onGoPortfolio={goToPortfolio}
337
+ />
338
+ )}
339
+ </div>
340
+ ) : route.view === "home" ? (
341
+ <div className="min-h-[100dvh] bg-[#fafafa] dark:bg-[#0b0d10]">
346
342
  <HomeShowcase />
347
343
  </div>
348
- </div>
349
- ) : (
350
- <ITLayout sidebar={sidebarProps} topBar={topBarProps}>
351
- {!route.valid ? (
352
- <NotFoundShowcase
353
- attempted={route.raw}
354
- onGoLanding={() => goToLanding()}
355
- onGoPortfolio={goToPortfolio}
356
- />
357
- ) : !route.group && !route.item ? (
358
- <LandingShowcase
359
- onOpenItem={goToItem}
360
- onOpenPortfolio={goToPortfolio}
361
- />
362
- ) : (
363
- renderShowcase(showroomActive)
364
- )}
365
- </ITLayout>
366
- )}
367
- </ITThemeProvider>
344
+ ) : (
345
+ <ITLayout sidebar={sidebarProps} topBar={topBarProps}>
346
+ {!route.valid ? (
347
+ <NotFoundShowcase
348
+ attempted={route.raw}
349
+ onGoLanding={() => goToLanding()}
350
+ onGoPortfolio={goToPortfolio}
351
+ />
352
+ ) : !route.group && !route.item ? (
353
+ <LandingShowcase
354
+ onOpenItem={goToItem}
355
+ onOpenPortfolio={goToPortfolio}
356
+ />
357
+ ) : (
358
+ renderShowcase(showroomActive)
359
+ )}
360
+ </ITLayout>
361
+ )}
362
+ </ITThemeProvider>
363
+ );
364
+ }
365
+
366
+ /**
367
+ * Sandbox root: el idioma vive en `SandboxLocaleProvider` (compartido con el
368
+ * portafolio) y `ITI18nProvider` lo aplica a todo el showroom.
369
+ */
370
+ function SandboxI18n() {
371
+ const { locale } = useSandboxLocale();
372
+
373
+ return (
374
+ <ITI18nProvider locale={locale} messages={sandboxMessages}>
375
+ <AppShell />
376
+ </ITI18nProvider>
377
+ );
378
+ }
379
+
380
+ function App() {
381
+ return (
382
+ <SandboxLocaleProvider>
383
+ <SandboxI18n />
384
+ </SandboxLocaleProvider>
368
385
  );
369
386
  }
370
387
 
@@ -30,6 +30,19 @@ export default function ITBadget({
30
30
  const themeBadge = (theme as any).badge || {};
31
31
  const config = themeBadge[color] || themeBadge.primary || {};
32
32
 
33
+ /**
34
+ * El acento base de la paleta (`--color-primary`, cian por defecto) queda en
35
+ * 2.4:1 con texto blanco y 3.3:1 como texto sobre blanco. Para `primary` el
36
+ * color se resuelve con utilidades (`primary-700` en claro, `primary-500` en
37
+ * oscuro) que sí pasan AA; el resto de colores del tema se respetan tal cual.
38
+ */
39
+ const esAcentoPrimario = color === "primary";
40
+ const claseAcento = esAcentoPrimario
41
+ ? variant === "filled"
42
+ ? "bg-primary-700 text-white dark:bg-primary-500 dark:text-slate-950"
43
+ : "border-primary-700 text-primary-700 dark:border-primary-500 dark:text-primary-500"
44
+ : "";
45
+
33
46
  const getStyle = () => {
34
47
  const style: React.CSSProperties = {
35
48
  fontWeight: config.fontWeight,
@@ -41,14 +54,18 @@ export default function ITBadget({
41
54
  };
42
55
 
43
56
  if (variant === "filled") {
44
- style.backgroundColor = config.backgroundColor;
45
- style.color = config.color;
57
+ if (!esAcentoPrimario) {
58
+ style.backgroundColor = config.backgroundColor;
59
+ style.color = config.color;
60
+ }
46
61
  style.borderColor = config.borderColor || 'transparent'; // Some filled badges might have subtle borders
47
62
  } else if (variant === "outlined") {
48
63
  style.backgroundColor = "transparent";
49
- // Use the dark text color for both text and border to ensure high contrast
50
- style.color = config.color;
51
- style.borderColor = config.color;
64
+ if (!esAcentoPrimario) {
65
+ // El tono del tema para texto y borde: legible sobre cualquier superficie.
66
+ style.color = config.color;
67
+ style.borderColor = config.color;
68
+ }
52
69
  }
53
70
 
54
71
  return style;
@@ -57,6 +74,7 @@ export default function ITBadget({
57
74
  return (
58
75
  <span
59
76
  className={clsx(
77
+ claseAcento,
60
78
  "inline-flex items-center justify-center",
61
79
  // Apply size classes (padding/font-size)
62
80
  badgeSizes[size],
@@ -4,6 +4,7 @@ import { chipSizes } from "@/types/chip.types";
4
4
  import { theme } from "@/theme/theme";
5
5
  import { FaTimes } from "react-icons/fa";
6
6
  import ITText from "@/components/atoms/text/text";
7
+ import { useITTranslation } from "@/i18n";
7
8
 
8
9
  /**
9
10
  * Compact tag / pill used for labels, active filters, and multi-value selections.
@@ -63,6 +64,7 @@ export default function ITChip({
63
64
  style.borderColor = palette[500];
64
65
  }
65
66
 
67
+ const { t } = useITTranslation();
66
68
  const interactive = Boolean(onClick) && !disabled;
67
69
 
68
70
  return (
@@ -96,7 +98,7 @@ export default function ITChip({
96
98
  {removable && (
97
99
  <button
98
100
  type="button"
99
- aria-label={label ? `Quitar ${label}` : "Quitar"}
101
+ aria-label={label ? t("chip.remove", { label }) : t("chip.remove", { label: "" }).trim()}
100
102
  disabled={disabled}
101
103
  onClick={(e) => {
102
104
  e.stopPropagation();
@@ -1,5 +1,6 @@
1
1
  import { useState } from "react";
2
2
  import { ITImageProps } from "./image.props";
3
+ import { useITTranslation } from "@/i18n";
3
4
 
4
5
  /**
5
6
  * Image component with automatic fallback on load error.
@@ -27,6 +28,7 @@ const ITImage = ({
27
28
  fallback = "",
28
29
  }: ITImageProps) => {
29
30
  const [imageError, setImageError] = useState(false);
31
+ const { t } = useITTranslation();
30
32
 
31
33
  const handleImageError = () => {
32
34
  setImageError(true);
@@ -39,7 +41,7 @@ const ITImage = ({
39
41
  {imageError ? (
40
42
  <img
41
43
  src={fallback}
42
- alt="Fallback"
44
+ alt={t("image.fallbackAlt")}
43
45
  className="w-full h-full object-cover"
44
46
  />
45
47
  ) : (
@@ -5,6 +5,7 @@ import { KeyboardEvent, useState, useEffect, useRef, useCallback } from "react";
5
5
  import { theme } from "@/theme/theme";
6
6
  import { disabledOverlay, iconAbsoluteLeft, iconAbsoluteRight, inputError, inputLabel, inputFieldStyle } from "@/utils/styles";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  /**
10
11
  * Text input component with label, validation error display, icon slots,
@@ -69,6 +70,7 @@ export default function ITInput({
69
70
  size = "md",
70
71
  decimals
71
72
  }: ITInputProps) {
73
+ const { t, formatNumber: formatNumberLocale } = useITTranslation();
72
74
  const isCheckboxOrRadio = type === "checkbox" || type === "radio";
73
75
  const isNumberType = type === "number";
74
76
  const isTextArea = type === "textarea";
@@ -120,11 +122,11 @@ export default function ITInput({
120
122
  : (value === undefined || value === null || String(value).trim() === "");
121
123
 
122
124
  const effectiveError = error !== undefined && error !== false
123
- ? (error === true ? "Este campo es requerido" : error)
124
- : (required && isEmpty ? "Este campo es requerido" : undefined);
125
+ ? (error === true ? t("field.required") : error)
126
+ : (required && isEmpty ? t("field.required") : undefined);
125
127
 
126
128
  const hasError = isTouched && !!effectiveError;
127
- const errorMessage = typeof effectiveError === "string" ? effectiveError : "Este campo es requerido";
129
+ const errorMessage = typeof effectiveError === "string" ? effectiveError : t("field.required");
128
130
 
129
131
 
130
132
  const handleClick = (e: React.MouseEvent<HTMLInputElement>) => {
@@ -148,7 +150,7 @@ export default function ITInput({
148
150
  }
149
151
 
150
152
  if (currencyFormat) {
151
- return num.toLocaleString("es-MX", {
153
+ return formatNumberLocale(num, {
152
154
  minimumFractionDigits: decimalPlaces,
153
155
  maximumFractionDigits: decimalPlaces,
154
156
  });
@@ -160,7 +162,7 @@ export default function ITInput({
160
162
 
161
163
  return num.toString();
162
164
  },
163
- [currencyFormat, decimalPlaces]
165
+ [currencyFormat, decimalPlaces, formatNumberLocale]
164
166
  );
165
167
 
166
168
  const unformatValue = useCallback(
@@ -594,6 +596,7 @@ const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
594
596
  type="button"
595
597
  className="absolute inset-y-0 right-0 flex items-center pr-3 z-10 text-secondary-400 hover:text-secondary-600 focus:outline-none"
596
598
  onClick={() => setShowPassword(!showPassword)}
599
+ aria-label={showPassword ? t("input.hidePassword") : t("input.showPassword")}
597
600
  tabIndex={-1} // Don't allow tabbing into the eye icon
598
601
  >
599
602
  {showPassword ? (
@@ -2,6 +2,7 @@ import { theme } from "@/theme/theme";
2
2
  import { LoaderSize, sizeClasses } from "@/types/loader.types";
3
3
  import { LoaderProps } from "./loader.props";
4
4
  import ITText from "@/components/atoms/text/text";
5
+ import { useITTranslation } from "@/i18n";
5
6
 
6
7
  /**
7
8
  * Loading indicators with multiple animation types: spinner, dots, pulse,
@@ -26,6 +27,7 @@ export default function ITLoader({
26
27
  color = "primary", // Default to semantic primary
27
28
  className = "",
28
29
  }: LoaderProps) {
30
+ const { t } = useITTranslation();
29
31
 
30
32
  // Resolve color: Check if it's a semantic color in theme, otherwise use as-is
31
33
  const isSemantic = color in theme.colors;
@@ -51,7 +53,7 @@ export default function ITLoader({
51
53
  style={style}
52
54
  >
53
55
  <ITText as="span" className="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]">
54
- Loading...
56
+ {t("common.loading")}
55
57
  </ITText>
56
58
  </div>
57
59
  );
@@ -27,6 +27,18 @@ export interface ITSlideToggleProps {
27
27
  /** Size of the switch: "sm" | "md" | "lg". Default: "md". */
28
28
  size?: "sm" | "md" | "lg";
29
29
 
30
+ /**
31
+ * Texto visible del interruptor. Se asocia al control como nombre accesible
32
+ * (`aria-label`) cuando no hay un `<label>` externo que lo nombre.
33
+ */
34
+ label?: string;
35
+
36
+ /**
37
+ * Nombre accesible cuando la etiqueta visible vive fuera del componente.
38
+ * Tiene prioridad sobre `label`.
39
+ */
40
+ ariaLabel?: string;
41
+
30
42
  /** Additional CSS classes on the container. */
31
43
  className?: string;
32
44
  }
@@ -24,6 +24,8 @@ export default function ITSlideToggle({
24
24
  inactiveColor = "#9ca3af", // default gray-400
25
25
  disabled = false,
26
26
  size = "md",
27
+ label,
28
+ ariaLabel,
27
29
  className = "",
28
30
  }: ITSlideToggleProps) {
29
31
  // Handle internal state if not controlled
@@ -98,6 +100,7 @@ export default function ITSlideToggle({
98
100
  style={{ backgroundColor }}
99
101
  role="switch"
100
102
  aria-checked={isOn}
103
+ aria-label={ariaLabel ?? label}
101
104
  tabIndex={disabled ? -1 : 0}
102
105
  onKeyDown={(e) => {
103
106
  if (e.key === 'Enter' || e.key === ' ') {
@@ -2,6 +2,7 @@ import clsx from "clsx";
2
2
  import { ITAlertProps, AlertVariant } from "./alert.props";
3
3
  import { FaInfoCircle, FaCheckCircle, FaExclamationTriangle, FaTimesCircle, FaTimes } from "react-icons/fa";
4
4
  import ITText from "@/components/atoms/text/text";
5
+ import { useITTranslation } from "@/i18n";
5
6
 
6
7
  const config: Record<AlertVariant, { icon: React.ReactNode; classes: string }> = {
7
8
  info: {
@@ -39,6 +40,7 @@ export default function ITAlert({
39
40
  icon,
40
41
  className,
41
42
  }: ITAlertProps) {
43
+ const { t } = useITTranslation();
42
44
  const cfg = config[variant];
43
45
  return (
44
46
  <div
@@ -51,7 +53,7 @@ export default function ITAlert({
51
53
  {children && <ITText as="div" className="text-sm opacity-90">{children}</ITText>}
52
54
  </div>
53
55
  {dismissible && onDismiss && (
54
- <button onClick={onDismiss} className="flex-shrink-0 opacity-60 hover:opacity-100 transition-opacity">
56
+ <button onClick={onDismiss} className="flex-shrink-0 opacity-60 hover:opacity-100 transition-opacity" aria-label={t("common.dismiss")}>
55
57
  <FaTimes size={12} />
56
58
  </button>
57
59
  )}
@@ -2,6 +2,7 @@ import clsx from "clsx";
2
2
  import { ITBreadcrumbsProps } from "./breadcrumbs.props";
3
3
  import { FaChevronRight } from "react-icons/fa";
4
4
  import ITText from "@/components/atoms/text/text";
5
+ import { useITTranslation } from "@/i18n";
5
6
 
6
7
  /**
7
8
  * Navigation breadcrumb trail with automatic link/button rendering and custom separator.
@@ -20,8 +21,9 @@ export default function ITBreadcrumbs({
20
21
  separator = <FaChevronRight size={10} />,
21
22
  className,
22
23
  }: ITBreadcrumbsProps) {
24
+ const { t } = useITTranslation();
23
25
  return (
24
- <nav className={clsx("flex items-center gap-1.5 text-sm", className)} aria-label="Breadcrumb">
26
+ <nav className={clsx("flex items-center gap-1.5 text-sm", className)} aria-label={t("breadcrumbs.aria")}>
25
27
  {items.map((item, i) => {
26
28
  const isLast = i === items.length - 1;
27
29
  return (
@@ -18,12 +18,12 @@ import {
18
18
  isBefore,
19
19
  isAfter
20
20
  } from 'date-fns';
21
- import { es } from 'date-fns/locale';
22
21
  import { clsx, type ClassValue } from 'clsx';
23
22
  import { twMerge } from 'tailwind-merge';
24
23
  import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
25
24
  import { ITCalendarProps, CalendarEvent } from './calendar.props';
26
25
  import ITText from "@/components/atoms/text/text";
26
+ import { useITTranslation } from "@/i18n";
27
27
 
28
28
  function cn(...inputs: ClassValue[]) {
29
29
  return twMerge(clsx(inputs));
@@ -74,6 +74,7 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
74
74
  className,
75
75
  variant = 'primary',
76
76
  }) => {
77
+ const { t, dateLocale } = useITTranslation();
77
78
  // Determine mode: if onChange provided, assume picker (month) unless specialized
78
79
  const mode = modeProp || (onChange ? 'month' : 'week');
79
80
 
@@ -326,7 +327,7 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
326
327
  e.currentTarget.style.backgroundColor = 'transparent';
327
328
  }}
328
329
  >
329
- {format(currentDate, 'MMMM', { locale: es })}
330
+ {format(currentDate, 'MMMM', { locale: dateLocale })}
330
331
  </button>
331
332
  )}
332
333
  <button
@@ -364,7 +365,7 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
364
365
  onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = 'var(--it-calendar-header-hover, #f1f5f9)'; }}
365
366
  onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = 'transparent'; }}
366
367
  >
367
- <ITText as="span">Hoy</ITText>
368
+ <ITText as="span">{t("calendar.today")}</ITText>
368
369
  </button>
369
370
  <button
370
371
  onClick={handleNext}
@@ -437,7 +438,7 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
437
438
  onClick={() => pickMonth(monthIndex)}
438
439
  >
439
440
  <ITText as="span" className="capitalize">
440
- {format(new Date(2000, monthIndex, 1), 'MMM', { locale: es })}
441
+ {format(new Date(2000, monthIndex, 1), 'MMM', { locale: dateLocale })}
441
442
  </ITText>
442
443
  </button>
443
444
  );
@@ -447,7 +448,9 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
447
448
  <div className="p-4">
448
449
  {/* Weekday Headers */}
449
450
  <div className="grid grid-cols-7 mb-2">
450
- {['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom'].map(day => (
451
+ {Array.from({ length: 7 }, (_, i) =>
452
+ format(addDays(startOfWeek(currentDate, { weekStartsOn: 1 }), i), 'EEE', { locale: dateLocale })
453
+ ).map(day => (
451
454
  <ITText key={day} as="div" className="text-center text-xs font-semibold text-secondary-400 uppercase py-1">
452
455
  {day}
453
456
  </ITText>
@@ -554,7 +557,7 @@ export const ITCalendar: React.FC<ITCalendarProps> = ({
554
557
  opacity: isToday(day) ? 1 : 0.6,
555
558
  }}
556
559
  >
557
- {format(day, 'EEE', { locale: es })}
560
+ {format(day, 'EEE', { locale: dateLocale })}
558
561
  </ITText>
559
562
  <ITText
560
563
  as="span"
@@ -3,6 +3,7 @@ import { ITCardProps } from "./card.props";
3
3
  import { useState } from "react";
4
4
  import ITText from "@/components/atoms/text/text";
5
5
  import { useITFlatAppearance } from "@/theme/theme-context";
6
+ import { useITTranslation } from "@/i18n";
6
7
 
7
8
  /**
8
9
  * Versatile card container with optional image, title, body content, and action footer. Supports interactive hover states.
@@ -20,7 +21,7 @@ import { useITFlatAppearance } from "@/theme/theme-context";
20
21
  export default function ITCard({
21
22
  title,
22
23
  image,
23
- alt = "Card Image",
24
+ alt,
24
25
  children,
25
26
  actions,
26
27
  className,
@@ -30,8 +31,10 @@ export default function ITCard({
30
31
  actionClassName,
31
32
  onClick,
32
33
  }: ITCardProps) {
34
+ const { t } = useITTranslation();
33
35
  const [isHovered, setIsHovered] = useState(false);
34
36
  const flat = useITFlatAppearance();
37
+ const resolvedAlt = alt ?? t("card.imageAlt");
35
38
 
36
39
  const containerStyle: React.CSSProperties = {
37
40
  backgroundColor: "var(--it-card-bg, #ffffff)",
@@ -76,7 +79,7 @@ export default function ITCard({
76
79
  {image && (
77
80
  <img
78
81
  src={image}
79
- alt={alt}
82
+ alt={resolvedAlt}
80
83
  className={clsx("w-full h-48 object-cover", imageClassName)}
81
84
  />
82
85
  )}