@axzydev/axzy_ui_system 1.4.7 → 1.5.0

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 (226) hide show
  1. package/README.md +2 -11
  2. package/dist/index.cjs +2174 -1570
  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 +232 -19
  7. package/dist/index.d.ts +232 -19
  8. package/dist/index.js +1838 -1238
  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.props.ts +10 -2
  39. package/src/components/molecules/table/table.tsx +163 -114
  40. package/src/components/molecules/time-picker/timePicker.tsx +8 -6
  41. package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
  42. package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
  43. package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
  44. package/src/components/organisms/data-table/dataTable.props.ts +3 -2
  45. package/src/components/organisms/data-table/dataTable.tsx +149 -81
  46. package/src/components/organisms/dialog/dialog.tsx +3 -0
  47. package/src/components/organisms/drawer/drawer.tsx +3 -0
  48. package/src/components/organisms/dropfile/dropfile.tsx +19 -17
  49. package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
  50. package/src/components/organisms/navbar/navbar.tsx +4 -2
  51. package/src/components/organisms/page-header/page-header.tsx +3 -1
  52. package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
  53. package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
  54. package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
  55. package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
  56. package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
  57. package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
  58. package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
  59. package/src/components/organisms/searchTable/searchTable.tsx +4 -2
  60. package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
  61. package/src/components/organisms/sidebar/sidebar.tsx +9 -4
  62. package/src/components/organisms/toast/toast.tsx +3 -1
  63. package/src/components/organisms/topbar/topbar.tsx +4 -2
  64. package/src/components/templates/page/page.props.ts +4 -4
  65. package/src/components/templates/page/page.tsx +6 -4
  66. package/src/components/theme-provider/themeProvider.tsx +28 -17
  67. package/src/dev.css +77 -2
  68. package/src/global.d.ts +10 -0
  69. package/src/i18n/context.ts +9 -0
  70. package/src/i18n/index.ts +22 -0
  71. package/src/i18n/locales/en.ts +163 -0
  72. package/src/i18n/locales/es.ts +165 -0
  73. package/src/i18n/provider.tsx +44 -0
  74. package/src/i18n/translator.ts +82 -0
  75. package/src/i18n/types.ts +54 -0
  76. package/src/i18n/useITTranslation.ts +20 -0
  77. package/src/index.css +0 -1
  78. package/src/index.ts +9 -3
  79. package/src/sandbox/LandingShowcase.tsx +224 -580
  80. package/src/sandbox/NotFoundShowcase.tsx +51 -36
  81. package/src/sandbox/TopbarSearch.tsx +6 -4
  82. package/src/sandbox/controls.tsx +180 -0
  83. package/src/sandbox/i18n.ts +1929 -0
  84. package/src/sandbox/icons.tsx +25 -0
  85. package/src/sandbox/locale.tsx +72 -0
  86. package/src/sandbox/ui.tsx +377 -0
  87. package/src/sandbox/useHashRoute.ts +46 -10
  88. package/src/showcases/DataShowcases.tsx +201 -80
  89. package/src/showcases/FeedbackShowcases.tsx +37 -30
  90. package/src/showcases/FormShowcases.tsx +302 -214
  91. package/src/showcases/GettingStartedShowcase.tsx +151 -145
  92. package/src/showcases/HelpShowcase.tsx +126 -128
  93. package/src/showcases/HomeShowcase.tsx +678 -482
  94. package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
  95. package/src/showcases/NavigationShowcases.tsx +45 -37
  96. package/src/showcases/PageShowcases.tsx +171 -152
  97. package/src/showcases/ProductoDetalle.tsx +272 -129
  98. package/src/showcases/ShowcaseLayout.tsx +270 -216
  99. package/src/showcases/SizesShowcase.tsx +123 -64
  100. package/src/showcases/StructureShowcases.tsx +157 -131
  101. package/src/showcases/portfolioUi.tsx +153 -0
  102. package/src/showcases/techIcons.ts +3 -0
  103. package/src/types/table.types.ts +110 -0
  104. package/src/utils/table.utils.ts +10 -2
  105. package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
  106. package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
  107. package/src/components/atoms/badget/badget.doc.mdx +0 -46
  108. package/src/components/atoms/badget/badget.stories.tsx +0 -75
  109. package/src/components/atoms/button/button.doc.mdx +0 -59
  110. package/src/components/atoms/button/button.stories.tsx +0 -173
  111. package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
  112. package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
  113. package/src/components/atoms/chip/chip.doc.mdx +0 -47
  114. package/src/components/atoms/chip/chip.stories.tsx +0 -64
  115. package/src/components/atoms/divider/divider.doc.mdx +0 -42
  116. package/src/components/atoms/divider/divider.stories.tsx +0 -33
  117. package/src/components/atoms/flex/flex.doc.mdx +0 -59
  118. package/src/components/atoms/flex/flex.stories.tsx +0 -70
  119. package/src/components/atoms/grid/grid.doc.mdx +0 -54
  120. package/src/components/atoms/grid/grid.stories.tsx +0 -71
  121. package/src/components/atoms/image/image.doc.mdx +0 -44
  122. package/src/components/atoms/image/image.stories.tsx +0 -48
  123. package/src/components/atoms/input/input.doc.mdx +0 -70
  124. package/src/components/atoms/input/input.stories.tsx +0 -113
  125. package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
  126. package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
  127. package/src/components/atoms/loader/loader.doc.mdx +0 -43
  128. package/src/components/atoms/loader/loader.stories.tsx +0 -104
  129. package/src/components/atoms/popover/popover.doc.mdx +0 -53
  130. package/src/components/atoms/popover/popover.stories.tsx +0 -24
  131. package/src/components/atoms/progress/progress.doc.mdx +0 -48
  132. package/src/components/atoms/progress/progress.stories.tsx +0 -39
  133. package/src/components/atoms/radio/radio.doc.mdx +0 -62
  134. package/src/components/atoms/radio/radio.stories.tsx +0 -48
  135. package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
  136. package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
  137. package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
  138. package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
  139. package/src/components/atoms/slide/slide.doc.mdx +0 -46
  140. package/src/components/atoms/slide/slide.stories.tsx +0 -120
  141. package/src/components/atoms/slider/slider.doc.mdx +0 -49
  142. package/src/components/atoms/slider/slider.stories.tsx +0 -29
  143. package/src/components/atoms/stack/stack.doc.mdx +0 -126
  144. package/src/components/atoms/stack/stack.stories.tsx +0 -283
  145. package/src/components/atoms/text/text.doc.mdx +0 -41
  146. package/src/components/atoms/text/text.stories.tsx +0 -66
  147. package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
  148. package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
  149. package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
  150. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
  151. package/src/components/isolation/isolation.stories.tsx +0 -202
  152. package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
  153. package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
  154. package/src/components/molecules/alert/alert.doc.mdx +0 -48
  155. package/src/components/molecules/alert/alert.stories.tsx +0 -40
  156. package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
  157. package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
  158. package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
  159. package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
  160. package/src/components/molecules/card/card.doc.mdx +0 -54
  161. package/src/components/molecules/card/card.stories.tsx +0 -57
  162. package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
  163. package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
  164. package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
  165. package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
  166. package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
  167. package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
  168. package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
  169. package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
  170. package/src/components/molecules/field/field.doc.mdx +0 -43
  171. package/src/components/molecules/field/field.stories.tsx +0 -42
  172. package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
  173. package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
  174. package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
  175. package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
  176. package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
  177. package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
  178. package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
  179. package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
  180. package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
  181. package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
  182. package/src/components/molecules/select/select.doc.mdx +0 -75
  183. package/src/components/molecules/select/select.stories.tsx +0 -85
  184. package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
  185. package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
  186. package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
  187. package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
  188. package/src/components/molecules/table/table.doc.mdx +0 -123
  189. package/src/components/molecules/table/table.stories.tsx +0 -402
  190. package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
  191. package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
  192. package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
  193. package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
  194. package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
  195. package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
  196. package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
  197. package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -115
  198. package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
  199. package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
  200. package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
  201. package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
  202. package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
  203. package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
  204. package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
  205. package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
  206. package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
  207. package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
  208. package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
  209. package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
  210. package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
  211. package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
  212. package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
  213. package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
  214. package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
  215. package/src/components/organisms/toast/toast.doc.mdx +0 -50
  216. package/src/components/organisms/toast/toast.stories.tsx +0 -137
  217. package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
  218. package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
  219. package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
  220. package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
  221. package/src/components/templates/layout/layout.doc.mdx +0 -53
  222. package/src/components/templates/layout/layout.stories.tsx +0 -113
  223. package/src/components/templates/page/page.doc.mdx +0 -86
  224. package/src/components/templates/page/page.stories.tsx +0 -97
  225. package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
  226. package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
@@ -14,6 +14,7 @@ import {
14
14
  ITDataTableResponse
15
15
  } from "../index";
16
16
  import { ShowcaseLayout } from "./ShowcaseLayout";
17
+ import { useITTranslation } from "@/i18n";
17
18
 
18
19
  const TABLE_DATA: Record<string, unknown>[] = [
19
20
  { id: 1, name: "Sofía Castillo", email: "sofia@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-18T09:30:00", sales: 28400 },
@@ -148,6 +149,7 @@ const roleColor = (r: string) =>
148
149
 
149
150
  // 1. ITTable Showcase
150
151
  export const TableShowcase = () => {
152
+ const { t } = useITTranslation();
151
153
  const [variant, setVariant] = useState<any>("default");
152
154
  const [size, setSize] = useState<any>("sm");
153
155
  const [useCustomCard, setUseCustomCard] = useState(true);
@@ -231,10 +233,10 @@ export const TableShowcase = () => {
231
233
  type: "actions",
232
234
  actions: () => (
233
235
  <div className="flex gap-1">
234
- <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600 transition-colors" title="View">
236
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600 transition-colors" title={t("showcase.common.view")}>
235
237
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
236
238
  </button>
237
- <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600 transition-colors" title="Edit">
239
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600 transition-colors" title={t("showcase.common.edit")}>
238
240
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
239
241
  </button>
240
242
  </div>
@@ -289,20 +291,20 @@ export const TableShowcase = () => {
289
291
  return (
290
292
  <ShowcaseLayout
291
293
  title="ITTable"
292
- description="Tabla con vista responsive Table / Cards, paginación, filtros y template de card personalizable."
294
+ description={t("showcase.data.tableDesc")}
293
295
  code={code}
294
296
  demo={
295
297
  <div className="w-full space-y-2">
296
298
  <div className="text-xs font-medium text-slate-500 dark:text-slate-400 px-1">
297
299
  {selectedRow
298
- ? `Selected: ${selectedRow.name as string}`
299
- : "Click a row/card or press Enter/Space to select"}
300
+ ? t("showcase.data.selectedName", { name: selectedRow.name as string })
301
+ : t("showcase.data.clickToSelect")}
300
302
  </div>
301
303
  <div className="w-full bg-white dark:bg-slate-900 rounded-xl overflow-hidden shadow-sm">
302
304
  <ITTable
303
305
  columns={columns}
304
306
  data={TABLE_DATA}
305
- title="Team members"
307
+ title={t("showcase.data.teamMembers")}
306
308
  variant={variant}
307
309
  size={size}
308
310
  defaultItemsPerPage={5}
@@ -316,7 +318,7 @@ export const TableShowcase = () => {
316
318
  <>
317
319
  <ITSelect
318
320
  name="var_ctrl"
319
- label="Table variant"
321
+ label={t("showcase.data.tableVariant")}
320
322
  value={variant}
321
323
  onChange={(e: any) => setVariant(e.target.value)}
322
324
  options={[
@@ -327,7 +329,7 @@ export const TableShowcase = () => {
327
329
  />
328
330
  <ITSelect
329
331
  name="sz_ctrl"
330
- label="Row size"
332
+ label={t("showcase.data.rowSize")}
331
333
  value={size}
332
334
  onChange={(e: any) => setSize(e.target.value)}
333
335
  options={[
@@ -337,12 +339,14 @@ export const TableShowcase = () => {
337
339
  ]}
338
340
  />
339
341
  <div className="flex items-center justify-between pt-2">
340
- <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Custom card template</span>
341
- <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm" />
342
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">{t("showcase.data.customCardTemplate")}</span>
343
+ <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm"
344
+ ariaLabel={t("showcase.data.customCardTemplate")}
345
+ />
342
346
  </div>
343
347
  {!useCustomCard && (
344
348
  <div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
345
- Default card active — shows all columns as label-value pairs
349
+ {t("showcase.data.defaultCardActive")}
346
350
  </div>
347
351
  )}
348
352
  </>
@@ -358,6 +362,9 @@ export const TableShowcase = () => {
358
362
  '// ITSearchSelect typeahead (server-side search opcional vía catalogOptions.onSearch)\n{ key: "role", label: "Role", type: "string", filter: "search", catalogOptions: { data: roles } }',
359
363
  '// ITDatePicker fecha única\n{ key: "lastLogin", label: "Last login", type: "date", filter: "date", dateFilterOptions: { maxDate: new Date() } }',
360
364
  '// ITDatePicker rango\n{ key: "hiredAt", label: "Hired", type: "date", filter: "date-range", dateFilterOptions: { maxDate: new Date() } }',
365
+ '// Diseño por instancia: colors pisa el tema; design estiliza cada región\n<ITTable columns={cols} data={rows} showViewToggle={false}\n colors={{ headerBg: "#0f172a", headerText: "#e2e8f0", rowHover: "#e0f2fe" }}\n design={{ card: { className: "rounded-2xl border-2 border-primary-200" }, headerCell: { className: "py-4" }, cell: { className: "font-semibold" } }} />',
366
+ '// Atajos planos equivalentes (ganan sobre design)*\n<ITTable columns={cols} data={rows} cardClassName="rounded-2xl" headerCellStyle={{ letterSpacing: "0.1em" }} rowClassName="border-b border-slate-100" cellClassName="font-semibold" />',
367
+ '// Diseño por columna\n{ key: "sales", label: "Sales", type: "number", headerClassName: "text-right", cellClassName: "text-right", cellStyle: { fontVariantNumeric: "tabular-nums" } }',
361
368
  ],
362
369
  props: [
363
370
  { name: "columns", type: "Column<T>[]", description: "Definiciones de columna: key, label, type, sortable, filter, render, actions." },
@@ -382,11 +389,24 @@ export const TableShowcase = () => {
382
389
  { name: "rowHeight", type: "number", default: "getRowHeight(size, density)", description: "Alto de fila asumido (px). Size-aware: baseline size=\"md\" = 33/45/53 según densidad; size lo desplaza por el line box (sm 16 / md 20 / lg 28 px)." },
383
390
  { name: "overscan", type: "number", default: "5", description: "Filas extra renderizadas arriba/abajo del viewport." },
384
391
  { name: "stickyHeader", type: "boolean", default: "false", description: "Header sticky mientras scrollea el cuerpo virtualizado." },
392
+ { name: "showViewToggle", type: "boolean", default: "true", description: "Muestra/oculta el switch Table|Cards. Ocultarlo no desactiva defaultView ni autoCardBreakpoint." },
393
+ { name: "colors", type: "TableColors", description: "Colores por instancia (headerBg, headerText, rowBg, rowText, rowHover); pisan el tema global solo en esta tabla." },
394
+ { name: "design", type: "TableDesign", description: "Estilo por región: container, card, toolbar, header, headerCell, row, cell, cards, footer. Cada uno con className y style." },
395
+ { name: "cardClassName / cardStyle", type: "string / CSSProperties", description: "Superficie tipo card (borde, radio, sombra, fondo)." },
396
+ { name: "toolbarClassName / toolbarStyle", type: "string / CSSProperties", description: "Barra con el título y el switch Table/Cards." },
397
+ { name: "headerClassName / headerStyle", type: "string / CSSProperties", description: "Fila de títulos (<thead> <tr>)." },
398
+ { name: "headerCellClassName / headerCellStyle", type: "string / CSSProperties", description: "Cada celda de encabezado (<th>)." },
399
+ { name: "rowClassName / rowStyle", type: "string / CSSProperties", description: "Cada fila del cuerpo (<tr>)." },
400
+ { name: "cellClassName / cellStyle", type: "string / CSSProperties", description: "Cada celda del cuerpo (<td>)." },
401
+ { name: "cardsClassName / cardsStyle", type: "string / CSSProperties", description: "Contenedor de la vista de cards." },
402
+ { name: "footerClassName / footerStyle", type: "string / CSSProperties", description: "Franja de paginación." },
403
+ { name: "containerStyle", type: "CSSProperties", description: "Estilos inline del contenedor externo." },
385
404
  ],
386
405
  notes: [
387
406
  "onRowClick funciona en ambas vistas (tabla y cards).",
388
407
  "Cuando se omite no se agrega cursor-pointer, foco, tabIndex ni role.",
389
- "Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo).",
408
+ "Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo). Diseño por columna: headerClassName, headerStyle, cellClassName y cellStyle (className aplica a th y td).",
409
+ "Diseño por instancia: design (o los atajos planos <slot>ClassName/<slot>Style) estiliza container, card, toolbar, header, headerCell, row, cell, cards y footer; el atajo plano gana sobre design. colors instala --color-table-* locales y pisa el tema global. showViewToggle controla el switch Table|Cards y autoCardBreakpoint (px) fuerza la vista cards bajo ese ancho.",
390
410
  "Columna fija: Column acepta pinned: \"left\" | \"right\" (opt-in; omitir = sin cambios). Los offsets sticky acumulan el width en px de cada columna pinneada previa del mismo lado; una columna sin width en px aporta 0px, así que declara width en px en todas las columnas pinneadas de un lado. La celda pinneada pinta fondo opaco (rowBg + rowHover) y un separador de 1px en el borde congelado. Una columna pinneada por lado es el contrato probado. Limitación: con variant=\"striped\" el fondo opaco tapa el tinte de la franja de esa columna.",
391
411
  "Anti scroll horizontal: layout=\"fixed\" + width por columna + truncate + density.",
392
412
  "Virtualización: solo vista tabla; rinde con page size grande; filas de alto uniforme; el scroll no se reinicia al cambiar filtros.",
@@ -398,7 +418,7 @@ export const TableShowcase = () => {
398
418
  gallery={
399
419
  <div className="space-y-6">
400
420
  <div>
401
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Custom cards with hover</h4>
421
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsHover")}</h3>
402
422
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
403
423
  {TABLE_DATA.slice(0, 4).map((row, i) => {
404
424
  const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
@@ -435,8 +455,8 @@ export const TableShowcase = () => {
435
455
  </div>
436
456
 
437
457
  <div>
438
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">ITTable with default cards</h4>
439
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
458
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDefaultCards")}</h3>
459
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
440
460
  <ITTable
441
461
  columns={columns}
442
462
  data={TABLE_DATA.slice(0, 6)}
@@ -449,13 +469,13 @@ export const TableShowcase = () => {
449
469
  </div>
450
470
 
451
471
  <div>
452
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">ITTable with custom cards + pagination</h4>
453
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
472
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsPagination")}</h3>
473
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
454
474
  <ITTable
455
475
  columns={columns}
456
476
  data={TABLE_DATA}
457
477
  size="sm"
458
- title="All members"
478
+ title={t("showcase.data.galleryAllMembers")}
459
479
  defaultItemsPerPage={4}
460
480
  renderCard={(row: any) => {
461
481
  const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
@@ -481,12 +501,12 @@ export const TableShowcase = () => {
481
501
  </div>
482
502
 
483
503
  <div>
484
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Compact fixed layout — no horizontal scroll</h4>
485
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 560 }}>
504
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCompactFixed")}</h3>
505
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 560 }}>
486
506
  <ITTable
487
507
  columns={fixedColumns}
488
508
  data={TABLE_DATA}
489
- title="Fixed layout"
509
+ title={t("showcase.data.galleryFixedLayout")}
490
510
  layout="fixed"
491
511
  density="compact"
492
512
  defaultItemsPerPage={5}
@@ -495,12 +515,12 @@ export const TableShowcase = () => {
495
515
  </div>
496
516
 
497
517
  <div>
498
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Pinned columns — left (Name) &amp; right (Actions)</h4>
499
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 520 }}>
518
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPinnedNameActions")}</h3>
519
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 520 }}>
500
520
  <ITTable
501
521
  columns={pinnedColumns}
502
522
  data={TABLE_DATA}
503
- title="Pinned columns"
523
+ title={t("showcase.data.galleryPinnedColumns")}
504
524
  layout="fixed"
505
525
  density="compact"
506
526
  defaultItemsPerPage={5}
@@ -512,12 +532,12 @@ export const TableShowcase = () => {
512
532
  </div>
513
533
 
514
534
  <div>
515
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Auto cards fallback (&lt; 640px container)</h4>
516
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 420 }}>
535
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryAutoCards")}</h3>
536
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 420 }}>
517
537
  <ITTable
518
538
  columns={columns}
519
539
  data={TABLE_DATA.slice(0, 6)}
520
- title="Narrow container"
540
+ title={t("showcase.data.galleryNarrowContainer")}
521
541
  autoCardBreakpoint={640}
522
542
  defaultItemsPerPage={6}
523
543
  />
@@ -525,12 +545,12 @@ export const TableShowcase = () => {
525
545
  </div>
526
546
 
527
547
  <div>
528
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Virtualized table — 300 rows (only visible window mounted)</h4>
529
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
548
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryVirtualized")}</h3>
549
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
530
550
  <ITTable
531
551
  columns={virtualColumns}
532
552
  data={virtualData}
533
- title="Virtualized · 300 rows"
553
+ title={t("showcase.data.galleryVirtualizedTitle")}
534
554
  virtualized
535
555
  stickyHeader
536
556
  layout="fixed"
@@ -543,12 +563,12 @@ export const TableShowcase = () => {
543
563
  </div>
544
564
 
545
565
  <div>
546
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Column filters: ITSearchSelect + ITDatePicker (single &amp; range)</h4>
547
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
566
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryColumnFilters")}</h3>
567
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
548
568
  <ITTable
549
569
  columns={filterDemoColumns}
550
570
  data={filterDemoData}
551
- title="Filter demo"
571
+ title={t("showcase.data.galleryFilterDemo")}
552
572
  size="sm"
553
573
  defaultItemsPerPage={8}
554
574
  />
@@ -557,6 +577,58 @@ export const TableShowcase = () => {
557
577
  Role uses <code>filter: "search"</code> (ITSearchSelect). Last login uses <code>filter: "date"</code> and Hired uses <code>filter: "date-range"</code> (ITDatePicker), with <code>dateFilterOptions</code> bounds.
558
578
  </p>
559
579
  </div>
580
+
581
+ <div>
582
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDesignOverrides")}</h3>
583
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
584
+ <ITTable
585
+ columns={columns}
586
+ data={TABLE_DATA.slice(0, 5)}
587
+ title={t("showcase.data.galleryCustomDesign")}
588
+ defaultItemsPerPage={5}
589
+ showViewToggle={false}
590
+ colors={{
591
+ headerBg: "#0f172a",
592
+ headerText: "#e2e8f0",
593
+ rowBg: "#ffffff",
594
+ rowText: "#0f172a",
595
+ rowHover: "#e0f2fe",
596
+ }}
597
+ design={{
598
+ card: { className: "rounded-2xl border-2 border-primary-200 shadow-lg" },
599
+ header: { className: "tracking-widest" },
600
+ headerCell: { className: "py-4" },
601
+ row: { className: "border-b border-slate-100" },
602
+ cell: { className: "font-semibold" },
603
+ }}
604
+ />
605
+ </div>
606
+ <p className="mt-2 text-xs text-slate-500 dark:text-slate-400">
607
+ <code>colors</code> pisa el tema global solo para esta instancia; <code>design</code> (o los atajos <code>cardClassName</code>, <code>headerCellStyle</code>, <code>rowClassName</code>, <code>cellStyle</code>…) estiliza cada región. <code>showViewToggle=&#123;false&#125;</code> oculta el switch Table/Cards.
608
+ </p>
609
+ </div>
610
+
611
+ <div>
612
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPerColumnDesign")}</h3>
613
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
614
+ <ITTable
615
+ columns={columns.map((col: any) =>
616
+ col.key === "sales"
617
+ ? { ...col, headerClassName: "text-right", cellClassName: "text-right", cellStyle: { fontVariantNumeric: "tabular-nums" } }
618
+ : col.key === "name"
619
+ ? { ...col, headerStyle: { letterSpacing: "0.1em" } }
620
+ : col
621
+ )}
622
+ data={TABLE_DATA.slice(0, 5)}
623
+ title={t("showcase.data.galleryPerColumnTitle")}
624
+ defaultItemsPerPage={5}
625
+ showViewToggle={false}
626
+ />
627
+ </div>
628
+ <p className="mt-2 text-xs text-slate-500 dark:text-slate-400">
629
+ Cada <code>Column</code> acepta <code>headerClassName</code>, <code>headerStyle</code>, <code>cellClassName</code> y <code>cellStyle</code>, aplicados solo a esa columna.
630
+ </p>
631
+ </div>
560
632
  </div>
561
633
  }
562
634
  />
@@ -565,6 +637,7 @@ export const TableShowcase = () => {
565
637
 
566
638
  // 2. ITDataTable Showcase
567
639
  export const DataTableShowcase = () => {
640
+ const { t } = useITTranslation();
568
641
  const [useCustomCard, setUseCustomCard] = useState(true);
569
642
  const [apiState, setApiState] = useState<"normal" | "loading" | "empty" | "error">("normal");
570
643
  const [reloadKey, setReloadKey] = useState(0);
@@ -747,21 +820,21 @@ export const DataTableShowcase = () => {
747
820
  return (
748
821
  <ShowcaseLayout
749
822
  title="ITDataTable"
750
- description="Tabla auto-suficiente con carga dinámica, filtros, paginación y vista responsive Cards/Table."
823
+ description={t("showcase.data.dataTableDesc")}
751
824
  code={code}
752
825
  demo={
753
826
  <div className="w-full space-y-2">
754
827
  <div className="text-xs font-medium text-slate-500 dark:text-slate-400 px-1">
755
828
  {selectedRow
756
- ? `Selected: ${selectedRow.name as string} (id ${selectedRow.id as number})`
757
- : "Click a row/card or press Enter/Space to select"}
829
+ ? t("showcase.data.selectedNameId", { name: selectedRow.name as string, id: selectedRow.id as number })
830
+ : t("showcase.data.clickToSelect")}
758
831
  </div>
759
832
  <div className="w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl overflow-hidden shadow-md">
760
833
  <ITDataTable
761
834
  key={reloadKey}
762
835
  columns={userColumns}
763
836
  fetchData={fetchData}
764
- title={apiState === "error" ? "Connection error" : apiState === "empty" ? "No data" : "Users"}
837
+ title={apiState === "error" ? t("showcase.data.titleConnectionError") : apiState === "empty" ? t("showcase.data.titleNoData") : t("showcase.data.titleUsers")}
765
838
  defaultItemsPerPage={4}
766
839
  size={dtSize}
767
840
  layout={dtLayout}
@@ -781,7 +854,7 @@ export const DataTableShowcase = () => {
781
854
  </div>
782
855
  <span className={`px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider rounded-md ${
783
856
  row.active ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300" : "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
784
- }`}>{row.active ? "Active" : "Inactive"}</span>
857
+ }`}>{row.active ? t("showcase.common.active") : t("showcase.data.inactive")}</span>
785
858
  </div>
786
859
  <div className="flex items-center justify-between">
787
860
  <span className={`px-1.5 py-0.5 text-[10px] font-bold rounded ${
@@ -801,7 +874,7 @@ export const DataTableShowcase = () => {
801
874
  controls={
802
875
  <div className="space-y-3">
803
876
  <div>
804
- <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">API state</p>
877
+ <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">{t("showcase.data.apiState")}</p>
805
878
  <div className="flex flex-wrap gap-2">
806
879
  {(["normal", "loading", "empty", "error"] as const).map((s) => (
807
880
  <button
@@ -813,22 +886,24 @@ export const DataTableShowcase = () => {
813
886
  : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
814
887
  }`}
815
888
  >
816
- {s === "normal" ? "✅ Normal" : s === "loading" ? "⏳ Loading" : s === "empty" ? "📭 Empty" : "💥 Error"}
889
+ {s === "normal" ? `✅ ${t("showcase.data.stateNormal")}` : s === "loading" ? `⏳ ${t("showcase.data.stateLoading")}` : s === "empty" ? `📭 ${t("showcase.data.stateEmpty")}` : `💥 ${t("showcase.data.stateError")}`}
817
890
  </button>
818
891
  ))}
819
892
  </div>
820
893
  </div>
821
894
  <div className="flex items-center justify-between pt-1">
822
- <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Custom card template</span>
823
- <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm" />
895
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">{t("showcase.data.customCardTemplate")}</span>
896
+ <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm"
897
+ ariaLabel={t("showcase.data.customCardTemplate")}
898
+ />
824
899
  </div>
825
900
  {!useCustomCard && (
826
901
  <div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
827
- Default card active — shows all columns as label-value pairs
902
+ {t("showcase.data.defaultCardActive")}
828
903
  </div>
829
904
  )}
830
905
  <div>
831
- <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Borders</p>
906
+ <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">{t("showcase.data.borders")}</p>
832
907
  <div className="flex flex-wrap gap-2">
833
908
  {(["default", "blue", "rose", "none"] as const).map((s) => (
834
909
  <button
@@ -843,16 +918,16 @@ export const DataTableShowcase = () => {
843
918
  : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
844
919
  }`}
845
920
  >
846
- {s === "default" ? "Gray" : s === "blue" ? "Blue" : s === "rose" ? "Rose" : "None"}
921
+ {s === "default" ? t("showcase.data.borderGray") : s === "blue" ? t("showcase.data.borderBlue") : s === "rose" ? t("showcase.data.borderRose") : t("showcase.data.borderNone")}
847
922
  </button>
848
923
  ))}
849
924
  </div>
850
925
  </div>
851
926
  <div className="space-y-3">
852
- <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Table capabilities</p>
927
+ <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">{t("showcase.data.tableCapabilities")}</p>
853
928
  <ITSelect
854
929
  name="dt_size"
855
- label="Size"
930
+ label={t("showcase.common.size")}
856
931
  value={dtSize}
857
932
  onChange={(e: any) => setDtSize(e.target.value as "sm" | "md" | "lg")}
858
933
  options={[
@@ -863,7 +938,7 @@ export const DataTableShowcase = () => {
863
938
  />
864
939
  <ITSelect
865
940
  name="dt_density"
866
- label="Density"
941
+ label={t("showcase.data.density")}
867
942
  value={dtDensity}
868
943
  onChange={(e: any) => setDtDensity(e.target.value as "compact" | "normal" | "comfortable")}
869
944
  options={[
@@ -874,7 +949,7 @@ export const DataTableShowcase = () => {
874
949
  />
875
950
  <ITSelect
876
951
  name="dt_layout"
877
- label="Layout"
952
+ label={t("showcase.data.layout")}
878
953
  value={dtLayout}
879
954
  onChange={(e: any) => setDtLayout(e.target.value as "auto" | "fixed")}
880
955
  options={[
@@ -884,7 +959,7 @@ export const DataTableShowcase = () => {
884
959
  />
885
960
  <ITSelect
886
961
  name="dt_autocards"
887
- label="Auto cards below (px)"
962
+ label={t("showcase.data.autoCardsBelow")}
888
963
  value={String(dtAutoCardBreakpoint)}
889
964
  onChange={(e: any) => setDtAutoCardBreakpoint(Number(e.target.value))}
890
965
  options={[
@@ -895,16 +970,20 @@ export const DataTableShowcase = () => {
895
970
  ]}
896
971
  />
897
972
  <div className="flex items-center justify-between pt-2">
898
- <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Virtualized (gallery)</span>
899
- <ITSlideToggle isOn={dtVirtualized} onToggle={setDtVirtualized} size="sm" />
973
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">{t("showcase.data.virtualizedGallery")}</span>
974
+ <ITSlideToggle isOn={dtVirtualized} onToggle={setDtVirtualized} size="sm"
975
+ ariaLabel={t("showcase.data.virtualizedGallery")}
976
+ />
900
977
  </div>
901
978
  <div className="flex items-center justify-between">
902
- <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Sticky header</span>
903
- <ITSlideToggle isOn={dtStickyHeader} onToggle={setDtStickyHeader} size="sm" disabled={!dtVirtualized} />
979
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">{t("showcase.data.stickyHeader")}</span>
980
+ <ITSlideToggle isOn={dtStickyHeader} onToggle={setDtStickyHeader} size="sm" disabled={!dtVirtualized}
981
+ ariaLabel={t("showcase.data.stickyHeader")}
982
+ />
904
983
  </div>
905
984
  {dtVirtualized && (
906
985
  <div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
907
- size, density, layout y auto-cards aplican al demo principal; virtualized y sticky header solo al bloque "Server-side virtualization" de la galería. Ese bloque no recibe autoCardBreakpoint: las cards nunca se virtualizan y el fallback de auto-cards desactivaría la virtualización en silencio.
986
+ {t("showcase.data.capabilitiesHint")}
908
987
  </div>
909
988
  )}
910
989
  </div>
@@ -950,11 +1029,24 @@ export const DataTableShowcase = () => {
950
1029
  { name: "rowHeight", type: "number", default: "getRowHeight(size, density)", description: "Alto de fila asumido (px). Size-aware: baseline size=\"md\" = 33/45/53 según densidad; size lo desplaza por el line box (sm 16 / md 20 / lg 28 px)." },
951
1030
  { name: "overscan", type: "number", default: "5", description: "Filas extra renderizadas arriba/abajo del viewport." },
952
1031
  { name: "stickyHeader", type: "boolean", default: "false", description: "Header sticky mientras scrollea el cuerpo virtualizado." },
1032
+ { name: "showViewToggle", type: "boolean", default: "true", description: "Muestra/oculta el switch Table|Cards. Ocultarlo no desactiva defaultView ni autoCardBreakpoint." },
1033
+ { name: "colors", type: "TableColors", description: "Colores por instancia (headerBg, headerText, rowBg, rowText, rowHover); pisan el tema global solo en esta tabla." },
1034
+ { name: "design", type: "TableDesign", description: "Estilo por región: container, card, toolbar, header, headerCell, row, cell, cards, footer. Cada uno con className y style." },
1035
+ { name: "cardClassName / cardStyle", type: "string / CSSProperties", description: "Superficie tipo card (borde, radio, sombra, fondo)." },
1036
+ { name: "toolbarClassName / toolbarStyle", type: "string / CSSProperties", description: "Barra con el título y el switch Table/Cards." },
1037
+ { name: "headerClassName / headerStyle", type: "string / CSSProperties", description: "Fila de títulos (<thead> <tr>)." },
1038
+ { name: "headerCellClassName / headerCellStyle", type: "string / CSSProperties", description: "Cada celda de encabezado (<th>)." },
1039
+ { name: "rowClassName / rowStyle", type: "string / CSSProperties", description: "Cada fila del cuerpo (<tr>)." },
1040
+ { name: "cellClassName / cellStyle", type: "string / CSSProperties", description: "Cada celda del cuerpo (<td>)." },
1041
+ { name: "cardsClassName / cardsStyle", type: "string / CSSProperties", description: "Contenedor de la vista de cards." },
1042
+ { name: "footerClassName / footerStyle", type: "string / CSSProperties", description: "Franja de paginación." },
1043
+ { name: "containerStyle", type: "CSSProperties", description: "Estilos inline del contenedor externo." },
953
1044
  ],
954
1045
  notes: [
955
1046
  "onRowClick funciona en ambas vistas (tabla y cards).",
956
1047
  "Interacciones que inician en botones, links o inputs no disparan onRowClick.",
957
- "Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo).",
1048
+ "Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo). Diseño por columna: headerClassName, headerStyle, cellClassName y cellStyle (className aplica a th y td).",
1049
+ "Diseño por instancia: design (o los atajos planos <slot>ClassName/<slot>Style) estiliza container, card, toolbar, header, headerCell, row, cell, cards y footer; el atajo plano gana sobre design. colors instala --color-table-* locales y pisa el tema global. showViewToggle controla el switch Table|Cards y autoCardBreakpoint (px) fuerza la vista cards bajo ese ancho.",
958
1050
  "Columna fija: Column acepta pinned: \"left\" | \"right\" (opt-in; omitir = sin cambios). Los offsets sticky acumulan el width en px de cada columna pinneada previa del mismo lado; una columna sin width en px aporta 0px, así que declara width en px en todas las columnas pinneadas de un lado. La celda pinneada pinta fondo opaco (rowBg + rowHover) y un separador de 1px en el borde congelado. Una columna pinneada por lado es el contrato probado. Limitación: con variant=\"striped\" el fondo opaco tapa el tinte de la franja de esa columna.",
959
1051
  "Anti scroll horizontal: layout=\"fixed\" + width por columna + truncate + density.",
960
1052
  "Virtualización: solo vista tabla; rinde con page size grande; filas de alto uniforme; el scroll no se reinicia al cambiar filtros.",
@@ -970,10 +1062,10 @@ export const DataTableShowcase = () => {
970
1062
  gallery={
971
1063
  <div className="space-y-6">
972
1064
  <div>
973
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Custom cards with hover</h4>
974
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
1065
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsHover")}</h3>
1066
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
975
1067
  <GalleryDataTable
976
- title="Team members"
1068
+ title={t("showcase.data.teamMembers")}
977
1069
  defaultItemsPerPage={4}
978
1070
  renderCard={(row: any) => {
979
1071
  const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
@@ -989,7 +1081,7 @@ export const DataTableShowcase = () => {
989
1081
  </div>
990
1082
  <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${
991
1083
  row.active ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300" : "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
992
- }`}>{row.active ? "Active" : "Inactive"}</span>
1084
+ }`}>{row.active ? t("showcase.common.active") : t("showcase.data.inactive")}</span>
993
1085
  </div>
994
1086
  <div className="flex items-center gap-2 text-xs text-slate-500 mb-3">
995
1087
  <span className={`px-2 py-0.5 rounded-md font-semibold ${
@@ -1013,8 +1105,8 @@ export const DataTableShowcase = () => {
1013
1105
  </div>
1014
1106
 
1015
1107
  <div>
1016
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Default cards with pagination</h4>
1017
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
1108
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDefaultCardsPagination")}</h3>
1109
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1018
1110
  <GalleryDataTable
1019
1111
  size="sm"
1020
1112
  defaultItemsPerPage={3}
@@ -1024,12 +1116,36 @@ export const DataTableShowcase = () => {
1024
1116
  </div>
1025
1117
 
1026
1118
  <div>
1027
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Pinned columns — left (ID) &amp; right (Active)</h4>
1028
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 520 }}>
1119
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDesignOverrides")}</h3>
1120
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1121
+ <ITDataTable
1122
+ columns={userColumns}
1123
+ fetchData={fetchData}
1124
+ title={t("showcase.data.galleryCustomDesign")}
1125
+ defaultItemsPerPage={4}
1126
+ debounceMs={0}
1127
+ showViewToggle={false}
1128
+ colors={{ headerBg: "#0f172a", headerText: "#e2e8f0", rowHover: "#e0f2fe" }}
1129
+ design={{
1130
+ card: { className: "rounded-2xl border-2 border-primary-200 shadow-lg" },
1131
+ headerCell: { className: "py-4" },
1132
+ row: { className: "border-b border-slate-100" },
1133
+ cell: { className: "font-semibold" },
1134
+ }}
1135
+ />
1136
+ </div>
1137
+ <p className="mt-2 text-xs text-slate-500 dark:text-slate-400">
1138
+ <code>colors</code> pisa el tema global solo para esta instancia; <code>design</code> (o <code>cardClassName</code>, <code>headerCellStyle</code>, <code>rowClassName</code>, <code>cellStyle</code>…) estiliza cada región. <code>showViewToggle=&#123;false&#125;</code> oculta el switch.
1139
+ </p>
1140
+ </div>
1141
+
1142
+ <div>
1143
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPinnedIdActive")}</h3>
1144
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 520 }}>
1029
1145
  <ITDataTable
1030
1146
  columns={pinnedColumns}
1031
1147
  fetchData={fetchData}
1032
- title="Pinned columns"
1148
+ title={t("showcase.data.galleryPinnedColumns")}
1033
1149
  layout="fixed"
1034
1150
  density="compact"
1035
1151
  defaultItemsPerPage={4}
@@ -1042,13 +1158,13 @@ export const DataTableShowcase = () => {
1042
1158
  </div>
1043
1159
 
1044
1160
  <div>
1045
- <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Server-side virtualization — 240 rows {dtVirtualized ? "(page 200)" : "(page 10)"}</h4>
1046
- <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
1161
+ <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryServerVirtualization", { page: dtVirtualized ? 200 : 10 })}</h3>
1162
+ <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1047
1163
  <ITDataTable
1048
1164
  key={dtVirtualized ? "dt-virt-on" : "dt-virt-off"}
1049
1165
  columns={virtualUserColumns}
1050
1166
  fetchData={virtualFetchData}
1051
- title="Virtualized users"
1167
+ title={t("showcase.data.galleryVirtualizedUsers")}
1052
1168
  defaultItemsPerPage={dtVirtualized ? 200 : 10}
1053
1169
  itemsPerPageOptions={dtVirtualized ? [50, 100, 200] : [5, 10, 20]}
1054
1170
  size={dtSize}
@@ -1128,6 +1244,7 @@ function GalleryDataTable({ title, size, defaultItemsPerPage, defaultView, rende
1128
1244
 
1129
1245
  // 3. ITBadget Showcase
1130
1246
  export const BadgetShowcase = () => {
1247
+ const { t } = useITTranslation();
1131
1248
  const [color, setColor] = useState<any>("success");
1132
1249
  const [size, setSize] = useState<any>("medium");
1133
1250
  const [labelText, setLabelText] = useState("Activo");
@@ -1137,7 +1254,7 @@ export const BadgetShowcase = () => {
1137
1254
  return (
1138
1255
  <ShowcaseLayout
1139
1256
  title="ITBadget"
1140
- description="Badges (etiquetas) estilizados para representar estados o categorías de items."
1257
+ description={t("showcase.data.badgetDesc")}
1141
1258
  code={code}
1142
1259
  demo={
1143
1260
  <ITBadget label={labelText} color={color} size={size} />
@@ -1146,14 +1263,14 @@ export const BadgetShowcase = () => {
1146
1263
  <>
1147
1264
  <ITInput
1148
1265
  name="label"
1149
- label="Texto"
1266
+ label={t("showcase.data.textLabel")}
1150
1267
  value={labelText}
1151
1268
  onChange={(e: any) => setLabelText(e.target.value)}
1152
1269
  onBlur={() => { }}
1153
1270
  />
1154
1271
  <ITSelect
1155
1272
  name="color"
1156
- label="Color"
1273
+ label={t("showcase.common.color")}
1157
1274
  value={color}
1158
1275
  onChange={(e: any) => setColor(e.target.value)}
1159
1276
  options={[
@@ -1167,7 +1284,7 @@ export const BadgetShowcase = () => {
1167
1284
  />
1168
1285
  <ITSelect
1169
1286
  name="size"
1170
- label="Tamaño"
1287
+ label={t("showcase.common.size")}
1171
1288
  value={size}
1172
1289
  onChange={(e: any) => setSize(e.target.value)}
1173
1290
  options={[
@@ -1198,6 +1315,7 @@ export const BadgetShowcase = () => {
1198
1315
 
1199
1316
  // 4. ITImage Showcase
1200
1317
  export const ImageShowcase = () => {
1318
+ const { t } = useITTranslation();
1201
1319
  const [broken, setBroken] = useState(false);
1202
1320
  const src = broken ? "https://nonexistent.image.site/broken.jpg" : "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=400&q=80";
1203
1321
 
@@ -1206,26 +1324,28 @@ export const ImageShowcase = () => {
1206
1324
  return (
1207
1325
  <ShowcaseLayout
1208
1326
  title="ITImage"
1209
- description="Componente de renderizado de imágenes inteligente con cargador interno y fallback automático ante fallas de red."
1327
+ description={t("showcase.data.imageDesc")}
1210
1328
  code={code}
1211
1329
  demo={
1212
1330
  <div className="w-full max-w-sm flex flex-col items-center gap-3">
1213
1331
  <ITImage
1214
1332
  src={src}
1215
- alt="Demo abstracta"
1333
+ alt={t("showcase.data.imageAlt")}
1216
1334
  fallback="https://placehold.co/400x200?text=Error+Carga+Imagen"
1217
1335
  className="rounded-xl object-cover h-40 w-full shadow-md"
1218
1336
  />
1219
1337
  <span className="text-xs text-slate-500 font-mono">
1220
- {broken ? "Cargando URL Rota (Fallback Activo)" : "Cargando URL Correcta"}
1338
+ {broken ? t("showcase.data.imageBroken") : t("showcase.data.imageOk")}
1221
1339
  </span>
1222
1340
  </div>
1223
1341
  }
1224
1342
  controls={
1225
1343
  <>
1226
1344
  <div className="flex items-center justify-between pt-2">
1227
- <span className="text-sm font-semibold text-gray-700">Forzar Error de Carga (URL rota)</span>
1228
- <ITSlideToggle isOn={broken} onToggle={setBroken} activeColor="danger" size="sm" />
1345
+ <span className="font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.forceError")}</span>
1346
+ <ITSlideToggle isOn={broken} onToggle={setBroken} activeColor="danger" size="sm"
1347
+ ariaLabel={t("showcase.data.forceError")}
1348
+ />
1229
1349
  </div>
1230
1350
  </>
1231
1351
  }
@@ -1235,6 +1355,7 @@ export const ImageShowcase = () => {
1235
1355
 
1236
1356
  // ITChip Showcase
1237
1357
  export const ChipShowcase = () => {
1358
+ const { t } = useITTranslation();
1238
1359
  const [active, setActive] = useState<string[]>(["mx"]);
1239
1360
 
1240
1361
  const toggle = (id: string) =>
@@ -1251,7 +1372,7 @@ export const ChipShowcase = () => {
1251
1372
  return (
1252
1373
  <ShowcaseLayout
1253
1374
  title="ITChip"
1254
- description="Etiqueta compacta (tag / pill) para labels, filtros activos y selecciones múltiples. Variantes soft, filled y outlined, icono, botón de quitar (X) y estado seleccionado."
1375
+ description={t("showcase.data.chipDesc")}
1255
1376
  code={code}
1256
1377
  demo={
1257
1378
  <div className="w-full max-w-lg space-y-5">
@@ -1273,13 +1394,13 @@ export const ChipShowcase = () => {
1273
1394
  </div>
1274
1395
  <div className="flex flex-wrap items-center gap-2">
1275
1396
  <ITChip label="México" color="danger" variant="outlined" removable onRemove={() => {}} />
1276
- <ITChip label="Deshabilitado" color="secondary" disabled />
1397
+ <ITChip label={t("showcase.data.chipDisabled")} color="secondary" disabled />
1277
1398
  </div>
1278
1399
  </div>
1279
1400
  }
1280
1401
  controls={
1281
1402
  <p className="text-xs text-slate-500">
1282
- Los chips con onClick son toggles de filtro (role=button, Enter/Space). Los chips removibles muestran una X.
1403
+ {t("showcase.data.chipHint")}
1283
1404
  </p>
1284
1405
  }
1285
1406
  doc={{