@axzydev/axzy_ui_system 1.2.6 → 1.2.8

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 (185) hide show
  1. package/dist/index.cjs +777 -535
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.cts +1470 -87
  6. package/dist/index.d.ts +1470 -87
  7. package/dist/index.js +799 -555
  8. package/dist/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/App.tsx +19 -54
  11. package/src/components/alert/alert.doc.mdx +48 -0
  12. package/src/components/alert/alert.props.ts +7 -0
  13. package/src/components/alert/alert.tsx +8 -0
  14. package/src/components/avatar/avatar.doc.mdx +48 -0
  15. package/src/components/avatar/avatar.props.ts +8 -0
  16. package/src/components/avatar/avatar.tsx +25 -5
  17. package/src/components/badget/badget.doc.mdx +46 -0
  18. package/src/components/badget/badget.props.ts +6 -0
  19. package/src/components/badget/badget.tsx +11 -0
  20. package/src/components/breadcrumbs/breadcrumbs.doc.mdx +57 -0
  21. package/src/components/breadcrumbs/breadcrumbs.props.ts +6 -0
  22. package/src/components/breadcrumbs/breadcrumbs.tsx +12 -0
  23. package/src/components/button/button.doc.mdx +59 -0
  24. package/src/components/button/button.props.ts +12 -0
  25. package/src/components/button/button.tsx +11 -0
  26. package/src/components/calendar/calendar.doc.mdx +75 -0
  27. package/src/components/calendar/calendar.props.ts +27 -7
  28. package/src/components/calendar/calendar.tsx +13 -0
  29. package/src/components/card/card.doc.mdx +54 -0
  30. package/src/components/card/card.props.ts +11 -0
  31. package/src/components/card/card.tsx +13 -0
  32. package/src/components/checkbox/checkbox.doc.mdx +54 -0
  33. package/src/components/checkbox/checkbox.props.ts +7 -0
  34. package/src/components/checkbox/checkbox.tsx +9 -0
  35. package/src/components/confirm-dialog/confirm-dialog.doc.mdx +60 -0
  36. package/src/components/confirm-dialog/confirm-dialog.props.ts +9 -0
  37. package/src/components/confirm-dialog/confirm-dialog.tsx +19 -0
  38. package/src/components/data-table/ITDataTable.doc.mdx +59 -0
  39. package/src/components/data-table/dataTable.props.ts +15 -0
  40. package/src/components/data-table/dataTable.tsx +178 -73
  41. package/src/components/date-picker/date-picker.doc.mdx +65 -0
  42. package/src/components/date-picker/date-picker.props.ts +17 -0
  43. package/src/components/date-picker/datePicker.tsx +31 -1
  44. package/src/components/dialog/dialog.doc.mdx +54 -0
  45. package/src/components/dialog/dialog.props.ts +7 -0
  46. package/src/components/dialog/dialog.tsx +26 -0
  47. package/src/components/divider/divider.doc.mdx +42 -0
  48. package/src/components/divider/divider.props.ts +5 -0
  49. package/src/components/divider/divider.tsx +16 -0
  50. package/src/components/drawer/drawer.doc.mdx +59 -0
  51. package/src/components/drawer/drawer.props.ts +9 -0
  52. package/src/components/drawer/drawer.tsx +20 -0
  53. package/src/components/dropfile/dropfile.doc.mdx +59 -0
  54. package/src/components/dropfile/dropfile.props.ts +41 -0
  55. package/src/components/dropfile/dropfile.tsx +21 -33
  56. package/src/components/empty-state/empty-state.doc.mdx +50 -0
  57. package/src/components/empty-state/empty-state.props.ts +5 -0
  58. package/src/components/empty-state/empty-state.tsx +16 -0
  59. package/src/components/flex/flex.doc.mdx +59 -0
  60. package/src/components/flex/flex.props.ts +13 -0
  61. package/src/components/flex/flex.tsx +15 -0
  62. package/src/components/form-builder/fieldRenderer.tsx +52 -2
  63. package/src/components/form-builder/formBuilder.doc.mdx +124 -0
  64. package/src/components/form-builder/formBuilder.props.ts +12 -2
  65. package/src/components/form-builder/formBuilder.tsx +31 -0
  66. package/src/components/form-header/form-header.doc.mdx +38 -0
  67. package/src/components/form-header/form-header.props.ts +3 -0
  68. package/src/components/form-header/form-header.stories.tsx +53 -0
  69. package/src/components/form-header/form-header.tsx +11 -0
  70. package/src/components/grid/grid.doc.mdx +54 -0
  71. package/src/components/grid/grid.props.ts +13 -0
  72. package/src/components/grid/grid.tsx +26 -2
  73. package/src/components/image/image.doc.mdx +44 -0
  74. package/src/components/image/image.props.ts +5 -0
  75. package/src/components/image/image.stories.tsx +49 -0
  76. package/src/components/image/image.tsx +19 -0
  77. package/src/components/input/input.doc.mdx +70 -0
  78. package/src/components/input/input.props.ts +34 -2
  79. package/src/components/input/input.tsx +29 -0
  80. package/src/components/layout/layout.doc.mdx +51 -0
  81. package/src/components/layout/layout.props.ts +5 -0
  82. package/src/components/layout/layout.tsx +41 -17
  83. package/src/components/loader/loader.doc.mdx +43 -0
  84. package/src/components/loader/loader.props.ts +4 -0
  85. package/src/components/loader/loader.tsx +17 -0
  86. package/src/components/navbar/navbar.doc.mdx +77 -0
  87. package/src/components/navbar/navbar.props.ts +37 -1
  88. package/src/components/navbar/navbar.stories.tsx +84 -0
  89. package/src/components/navbar/navbar.tsx +78 -46
  90. package/src/components/page/page.doc.mdx +80 -0
  91. package/src/components/page/page.props.ts +25 -0
  92. package/src/components/page/page.tsx +35 -7
  93. package/src/components/page-header/page-header.doc.mdx +54 -0
  94. package/src/components/page-header/page-header.props.ts +11 -0
  95. package/src/components/page-header/page-header.stories.tsx +14 -0
  96. package/src/components/page-header/page-header.tsx +50 -15
  97. package/src/components/pagination/pagination.doc.mdx +62 -0
  98. package/src/components/pagination/pagination.props.ts +11 -32
  99. package/src/components/pagination/pagination.tsx +17 -0
  100. package/src/components/popover/popover.doc.mdx +53 -0
  101. package/src/components/popover/popover.props.ts +8 -0
  102. package/src/components/popover/popover.tsx +13 -0
  103. package/src/components/progress/progress.doc.mdx +48 -0
  104. package/src/components/progress/progress.props.ts +8 -0
  105. package/src/components/progress/progress.tsx +9 -0
  106. package/src/components/radio/radio.doc.mdx +62 -0
  107. package/src/components/radio/radio.props.ts +11 -0
  108. package/src/components/radio/radio.tsx +18 -0
  109. package/src/components/search-select/search-select.doc.mdx +72 -0
  110. package/src/components/search-select/search-select.props.ts +23 -18
  111. package/src/components/search-select/search-select.tsx +27 -2
  112. package/src/components/searchTable/searchTable.doc.mdx +79 -0
  113. package/src/components/searchTable/searchTable.props.ts +42 -3
  114. package/src/components/searchTable/searchTable.stories.tsx +74 -0
  115. package/src/components/searchTable/searchTable.tsx +23 -0
  116. package/src/components/segmented-control/segmented-control.doc.mdx +60 -0
  117. package/src/components/segmented-control/segmented-control.props.ts +12 -0
  118. package/src/components/segmented-control/segmented-control.tsx +15 -0
  119. package/src/components/select/select.doc.mdx +75 -0
  120. package/src/components/select/select.props.ts +22 -0
  121. package/src/components/select/select.tsx +2 -5
  122. package/src/components/sidebar/sidebar.doc.mdx +86 -0
  123. package/src/components/sidebar/sidebar.props.ts +22 -0
  124. package/src/components/sidebar/sidebar.tsx +22 -0
  125. package/src/components/skeleton/skeleton.doc.mdx +43 -0
  126. package/src/components/skeleton/skeleton.props.ts +8 -0
  127. package/src/components/skeleton/skeleton.tsx +10 -0
  128. package/src/components/slide/slide.doc.mdx +46 -0
  129. package/src/components/slide/slide.props.ts +11 -24
  130. package/src/components/slide/slide.tsx +10 -1
  131. package/src/components/slider/slider.doc.mdx +49 -0
  132. package/src/components/slider/slider.props.ts +9 -0
  133. package/src/components/slider/slider.tsx +15 -0
  134. package/src/components/stack/stack.doc.mdx +126 -0
  135. package/src/components/stack/stack.props.ts +18 -0
  136. package/src/components/stack/stack.stories.tsx +221 -16
  137. package/src/components/stack/stack.tsx +21 -0
  138. package/src/components/stat-card/stat-card.doc.mdx +48 -0
  139. package/src/components/stat-card/stat-card.props.ts +9 -0
  140. package/src/components/stat-card/stat-card.tsx +10 -0
  141. package/src/components/stepper/stepper.doc.mdx +54 -0
  142. package/src/components/stepper/stepper.props.ts +11 -1
  143. package/src/components/stepper/stepper.tsx +15 -0
  144. package/src/components/table/table.doc.mdx +58 -0
  145. package/src/components/table/table.props.ts +17 -0
  146. package/src/components/table/table.tsx +199 -84
  147. package/src/components/tabs/tabs.doc.mdx +52 -0
  148. package/src/components/tabs/tabs.props.ts +11 -0
  149. package/src/components/tabs/tabs.tsx +14 -0
  150. package/src/components/text/text.doc.mdx +41 -0
  151. package/src/components/text/text.props.ts +5 -0
  152. package/src/components/text/text.stories.tsx +67 -0
  153. package/src/components/text/text.tsx +10 -0
  154. package/src/components/textarea/textarea.doc.mdx +64 -0
  155. package/src/components/textarea/textarea.props.ts +11 -0
  156. package/src/components/textarea/textarea.tsx +16 -0
  157. package/src/components/theme-provider/themeProvider.doc.mdx +75 -0
  158. package/src/components/theme-provider/themeProvider.props.ts +3 -0
  159. package/src/components/theme-provider/themeProvider.stories.tsx +59 -0
  160. package/src/components/theme-provider/themeProvider.tsx +14 -0
  161. package/src/components/time-picker/timePicker.doc.mdx +68 -0
  162. package/src/components/time-picker/timePicker.props.ts +14 -0
  163. package/src/components/time-picker/timePicker.tsx +22 -1
  164. package/src/components/toast/toast.doc.mdx +50 -0
  165. package/src/components/toast/toast.props.ts +5 -0
  166. package/src/components/toast/toast.tsx +21 -0
  167. package/src/components/tooltip/tooltip.doc.mdx +47 -0
  168. package/src/components/tooltip/tooltip.props.ts +6 -0
  169. package/src/components/tooltip/tooltip.tsx +19 -0
  170. package/src/components/topbar/topbar.doc.mdx +69 -0
  171. package/src/components/topbar/topbar.props.ts +15 -0
  172. package/src/components/topbar/topbar.tsx +38 -0
  173. package/src/components/triple-filter/tripleFilter.doc.mdx +62 -0
  174. package/src/components/triple-filter/tripleFilter.props.ts +7 -0
  175. package/src/components/triple-filter/tripleFilter.tsx +32 -0
  176. package/src/index.css +17 -0
  177. package/src/showcases/DataShowcases.tsx +529 -45
  178. package/src/showcases/FormShowcases.tsx +399 -29
  179. package/src/showcases/HomeShowcase.tsx +252 -272
  180. package/src/showcases/LayoutPrimitivesShowcases.tsx +1224 -248
  181. package/src/showcases/PageShowcases.tsx +868 -151
  182. package/src/showcases/ShowcaseLayout.tsx +237 -73
  183. package/src/showcases/StructureShowcases.tsx +1039 -97
  184. package/src/types/table.types.ts +1 -1
  185. package/src/utils/styles.ts +2 -1
@@ -1,11 +1,13 @@
1
1
  import { useTableState } from "@/hooks/useTableState";
2
2
  import { sizeStyles, variantStyles } from "@/types/table.types";
3
3
  import clsx from "clsx";
4
- import React from "react";
4
+ import React, { useState } from "react";
5
5
  import {
6
6
  FaCheck,
7
7
  FaSpinner,
8
- FaTimes
8
+ FaTimes,
9
+ FaTable,
10
+ FaThLarge
9
11
  } from "react-icons/fa";
10
12
  import { MdOutlineSwapVert } from "react-icons/md";
11
13
  import ITInput from "../input/input";
@@ -23,6 +25,23 @@ export const formatCurrencyMX = (value: number) => {
23
25
  currency: "MXN",
24
26
  });
25
27
  };
28
+
29
+ /**
30
+ * A feature-rich data table with per-column filtering, sortable columns,
31
+ * pagination, boolean/catalog type support, and currency formatting.
32
+ *
33
+ * @example
34
+ * <ITTable
35
+ * columns={[
36
+ * { key: "name", label: "Name", type: "string", sortable: true },
37
+ * { key: "active", label: "Active", type: "boolean", filter: true },
38
+ * { key: "actions", label: "", type: "actions", actions: (row) => <ITButton>Edit</ITButton> },
39
+ * ]}
40
+ * data={users}
41
+ * title="User List"
42
+ * size="sm"
43
+ * />
44
+ */
26
45
  export default function ITTable<T extends Record<string, unknown>>({
27
46
  columns,
28
47
  data = [],
@@ -33,7 +52,13 @@ export default function ITTable<T extends Record<string, unknown>>({
33
52
  itemsPerPageOptions = [5, 10, 20],
34
53
  defaultItemsPerPage = 10,
35
54
  title,
55
+ renderCard,
56
+ defaultView = "table",
57
+ showVerticalBorder = true,
58
+ verticalBorderClassname,
36
59
  }: ITTableProps<T>) {
60
+ const [viewMode, setViewMode] = useState<"table" | "cards">(defaultView);
61
+
37
62
  const {
38
63
  currentPage,
39
64
  itemsPerPage,
@@ -261,103 +286,193 @@ export default function ITTable<T extends Record<string, unknown>>({
261
286
  }
262
287
  };
263
288
 
289
+ const renderDefaultCard = (row: T) => {
290
+ const dataCols = columns.filter((c) => c.type !== "actions");
291
+ const actionCol = columns.find((c) => c.type === "actions");
292
+ return (
293
+ <div className="bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 p-3 space-y-2 shadow-sm">
294
+ {dataCols.map((col) => (
295
+ <div key={col.key} className="flex items-center justify-between gap-2">
296
+ <span className="text-[10px] font-bold uppercase tracking-wider text-slate-400 whitespace-nowrap flex-shrink-0 min-w-[70px]">
297
+ {col.label}
298
+ </span>
299
+ <span className="text-sm font-medium text-slate-800 dark:text-white text-right truncate">
300
+ {renderCellContent(col, row) as React.ReactNode}
301
+ </span>
302
+ </div>
303
+ ))}
304
+ {actionCol?.actions && (
305
+ <div className="flex items-center justify-end gap-2 pt-2 border-t border-slate-100 dark:border-slate-700">
306
+ {actionCol.actions(row)}
307
+ </div>
308
+ )}
309
+ </div>
310
+ );
311
+ };
312
+
264
313
  return (
265
314
  <div className={clsx("space-y-4 w-full", containerClassName)}>
266
315
  <div className="rounded-xl shadow-sm overflow-hidden" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
267
316
  {/* Header outside overflow */}
268
317
  {title && (
269
- <div className="px-6 py-5 border-b border-secondary-100" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
318
+ <div className="px-6 py-5 flex items-center justify-between" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
270
319
  <ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
320
+ <div className="flex items-center bg-slate-100 dark:bg-slate-800/80 rounded-lg p-0.5 border border-slate-200 dark:border-slate-700/50">
321
+ <button
322
+ onClick={() => setViewMode("table")}
323
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
324
+ viewMode === "table"
325
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
326
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
327
+ }`}
328
+ >
329
+ <FaTable size={11} />
330
+ Table
331
+ </button>
332
+ <button
333
+ onClick={() => setViewMode("cards")}
334
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
335
+ viewMode === "cards"
336
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
337
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
338
+ }`}
339
+ >
340
+ <FaThLarge size={11} />
341
+ Cards
342
+ </button>
343
+ </div>
271
344
  </div>
272
345
  )}
273
346
 
274
- {/* Scrollable Table */}
275
- <div className="overflow-x-auto">
276
- <table
277
- className={clsx(
278
- "min-w-max w-full text-sm text-left text-secondary-600",
279
- variantStyles[variant],
280
- sizeStyles[size]
281
- )}
282
- >
347
+ {!title && (
348
+ <div className="flex justify-end px-4 pt-3">
349
+ <div className="flex items-center bg-slate-100 dark:bg-slate-800/80 rounded-lg p-0.5 border border-slate-200 dark:border-slate-700/50">
350
+ <button
351
+ onClick={() => setViewMode("table")}
352
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
353
+ viewMode === "table"
354
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
355
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
356
+ }`}
357
+ >
358
+ <FaTable size={11} />
359
+ Table
360
+ </button>
361
+ <button
362
+ onClick={() => setViewMode("cards")}
363
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
364
+ viewMode === "cards"
365
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
366
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
367
+ }`}
368
+ >
369
+ <FaThLarge size={11} />
370
+ Cards
371
+ </button>
372
+ </div>
373
+ </div>
374
+ )}
283
375
 
284
- <thead>
285
- <tr className="bg-secondary-50 border-b border-secondary-200 text-xs uppercase tracking-wider font-semibold text-secondary-500">
286
- {columns.map((col) => (
287
- <th
288
- key={col.key}
289
- scope="col"
290
- className={clsx("px-4 py-4 align-top", col.className)}
291
- >
292
- <div className="flex flex-col gap-3 min-w-[150px]">
293
- {/* Column header */}
294
- <div className="flex items-center justify-between gap-2">
295
- <ITText as="span" className="text-secondary-700 font-bold">{col.label}</ITText>
296
- {col.sortable && col.type !== "actions" && (
297
- <button
298
- onClick={() => handleSort(col.key)}
299
- className={`p-1 rounded-md transition-colors ${sortConfig?.key === col.key
300
- ? "bg-secondary-200 text-secondary-900"
301
- : "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
302
- }`}
303
- title={`Ordenar por ${col.label}`}
304
- >
305
- <MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
306
- </button>
307
- )}
376
+ {viewMode === "cards" ? (
377
+ <div className="p-4 space-y-3 max-h-[400px] overflow-y-auto">
378
+ {currentData.length > 0 ? (
379
+ currentData.map((row, i) => (
380
+ <div key={i}>
381
+ {renderCard ? renderCard(row) : renderDefaultCard(row)}
382
+ </div>
383
+ ))
384
+ ) : (
385
+ <div className="flex flex-col items-center justify-center py-12 text-secondary-400">
386
+ <ITText as="span" className="text-lg">No se encontraron resultados</ITText>
387
+ <ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
388
+ </div>
389
+ )}
390
+ </div>
391
+ ) : (
392
+ <div className="overflow-x-auto">
393
+ <table
394
+ className={clsx(
395
+ "min-w-max w-full text-sm text-left text-secondary-600",
396
+ showVerticalBorder && "[&_th]:border-r [&_th:last-child]:border-r-0 [&_td]:border-r [&_td:last-child]:border-r-0",
397
+ showVerticalBorder && (verticalBorderClassname || "[&_th]:border-slate-100 dark:[&_th]:border-slate-700/30 [&_td]:border-slate-100 dark:[&_td]:border-slate-700/30"),
398
+ variantStyles[variant],
399
+ sizeStyles[size]
400
+ )}
401
+ >
402
+ <thead>
403
+ <tr className="bg-secondary-50 text-xs uppercase tracking-wider font-semibold text-slate-700 dark:text-slate-200">
404
+ {columns.map((col) => (
405
+ <th
406
+ key={col.key}
407
+ scope="col"
408
+ className={clsx("px-4 py-4 align-top", col.className)}
409
+ >
410
+ <div className="flex flex-col gap-3 min-w-[150px]">
411
+ <div className="flex items-center justify-between gap-2">
412
+ <ITText as="span" className="text-slate-900 dark:text-white font-bold">{col.label}</ITText>
413
+ {col.sortable && col.type !== "actions" && (
414
+ <button
415
+ onClick={() => handleSort(col.key)}
416
+ className={`p-1 rounded-md transition-colors ${sortConfig?.key === col.key
417
+ ? "bg-secondary-200 text-secondary-900"
418
+ : "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
419
+ }`}
420
+ title={`Ordenar por ${col.label}`}
421
+ >
422
+ <MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
423
+ </button>
424
+ )}
425
+ </div>
426
+ <div className="w-full">
427
+ {col.filter ? renderFilterInput(col) : null}
428
+ </div>
308
429
  </div>
309
-
310
- {/* Filter section */}
311
- <div className="w-full">
312
- {col.filter ? renderFilterInput(col) : null}
430
+ </th>
431
+ ))}
432
+ </tr>
433
+ </thead>
434
+ <tbody className="divide-y divide-slate-100 dark:divide-slate-700/30">
435
+ {currentData.length > 0 ? (
436
+ currentData.map((row, rowIndex) => (
437
+ <tr
438
+ key={rowIndex}
439
+ className={clsx("hover:bg-secondary-50/50 transition-colors duration-150 group", variant === "striped" && "odd:bg-secondary-50/40 dark:odd:bg-slate-800/20")}
440
+ >
441
+ {columns.map((col) => (
442
+ <td
443
+ key={`${rowIndex}-${col.key}`}
444
+ className={clsx("px-4 py-3 align-middle", col.className)}
445
+ >
446
+ {col.type === "actions" ? (
447
+ <div className="flex items-center justify-center gap-2">
448
+ {renderCellContent(col, row) as React.ReactNode}
449
+ </div>
450
+ ) : (
451
+ <div className="text-secondary-700 font-medium">
452
+ {renderCellContent(col, row) as React.ReactNode}
453
+ </div>
454
+ )}
455
+ </td>
456
+ ))}
457
+ </tr>
458
+ ))
459
+ ) : (
460
+ <tr>
461
+ <td colSpan={columns.length} className="px-6 py-12 text-center">
462
+ <div className="flex flex-col items-center justify-center text-secondary-400">
463
+ <ITText as="span" className="text-lg">No se encontraron resultados</ITText>
464
+ <ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
313
465
  </div>
314
- </div>
315
- </th>
316
- ))}
317
- </tr>
318
- </thead>
319
- <tbody className="divide-y divide-secondary-100">
320
- {currentData.length > 0 ? (
321
- currentData.map((row, rowIndex) => (
322
- <tr
323
- key={rowIndex}
324
- className="hover:bg-secondary-50/50 transition-colors duration-150 group"
325
- >
326
- {columns.map((col) => (
327
- <td
328
- key={`${rowIndex}-${col.key}`}
329
- className={clsx("px-4 py-3 align-middle", col.className)}
330
- >
331
- {col.type === "actions" ? (
332
- <div className="flex items-center justify-center gap-2">
333
- {renderCellContent(col, row) as React.ReactNode}
334
- </div>
335
- ) : (
336
- <div className="text-secondary-700 font-medium">
337
- {renderCellContent(col, row) as React.ReactNode}
338
- </div>
339
- )}
340
- </td>
341
- ))}
466
+ </td>
342
467
  </tr>
343
- ))
344
- ) : (
345
- <tr>
346
- <td colSpan={columns.length} className="px-6 py-12 text-center">
347
- <div className="flex flex-col items-center justify-center text-secondary-400">
348
- <ITText as="span" className="text-lg">No se encontraron resultados</ITText>
349
- <ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
350
- </div>
351
- </td>
352
- </tr>
353
- )}
354
- </tbody>
355
- </table>
356
- </div>
357
-
468
+ )}
469
+ </tbody>
470
+ </table>
471
+ </div>
472
+ )}
358
473
 
359
474
  {/* Pagination */}
360
- <div className="rounded-b-xl border-t border-secondary-200 px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
475
+ <div className="rounded-b-xl px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
361
476
  <ITPagination
362
477
  currentPage={currentPage}
363
478
  totalPages={computedTotalPages}
@@ -0,0 +1,52 @@
1
+ import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
2
+ import * as Stories from './tabs.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # ITTabs
7
+
8
+ Tab navigation component with multiple visual variants, orientation options, and color themes.
9
+
10
+ ## Props
11
+
12
+ | Prop | Type | Default | Description |
13
+ |------|------|---------|-------------|
14
+ | `tabs` | `{ label: string; icon?: ReactNode; disabled?: boolean }[]` | — | Tab definitions |
15
+ | `activeTab` | `number` | `0` | Zero-indexed active tab |
16
+ | `onChange` | `(index: number) => void` | — | Tab change handler |
17
+ | `variant` | `'default' \| 'pills' \| 'underline' \| 'enclosed'` | `'default'` | Visual style variant |
18
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab layout orientation |
19
+ | `fullWidth` | `boolean` | `false` | Stretch tabs to fill container |
20
+ | `color` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'neutral'` | `'primary'` | Color theme |
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ import { ITTabs } from '@axzydev/axzy_ui_system';
26
+
27
+ const tabs = [
28
+ { label: 'General' },
29
+ { label: 'Security', icon: <LockIcon /> },
30
+ { label: 'Billing', disabled: true },
31
+ ];
32
+
33
+ <ITTabs tabs={tabs} activeTab={0} onChange={(i) => console.log(i)} />
34
+ <ITTabs tabs={tabs} variant="pills" fullWidth />
35
+ <ITTabs tabs={tabs} variant="underline" orientation="vertical" color="success" />
36
+ <ITTabs tabs={tabs} variant="enclosed" />
37
+ ```
38
+
39
+ ## Notes
40
+
41
+ - Four visual variants: default, pills, underline, enclosed
42
+ - Horizontal or vertical tab layout
43
+ - `fullWidth` makes tabs span the full container width
44
+ - Icons rendered alongside tab labels via the `icon` prop
45
+ - Individual tabs can be disabled
46
+ - Seven color themes available
47
+
48
+ ## Stories
49
+
50
+ <Primary />
51
+ <Controls />
52
+ <Stories />
@@ -1,18 +1,29 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
3
  export interface ITTabItem {
4
+ /** Unique identifier for the tab. */
4
5
  id: string;
6
+ /** Display label shown on the tab button. */
5
7
  label: string;
8
+ /** Content rendered when this tab is active. */
6
9
  content: ReactNode;
10
+ /** Optional icon rendered beside the label. */
7
11
  icon?: ReactNode;
12
+ /** Whether the tab is disabled and non-interactive. */
8
13
  disabled?: boolean;
9
14
  }
10
15
 
11
16
  export interface ITTabsProps {
17
+ /** Array of tab definitions: id, label, content, icon, and disabled state. */
12
18
  items: ITTabItem[];
19
+ /** The id of the tab active by default (first item if omitted). */
13
20
  defaultActiveId?: string;
21
+ /** Callback fired when the active tab changes, receiving the new tab id. */
14
22
  onChange?: (id: string) => void;
23
+ /** Visual variant: "line" (underline indicator) or "pill" (rounded background). */
15
24
  variant?: 'line' | 'pill';
25
+ /** Additional CSS classes for the tab header row. */
16
26
  className?: string;
27
+ /** Additional CSS classes for the outermost wrapper. */
17
28
  containerClassName?: string;
18
29
  }
@@ -3,6 +3,20 @@ import { ITTabsProps } from './tabs.props';
3
3
  import { clsx } from 'clsx';
4
4
  import ITText from "@/components/text/text";
5
5
 
6
+ /**
7
+ * Tabbed navigation component with "line" and "pill" visual variants.
8
+ * Manages its own active state internally via a defaultActiveId.
9
+ *
10
+ * @example
11
+ * <ITTabs
12
+ * items={[
13
+ * { id: "tab1", label: "General", content: <GeneralPanel /> },
14
+ * { id: "tab2", label: "Settings", content: <SettingsPanel />, disabled: true },
15
+ * ]}
16
+ * defaultActiveId="tab1"
17
+ * onChange={(id) => console.log("Active tab:", id)}
18
+ * />
19
+ */
6
20
  const ITTabs: React.FC<ITTabsProps> = ({
7
21
  items,
8
22
  defaultActiveId,
@@ -0,0 +1,41 @@
1
+ import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
2
+ import * as Stories from './text.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # ITText
7
+
8
+ Semantic typography wrapper rendering any HTML text element. Foundation for all text in the system.
9
+
10
+ ## Props
11
+
12
+ | Prop | Type | Default | Description |
13
+ |------|------|---------|-------------|
14
+ | `as` | `'p' \| 'span' \| 'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'small' \| 'label' \| 'strong' \| 'em'` | `'p'` | HTML element to render |
15
+ | `muted` | `boolean` | `false` | Apply muted style via CSS variable |
16
+ | `children` | `ReactNode` | — | Text content |
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { ITText } from '@axzydev/axzy_ui_system';
22
+
23
+ <ITText>Default paragraph text</ITText>
24
+ <ITText as="h1">Heading level 1</ITText>
25
+ <ITText as="span" muted>Muted secondary text</ITText>
26
+ <ITText as="small">Small print</ITText>
27
+ <ITText as="strong">Bold emphasis</ITText>
28
+ ```
29
+
30
+ ## Notes
31
+
32
+ - Polymorphic `as` prop renders any HTML text-level element
33
+ - `muted` toggles a CSS variable for reduced-importance text
34
+ - Used as the foundational text component throughout the system
35
+ - Renders default paragraph (`<p>`) when no `as` specified
36
+
37
+ ## Stories
38
+
39
+ <Primary />
40
+ <Controls />
41
+ <Stories />
@@ -1,9 +1,14 @@
1
1
  import { ReactNode, ElementType, HTMLAttributes } from "react";
2
2
 
3
3
  export interface ITTextProps extends HTMLAttributes<HTMLElement> {
4
+ /** The content rendered inside the element. */
4
5
  children?: ReactNode;
6
+ /** Additional CSS classes applied to the element. */
5
7
  className?: string;
8
+ /** HTML element type to render (e.g. "p", "span", "h1", "label"). */
6
9
  as?: ElementType;
10
+ /** Applies muted text color (--color-text-muted) when true. */
7
11
  muted?: boolean;
12
+ /** Associates a label with a form element (rendered as `htmlFor` when `as` is "label"). */
8
13
  htmlFor?: string;
9
14
  }
@@ -0,0 +1,67 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import ITText from './text';
3
+
4
+ const meta = {
5
+ title: 'Components/Layout & Navigation/ITText',
6
+ component: ITText,
7
+ parameters: { layout: 'centered' },
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ as: {
11
+ control: 'select',
12
+ options: ['p', 'span', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'small', 'strong', 'em', 'label'],
13
+ description: 'HTML element to render',
14
+ },
15
+ children: { control: 'text', description: 'Text content' },
16
+ muted: { control: 'boolean', description: 'Apply muted text color' },
17
+ className: { control: 'text', description: 'Additional CSS classes' },
18
+ },
19
+ } satisfies Meta<typeof ITText>;
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+
24
+ export const Paragraph: Story = {
25
+ args: {
26
+ as: 'p',
27
+ children: 'This is a paragraph of text rendered via ITText.',
28
+ className: 'text-base',
29
+ },
30
+ };
31
+
32
+ export const Headings: Story = {
33
+ render: () => (
34
+ <div className="space-y-3">
35
+ <ITText as="h1" className="text-4xl font-bold">Heading 1</ITText>
36
+ <ITText as="h2" className="text-3xl font-bold">Heading 2</ITText>
37
+ <ITText as="h3" className="text-2xl font-semibold">Heading 3</ITText>
38
+ <ITText as="h4" className="text-xl font-semibold">Heading 4</ITText>
39
+ <ITText as="h5" className="text-lg font-medium">Heading 5</ITText>
40
+ <ITText as="h6" className="text-base font-medium">Heading 6</ITText>
41
+ </div>
42
+ ),
43
+ };
44
+
45
+ export const Muted: Story = {
46
+ args: {
47
+ as: 'p',
48
+ children: 'This text appears muted / secondary.',
49
+ muted: true,
50
+ className: 'text-sm',
51
+ },
52
+ };
53
+
54
+ export const InlineElements: Story = {
55
+ render: () => (
56
+ <div className="space-y-2">
57
+ <ITText as="p">
58
+ This is <ITText as="strong" className="font-bold">bold</ITText>,{' '}
59
+ <ITText as="em" className="italic">italic</ITText>, and{' '}
60
+ <ITText as="small" className="text-xs">small</ITText> text.
61
+ </ITText>
62
+ <ITText as="p">
63
+ A <ITText as="span" className="text-primary-600 font-mono">span</ITText> can be used for inline styling.
64
+ </ITText>
65
+ </div>
66
+ ),
67
+ };
@@ -1,5 +1,15 @@
1
1
  import { ITTextProps } from "./text.props";
2
2
 
3
+ /**
4
+ * Semantic typography wrapper that renders any HTML element.
5
+ * Supports muted styling via the `muted` prop and passes through all standard HTML attributes.
6
+ *
7
+ * @example
8
+ * <ITText as="h1" className="text-2xl font-bold">Welcome</ITText>
9
+ *
10
+ * @example
11
+ * <ITText as="label" htmlFor="email" muted>Email Address</ITText>
12
+ */
3
13
  export default function ITText({
4
14
  children,
5
15
  as: Tag = "p",
@@ -0,0 +1,64 @@
1
+ import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
2
+ import * as Stories from './textarea.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # ITTextarea
7
+
8
+ Multi-line text input with label, validation error display, character limit, and configurable resize behavior. Built on native `<textarea>` with consistent styling and focus ring effects.
9
+
10
+ ## Props
11
+
12
+ | Prop | Type | Default | Description |
13
+ |------|------|---------|-------------|
14
+ | `value` | `string` | — | Controlled value of the textarea. |
15
+ | `onChange` | `(value: string) => void` | — | Callback fired when the value changes, receiving the new string. |
16
+ | `label` | `string` | — | Label text rendered above the textarea. |
17
+ | `placeholder` | `string` | — | Placeholder text shown when the textarea is empty. |
18
+ | `rows` | `number` | `4` | Number of visible text rows. |
19
+ | `disabled` | `boolean` | `false` | Disables the textarea when true. |
20
+ | `error` | `string` | — | Validation error message displayed below the textarea. |
21
+ | `className` | `string` | — | Additional CSS classes for the wrapper. |
22
+ | `name` | `string` | — | Name attribute for form submission and label association via `htmlFor`. |
23
+ | `maxLength` | `number` | — | Maximum number of characters allowed. |
24
+ | `resize` | `"none" \| "vertical" \| "horizontal" \| "both"` | `"vertical"` | Resize behavior of the textarea. |
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ import { ITTextarea } from '@axzydev/axzy_ui_system';
30
+
31
+ <ITTextarea
32
+ name="bio"
33
+ label="Biography"
34
+ value={bio}
35
+ onChange={setBio}
36
+ maxLength={500}
37
+ rows={6}
38
+ resize="vertical"
39
+ error={bio.length > 500 ? "Max 500 characters" : undefined}
40
+ />
41
+
42
+ <ITTextarea
43
+ name="comments"
44
+ label="Comments"
45
+ placeholder="Write your feedback..."
46
+ rows={3}
47
+ resize="none"
48
+ disabled
49
+ />
50
+ ```
51
+
52
+ ## Notes
53
+
54
+ - **resize** controls the native CSS resize property: `none` (no resize handle), `vertical` (height only), `horizontal` (width only), or `both`.
55
+ - **error** renders a red error message below the textarea and applies red border/ring styling.
56
+ - **maxLength** enforces a hard character limit via the native `maxLength` attribute — no visual counter included.
57
+ - **rows** only sets the initial visible height; combined with `resize`, the user can adjust as needed.
58
+ - Disabled state applies `opacity-50` and `cursor-not-allowed` with a gray background.
59
+
60
+ ## Stories
61
+
62
+ <Primary />
63
+ <Controls />
64
+ <Stories />