@axzydev/axzy_ui_system 1.4.6 → 1.4.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.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.4.6",
7
+ "version": "1.4.8",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
@@ -58,8 +58,15 @@ export function ITLineBarChart({
58
58
 
59
59
  const active = hover !== null ? data[hover] : null;
60
60
 
61
+ /** Posición del tooltip en % del ancho, para que siga al punto sin salir del gráfico. */
62
+ const tooltipLeft = (() => {
63
+ if (hover === null) return 0;
64
+ const center = (x(hover) / width) * 100;
65
+ return Math.min(Math.max(center, 12), 88);
66
+ })();
67
+
61
68
  return (
62
- <div className={`flex flex-col gap-3 ${className}`.trim()}>
69
+ <div className={`relative flex flex-col gap-3 ${className}`.trim()}>
63
70
  <svg
64
71
  role="img"
65
72
  aria-label={ariaLabel}
@@ -136,22 +143,24 @@ export function ITLineBarChart({
136
143
  </g>
137
144
  ))}
138
145
 
139
- {active && hover !== null && (
140
- <g transform={`translate(${Math.min(Math.max(x(hover) - 62, 4), width - 132)}, ${padding.top + 4})`}>
141
- <rect width={128} height={46} rx={6} fill="#0f172a" opacity={0.9} />
142
- <text x={10} y={18} fontSize={10} fill="#e2e8f0">
143
- {active.label}
144
- </text>
145
- <text x={10} y={32} fontSize={10} fill={lineColor}>
146
- {lineLabel}: {formatValue(active.line)}
147
- </text>
148
- <text x={10} y={44} fontSize={10} fill="#93c5fd">
149
- {barLabel}: {formatValue(active.bar)}
150
- </text>
151
- </g>
152
- )}
153
146
  </svg>
154
147
 
148
+ {active && (
149
+ <div
150
+ className="pointer-events-none absolute z-10 -translate-x-1/2 rounded-lg px-2.5 py-1.5 shadow-lg"
151
+ style={{ left: `${tooltipLeft}%`, top: 4, background: "#0f172a", opacity: 0.94, minWidth: 132 }}
152
+ role="status"
153
+ >
154
+ <p className="text-[10px] font-semibold" style={{ color: "#e2e8f0" }}>{active.label}</p>
155
+ <p className="text-[11px] tabular-nums" style={{ color: lineColor }}>
156
+ {lineLabel}: {formatValue(active.line)}
157
+ </p>
158
+ <p className="text-[11px] tabular-nums" style={{ color: "#93c5fd" }}>
159
+ {barLabel}: {formatValue(active.bar)}
160
+ </p>
161
+ </div>
162
+ )}
163
+
155
164
  <ul className="flex flex-wrap items-center gap-4 text-[11px]" style={{ color: "#475569" }}>
156
165
  <li>
157
166
  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: lineColor }} aria-hidden />
@@ -33,6 +33,20 @@ Basic data table with column definitions, sorting, and configurable visual varia
33
33
  | `rowHeight` | `number` | `getRowHeight(size, density)` | Assumed uniform row height (px). Size-aware: `size="md"` baseline is 33 / 45 / 53 for compact / normal / comfortable; `size` shifts it by the font-size line box (sm 16px / md 20px / lg 28px). |
34
34
  | `overscan` | `number` | `5` | Extra rows rendered above and below the viewport to avoid blank gaps. |
35
35
  | `stickyHeader` | `boolean` | `false` | Keeps header cells sticky while the virtualized body scrolls. Only effective when `virtualized` is true. |
36
+ | `showViewToggle` | `boolean` | `true` | Renders the Table\|Cards switcher in the toolbar. Hiding it does not disable `defaultView` or `autoCardBreakpoint`. |
37
+ | `colors` | `TableColors` | — | Per-instance color overrides (`headerBg`, `headerText`, `rowBg`, `rowText`, `rowHover`). Written as local `--color-table-*` variables, so they beat the global theme for this instance only. |
38
+ | `design` | `TableDesign` | — | Per-region overrides. Each region (`container`, `card`, `toolbar`, `header`, `headerCell`, `row`, `cell`, `cards`, `footer`) accepts `className` and `style`. |
39
+ | `containerStyle` | `CSSProperties` | — | Inline styles for the outermost wrapper. |
40
+ | `cardClassName` / `cardStyle` | `string` / `CSSProperties` | — | The rounded card surface that wraps toolbar, table and footer. |
41
+ | `toolbarClassName` / `toolbarStyle` | `string` / `CSSProperties` | — | The toolbar strip holding the title and/or the view toggle. |
42
+ | `headerClassName` / `headerStyle` | `string` / `CSSProperties` | — | The `<thead>` row. |
43
+ | `headerCellClassName` / `headerCellStyle` | `string` / `CSSProperties` | — | Every `<th>` header cell. |
44
+ | `rowClassName` / `rowStyle` | `string` / `CSSProperties` | — | Every body `<tr>`. |
45
+ | `cellClassName` / `cellStyle` | `string` / `CSSProperties` | — | Every body `<td>`. |
46
+ | `cardsClassName` / `cardsStyle` | `string` / `CSSProperties` | — | The cards-view scroll wrapper. |
47
+ | `footerClassName` / `footerStyle` | `string` / `CSSProperties` | — | The pagination footer strip. |
48
+
49
+ > When both `design.<region>` and its flat shortcut are passed, the flat shortcut wins.
36
50
 
37
51
  ### Column fields
38
52
 
@@ -43,6 +57,8 @@ Basic data table with column definitions, sorting, and configurable visual varia
43
57
  | `align` | `"left" \| "center" \| "right"` | `"left"` | Horizontal alignment of the column's header and cells. |
44
58
  | `truncate` | `boolean` | `false` | Truncates overflowing cell content with an ellipsis and exposes the raw string/number via the native `title` attribute. |
45
59
  | `pinned` | `"left" \| "right"` | `undefined` | Freezes the column to the given horizontal edge while the table scrolls sideways. `"left"` keeps it flush to the left edge, `"right"` to the right edge. Omit to leave the column unpinned (identical to the previous behavior). Offsets accumulate from each preceding pinned column's pixel `width` / `minWidth`, so give every pinned column of a side a pixel `width`. The pinned cell gets an opaque background (row background + row hover) and a 1px separator shadow on its frozen edge. One pinned column per side is the supported contract. |
60
+ | `headerClassName` / `headerStyle` | `string` / `CSSProperties` | — | Extra classes / inline styles for this column's `<th>` only (merged after `design.headerCell`). |
61
+ | `cellClassName` / `cellStyle` | `string` / `CSSProperties` | — | Extra classes / inline styles for this column's `<td>` only (merged after `design.cell`). |
46
62
  | `filter` | `true \| "catalog" \| "search" \| "date" \| "date-range"` | `false` | Filter control rendered in the header. `true` = text/number input (or boolean toggle when `type: "boolean"`); `"catalog"` = `ITSelect` fed by `catalogOptions.data`; `"search"` = `ITSearchSelect` typeahead fed by `catalogOptions.data` (optional `catalogOptions.onSearch` for server-side search); `"date"` / `"date-range"` = `ITDatePicker` single/range, bounded by `dateFilterOptions`. |
47
63
 
48
64
  ## Usage
@@ -109,7 +125,8 @@ const pinnedColumns = [
109
125
  - **Anti horizontal scroll recipe:** `layout="fixed"` + a `width` on every column + `truncate` on long text + `density="compact"`. `width` accepts a `number` (px) or any CSS length string (`"20%"`, `"12rem"`). A `<colgroup>` is only emitted when at least one column defines a `width`.
110
126
  - **Pinned columns:** set `columns[].pinned: "left" | "right"` to freeze a column to that edge during horizontal scroll (`position: sticky`; the wrapper needs no `relative`). Omitting `pinned` renders exactly as before. Sticky offsets accumulate from each preceding pinned column's pixel `width` / `minWidth`; a non-px width contributes `minWidth ?? 0`, so give every pinned column of a side a pixel `width`. The pinned cell gets an opaque background (`--color-table-rowBg`, row hover `--color-table-rowHover`) and a 1px separator shadow on its frozen edge. Z-layering: pinned body cell `z-[1]` < sticky header `z-10` < pinned header `z-[15]`; it composes with `stickyHeader` and `virtualized`. One pinned column per side is the supported contract. Known limitation: with `variant="striped"` the opaque pinned background covers that column's stripe tint.
111
127
  - With the default `layout="auto"`, `min-w-max` stays active so column widths can still reflow as different cell content enters the window during vertical scroll. Use `layout="fixed"` + column `width` for stable widths.
112
- - `autoCardBreakpoint` swaps to the cards view when the container (not the viewport) is narrower than the given px value; the Table toggle is disabled while forced, the Cards toggle stays enabled.
128
+ - `autoCardBreakpoint` swaps to the cards view when the container (not the viewport) is narrower than the given px value; the Table toggle is disabled while forced, the Cards toggle stays enabled. It is measured in px in both directions.
129
+ - **Design props:** `design` (or the flat `<slot>ClassName` / `<slot>Style` shortcuts, which win when both are passed) styles `container`, `card`, `toolbar`, `header`, `headerCell`, `row`, `cell`, `cards` and `footer`; `columns[]` adds `headerClassName` / `cellClassName` (+ their `*Style`). `colors` writes local `--color-table-*` variables and overrides the global `ITThemeProvider` palette for that instance only. Because the theme pins its table colors with `!important` on `thead tr` / `tbody tr` / `th span` / `td div`, use `colors` (not `style`) to change colors, and `showViewToggle={false}` to hide the Table\|Cards switch.
113
130
  - **Virtualization** applies to the **table view only** — the cards view is never virtualized. It only pays off with a large page size: raise `defaultItemsPerPage` and `itemsPerPageOptions` (defaults are `10` and `[5, 10, 20]`). Rows are assumed to have a uniform `rowHeight`; use `truncate` + `layout="fixed"` for predictable heights.
114
131
  - Scroll resets to the top on page or items-per-page change, but **not** on filter change (known limitation). `aria-rowcount` reflects the current page (+1), not the whole dataset, because pagination is independent.
115
132
  - `virtualizedMaxHeight`, `rowHeight`, `overscan`, and `stickyHeader` only take effect when `virtualized` is true. `stickyHeader` makes each `<th>` sticky using the `--color-table-headerBg` token (fallback `#f8fafc`).
@@ -1,4 +1,4 @@
1
- import { TableDensity, TableSize, TableVariants } from "@/types/table.types";
1
+ import { TableDensity, TableDesignProps, TableSize, TableVariants } from "@/types/table.types";
2
2
 
3
3
  /** Data type of a table column, controls default rendering, filter UI, and sort comparison. */
4
4
  export type ColumnType = "string" | "date" | "number" | "boolean" | "actions" | "catalog";
@@ -31,6 +31,14 @@ export interface Column<T = any> {
31
31
  label: string;
32
32
  /** Additional CSS classes applied to both the header (`<th>`) and body (`<td>`) cells of this column. */
33
33
  className?: string;
34
+ /** Extra CSS classes applied only to this column's header (`<th>`), merged after `className`. */
35
+ headerClassName?: string;
36
+ /** Inline styles applied only to this column's header (`<th>`), merged after the table-level header-cell style. */
37
+ headerStyle?: React.CSSProperties;
38
+ /** Extra CSS classes applied only to this column's body cells (`<td>`), merged after `className`. */
39
+ cellClassName?: string;
40
+ /** Inline styles applied only to this column's body cells (`<td>`), merged after the table-level cell style. */
41
+ cellStyle?: React.CSSProperties;
34
42
  /** Formats numeric values as Mexican pesos (MXN) via `Intl`-based currency formatting. Only applies when `type` is `"number"`. @default false */
35
43
  currencyMX?: boolean;
36
44
  /** Custom action buttons/content rendered for a `"actions"`-type column. Receives the full row object. */
@@ -109,7 +117,7 @@ export interface Column<T = any> {
109
117
  pinned?: "left" | "right";
110
118
  }
111
119
 
112
- export interface ITTableProps<T> {
120
+ export interface ITTableProps<T> extends TableDesignProps {
113
121
  /** Column definitions: key, label, type, sortable behavior, filters, and custom rendering. */
114
122
  columns: Column<T>[];
115
123
  /** Additional CSS classes for the outermost container. */
@@ -74,10 +74,51 @@ export default function ITTable<T extends Record<string, unknown>>({
74
74
  rowHeight,
75
75
  overscan = 5,
76
76
  stickyHeader = false,
77
+ showViewToggle = true,
78
+ design,
79
+ colors,
80
+ containerStyle,
81
+ cardClassName,
82
+ cardStyle,
83
+ toolbarClassName,
84
+ toolbarStyle,
85
+ headerClassName,
86
+ headerStyle,
87
+ headerCellClassName,
88
+ headerCellStyle,
89
+ rowClassName,
90
+ rowStyle,
91
+ cellClassName,
92
+ cellStyle,
93
+ cardsClassName,
94
+ cardsStyle,
95
+ footerClassName,
96
+ footerStyle,
77
97
  }: ITTableProps<T>) {
78
98
  const [viewMode, setViewMode] = useState<"table" | "cards">(defaultView);
79
99
  const { ref: rootRef, width: containerWidth } = useElementSize<HTMLDivElement>();
80
100
 
101
+ // Per-instance color overrides written as local `--color-table-*` variables.
102
+ // Declared on the wrapper (an ancestor), they beat the global theme's
103
+ // `:root` values for this subtree only.
104
+ const colorVars = {
105
+ ...(colors?.headerBg ? { "--color-table-headerBg": colors.headerBg } : {}),
106
+ ...(colors?.headerText ? { "--color-table-headerText": colors.headerText } : {}),
107
+ ...(colors?.rowBg ? { "--color-table-rowBg": colors.rowBg } : {}),
108
+ ...(colors?.rowText ? { "--color-table-rowText": colors.rowText } : {}),
109
+ ...(colors?.rowHover ? { "--color-table-rowHover": colors.rowHover } : {}),
110
+ } as React.CSSProperties;
111
+
112
+ /** Merge a `design` slot with its flat shortcut (flat wins) into className/style. */
113
+ const mergeSlot = (
114
+ slot: { className?: string; style?: React.CSSProperties } | undefined,
115
+ flatClassName?: string,
116
+ flatStyle?: React.CSSProperties
117
+ ) => ({
118
+ className: clsx(slot?.className, flatClassName),
119
+ style: { ...slot?.style, ...flatStyle } as React.CSSProperties,
120
+ });
121
+
81
122
  const isFixed = layout === "fixed";
82
123
  const forcedCards =
83
124
  autoCardBreakpoint > 0 && containerWidth > 0 && containerWidth < autoCardBreakpoint;
@@ -468,73 +509,73 @@ export default function ITTable<T extends Record<string, unknown>>({
468
509
  onRowClick(row, event);
469
510
  };
470
511
 
512
+ const containerSlot = mergeSlot(design?.container, containerClassName, containerStyle);
513
+ const cardSlot = mergeSlot(design?.card, cardClassName, cardStyle);
514
+ const toolbarSlot = mergeSlot(design?.toolbar, toolbarClassName, toolbarStyle);
515
+ const headerSlot = mergeSlot(design?.header, headerClassName, headerStyle);
516
+ const headerCellSlot = mergeSlot(design?.headerCell, headerCellClassName, headerCellStyle);
517
+ const rowSlot = mergeSlot(design?.row, rowClassName, rowStyle);
518
+ const cellSlot = mergeSlot(design?.cell, cellClassName, cellStyle);
519
+ const cardsSlot = mergeSlot(design?.cards, cardsClassName, cardsStyle);
520
+ const footerSlot = mergeSlot(design?.footer, footerClassName, footerStyle);
521
+
522
+ const viewToggle = (
523
+ <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">
524
+ <button
525
+ onClick={() => setViewMode("table")}
526
+ disabled={forcedCards}
527
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
528
+ effectiveView === "table"
529
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
530
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
531
+ }`}
532
+ >
533
+ <FaTable size={11} />
534
+ Table
535
+ </button>
536
+ <button
537
+ onClick={() => setViewMode("cards")}
538
+ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
539
+ effectiveView === "cards"
540
+ ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
541
+ : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
542
+ }`}
543
+ >
544
+ <FaThLarge size={11} />
545
+ Cards
546
+ </button>
547
+ </div>
548
+ );
549
+
471
550
  return (
472
- <div ref={rootRef} className={clsx("space-y-4 w-full", containerClassName)}>
473
- <div className={tableContainer} style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
551
+ <div
552
+ ref={rootRef}
553
+ className={clsx("space-y-4 w-full", containerSlot.className)}
554
+ style={{ ...colorVars, ...containerSlot.style }}
555
+ >
556
+ <div
557
+ className={clsx(tableContainer, cardSlot.className)}
558
+ style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)', ...cardSlot.style }}
559
+ >
474
560
  {/* Header outside overflow */}
475
561
  {title && (
476
- <div className="px-6 py-5 flex items-center justify-between" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
562
+ <div
563
+ className={clsx("px-6 py-5 flex items-center justify-between", toolbarSlot.className)}
564
+ style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)', ...toolbarSlot.style }}
565
+ >
477
566
  <ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
478
- <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">
479
- <button
480
- onClick={() => setViewMode("table")}
481
- disabled={forcedCards}
482
- className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
483
- effectiveView === "table"
484
- ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
485
- : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
486
- }`}
487
- >
488
- <FaTable size={11} />
489
- Table
490
- </button>
491
- <button
492
- onClick={() => setViewMode("cards")}
493
- className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
494
- effectiveView === "cards"
495
- ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
496
- : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
497
- }`}
498
- >
499
- <FaThLarge size={11} />
500
- Cards
501
- </button>
502
- </div>
567
+ {showViewToggle && viewToggle}
503
568
  </div>
504
569
  )}
505
570
 
506
- {!title && (
507
- <div className="flex justify-end px-4 pt-3">
508
- <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">
509
- <button
510
- onClick={() => setViewMode("table")}
511
- disabled={forcedCards}
512
- className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
513
- effectiveView === "table"
514
- ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
515
- : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
516
- }`}
517
- >
518
- <FaTable size={11} />
519
- Table
520
- </button>
521
- <button
522
- onClick={() => setViewMode("cards")}
523
- className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
524
- effectiveView === "cards"
525
- ? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
526
- : "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
527
- }`}
528
- >
529
- <FaThLarge size={11} />
530
- Cards
531
- </button>
532
- </div>
571
+ {!title && showViewToggle && (
572
+ <div className={clsx("flex justify-end px-4 pt-3", toolbarSlot.className)} style={toolbarSlot.style}>
573
+ {viewToggle}
533
574
  </div>
534
575
  )}
535
576
 
536
577
  {effectiveView === "cards" ? (
537
- <div className="p-4 space-y-3 max-h-[400px] overflow-y-auto">
578
+ <div className={clsx("p-4 space-y-3 max-h-[400px] overflow-y-auto", cardsSlot.className)} style={cardsSlot.style}>
538
579
  {currentData.length > 0 ? (
539
580
  currentData.map((row, i) => (
540
581
  <div
@@ -588,7 +629,8 @@ export default function ITTable<T extends Record<string, unknown>>({
588
629
  )}
589
630
  <thead>
590
631
  <tr
591
- className={clsx(tableHeaderRow, "dark:text-slate-200")}
632
+ className={clsx(tableHeaderRow, "dark:text-slate-200", headerSlot.className)}
633
+ style={headerSlot.style}
592
634
  aria-rowindex={isTableVirtual ? 1 : undefined}
593
635
  >
594
636
  {columns.map((col) => {
@@ -604,23 +646,23 @@ export default function ITTable<T extends Record<string, unknown>>({
604
646
  isPinned && "sticky",
605
647
  isStickyHeader && "sticky top-0 bg-secondary-50",
606
648
  isPinned ? "z-[15]" : isStickyHeader && "z-10",
607
- isPinned && (col.pinned === "left" ? "it-table-pinned-left" : "it-table-pinned-right")
649
+ isPinned && (col.pinned === "left" ? "it-table-pinned-left" : "it-table-pinned-right"),
650
+ headerCellSlot.className,
651
+ col.headerClassName
608
652
  )}
609
- style={
610
- col.minWidth != null || isStickyHeader || isPinned
611
- ? {
612
- ...(col.minWidth != null ? { minWidth: `${col.minWidth}px` } : {}),
613
- ...(isStickyHeader || isPinned
614
- ? { backgroundColor: "var(--color-table-headerBg, #f8fafc)" }
615
- : {}),
616
- ...(isPinned
617
- ? pin.side === "left"
618
- ? { left: pin.offset }
619
- : { right: pin.offset }
620
- : {}),
621
- }
622
- : undefined
623
- }
653
+ style={{
654
+ ...(col.minWidth != null ? { minWidth: `${col.minWidth}px` } : {}),
655
+ ...(isStickyHeader || isPinned
656
+ ? { backgroundColor: "var(--color-table-headerBg, #f8fafc)" }
657
+ : {}),
658
+ ...(isPinned
659
+ ? pin.side === "left"
660
+ ? { left: pin.offset }
661
+ : { right: pin.offset }
662
+ : {}),
663
+ ...headerCellSlot.style,
664
+ ...col.headerStyle,
665
+ }}
624
666
  >
625
667
  <div className={isFixed ? "flex flex-col gap-3" : "flex flex-col gap-3 min-w-[150px]"}>
626
668
  <div className="flex items-center justify-between gap-2">
@@ -664,8 +706,10 @@ export default function ITTable<T extends Record<string, unknown>>({
664
706
  className={clsx(
665
707
  tableRow,
666
708
  variant === "striped" && absoluteIndex % 2 === 0 && "bg-secondary-50/40 dark:bg-slate-800/20",
667
- onRowClick && tableRowClickable
709
+ onRowClick && tableRowClickable,
710
+ rowSlot.className
668
711
  )}
712
+ style={rowSlot.style}
669
713
  tabIndex={onRowClick ? 0 : undefined}
670
714
  onClick={onRowClick ? handleRowClick(row) : undefined}
671
715
  onKeyDown={onRowClick ? handleRowKeyDown(row) : undefined}
@@ -687,20 +731,20 @@ export default function ITTable<T extends Record<string, unknown>>({
687
731
  col.align && tableAlignClasses[col.align],
688
732
  isPinned && "sticky bg-[var(--color-table-rowBg,white)] group-hover:bg-[var(--color-table-rowHover,var(--color-secondary-50))]",
689
733
  isPinned && (col.pinned === "left" ? "it-table-pinned-left" : "it-table-pinned-right"),
690
- isPinned && "z-[1]"
734
+ isPinned && "z-[1]",
735
+ cellSlot.className,
736
+ col.cellClassName
691
737
  )}
692
- style={
693
- col.minWidth != null || isPinned
694
- ? {
695
- ...(col.minWidth != null ? { minWidth: `${col.minWidth}px` } : {}),
696
- ...(isPinned
697
- ? pin.side === "left"
698
- ? { left: pin.offset }
699
- : { right: pin.offset }
700
- : {}),
701
- }
702
- : undefined
703
- }
738
+ style={{
739
+ ...(col.minWidth != null ? { minWidth: `${col.minWidth}px` } : {}),
740
+ ...(isPinned
741
+ ? pin.side === "left"
742
+ ? { left: pin.offset }
743
+ : { right: pin.offset }
744
+ : {}),
745
+ ...cellSlot.style,
746
+ ...col.cellStyle,
747
+ }}
704
748
  title={truncateTitle}
705
749
  >
706
750
  {col.type === "actions" ? (
@@ -740,7 +784,10 @@ export default function ITTable<T extends Record<string, unknown>>({
740
784
  )}
741
785
 
742
786
  {/* Pagination */}
743
- <div className="rounded-b-xl px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
787
+ <div
788
+ className={clsx("rounded-b-xl px-6 py-4", footerSlot.className)}
789
+ style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)', ...footerSlot.style }}
790
+ >
744
791
  <ITPagination
745
792
  currentPage={currentPage}
746
793
  totalPages={computedTotalPages}
@@ -38,6 +38,22 @@ Advanced server-side data table with automatic pagination, column sorting, and f
38
38
  | `rowHeight` | `number` | `getRowHeight(size, density)` | Assumed uniform row height (px). Size-aware: `size="md"` baseline is 33 / 45 / 53 for compact / normal / comfortable; `size` shifts it by the font-size line box (sm 16px / md 20px / lg 28px). |
39
39
  | `overscan` | `number` | `5` | Extra rows rendered above and below the viewport to avoid blank gaps. |
40
40
  | `stickyHeader` | `boolean` | `false` | Keeps header cells sticky while the virtualized body scrolls. Only effective when `virtualized` is true. |
41
+ | `showViewToggle` | `boolean` | `true` | Renders the Table\|Cards switcher in the toolbar. Hiding it does not disable `defaultView` or `autoCardBreakpoint`. |
42
+ | `colors` | `TableColors` | — | Per-instance color overrides (`headerBg`, `headerText`, `rowBg`, `rowText`, `rowHover`). Written as local `--color-table-*` variables, so they beat the global theme for this instance only. |
43
+ | `design` | `TableDesign` | — | Per-region overrides. Each region (`container`, `card`, `toolbar`, `header`, `headerCell`, `row`, `cell`, `cards`, `footer`) accepts `className` and `style`. |
44
+ | `containerStyle` | `CSSProperties` | — | Inline styles for the outermost wrapper. |
45
+ | `cardClassName` / `cardStyle` | `string` / `CSSProperties` | — | The rounded card surface that wraps toolbar, table and footer. |
46
+ | `toolbarClassName` / `toolbarStyle` | `string` / `CSSProperties` | — | The toolbar strip holding the title and/or the view toggle. |
47
+ | `headerClassName` / `headerStyle` | `string` / `CSSProperties` | — | The `<thead>` row. |
48
+ | `headerCellClassName` / `headerCellStyle` | `string` / `CSSProperties` | — | Every `<th>` header cell. |
49
+ | `rowClassName` / `rowStyle` | `string` / `CSSProperties` | — | Every body `<tr>`. |
50
+ | `cellClassName` / `cellStyle` | `string` / `CSSProperties` | — | Every body `<td>`. |
51
+ | `cardsClassName` / `cardsStyle` | `string` / `CSSProperties` | — | The cards-view scroll wrapper. |
52
+ | `footerClassName` / `footerStyle` | `string` / `CSSProperties` | — | The pagination footer strip. |
53
+
54
+ Columns also accept `headerClassName`, `headerStyle`, `cellClassName` and `cellStyle` for per-column design.
55
+
56
+ > When both `design.<region>` and its flat shortcut are passed, the flat shortcut wins.
41
57
 
42
58
  ## Usage
43
59
 
@@ -102,7 +118,8 @@ import { ITDataTable } from '@axzydev/axzy_ui_system';
102
118
  - **Anti horizontal scroll recipe:** `layout="fixed"` + a `width` on every column + `truncate` on long text + `density="compact"`. `width` accepts a `number` (px) or any CSS length string (`"20%"`, `"12rem"`). A `<colgroup>` is only emitted when at least one column defines a `width`.
103
119
  - **Pinned columns:** set `columns[].pinned: "left" | "right"` to freeze a column to that edge during horizontal scroll (`position: sticky`; the wrapper needs no `relative`). Omitting `pinned` renders exactly as before. Sticky offsets accumulate from each preceding pinned column's pixel `width` / `minWidth`; a non-px width contributes `minWidth ?? 0`, so give every pinned column of a side a pixel `width`. The pinned cell gets an opaque background (`--color-table-rowBg`, row hover `--color-table-rowHover`) and a 1px separator shadow on its frozen edge. Z-layering: pinned body cell `z-[1]` < sticky header `z-10` < pinned header `z-[15]` < the loading overlay `z-20`; it composes with `stickyHeader` and `virtualized`. One pinned column per side is the supported contract. Known limitation: with `variant="striped"` the opaque pinned background covers that column's stripe tint.
104
120
  - With the default `layout="auto"`, `min-w-max` stays active so column widths can still reflow as different cell content enters the window during vertical scroll. Use `layout="fixed"` + column `width` for stable widths.
105
- - `autoCardBreakpoint` swaps to the cards view when the container (not the viewport) is narrower than the given px value; the Table toggle is disabled while forced, the Cards toggle stays enabled.
121
+ - `autoCardBreakpoint` swaps to the cards view when the container (not the viewport) is narrower than the given px value; the Table toggle is disabled while forced, the Cards toggle stays enabled. It is measured in px in both directions.
122
+ - **Design props:** `design` (or the flat `<slot>ClassName` / `<slot>Style` shortcuts, which win when both are passed) styles `container`, `card`, `toolbar`, `header`, `headerCell`, `row`, `cell`, `cards` and `footer`; `columns[]` adds `headerClassName` / `cellClassName` (+ their `*Style`). `colors` writes local `--color-table-*` variables and overrides the global `ITThemeProvider` palette for that instance only. Because the theme pins its table colors with `!important` on `thead tr` / `tbody tr` / `th span` / `td div`, use `colors` (not `style`) to change colors, and `showViewToggle={false}` to hide the Table\|Cards switch.
106
123
  - **Virtualization** applies to the **table view only** — the cards view is never virtualized. It only pays off with a large page size: raise `defaultItemsPerPage` and `itemsPerPageOptions` (defaults are `10` and `[5, 10, 20]`). Rows are assumed to have a uniform `rowHeight`; use `truncate` + `layout="fixed"` for predictable heights.
107
124
  - Scroll resets to the top on page or items-per-page change, but **not** on filter change (known limitation). `aria-rowcount` reflects the current page (+1), not the whole dataset, because pagination is independent.
108
125
  - `virtualizedMaxHeight`, `rowHeight`, `overscan`, and `stickyHeader` only take effect when `virtualized` is true. `stickyHeader` makes each `<th>` sticky using the `--color-table-headerBg` token (fallback `#f8fafc`).
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { Column } from "@/components/molecules/table/table.props";
3
- import { ColumnFilters, TableDensity } from "@/types/table.types";
3
+ import { ColumnFilters, TableDensity, TableDesignProps } from "@/types/table.types";
4
4
 
5
5
  /** Parameters passed to `fetchData` every time pagination, filters, or sorting change. */
6
6
  export interface ITDataTableFetchParams {
@@ -31,7 +31,8 @@ export interface ITDataTableResponse<T> {
31
31
  total: number;
32
32
  }
33
33
 
34
- export interface ITDataTableProps<T extends Record<string, unknown>> {
34
+ export interface ITDataTableProps<T extends Record<string, unknown>>
35
+ extends TableDesignProps {
35
36
  /**
36
37
  * The column configuration array matching ITTable but adapted for Server-Side processing
37
38
  */