@underverse-ui/underverse 0.2.75 → 0.2.76

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/dist/index.cjs CHANGED
@@ -13143,6 +13143,103 @@ function DataTablePagination({
13143
13143
  ] });
13144
13144
  }
13145
13145
 
13146
+ // ../../components/ui/DataTable/utils/headers.ts
13147
+ function isLeafColumn(col) {
13148
+ return !col.children || col.children.length === 0;
13149
+ }
13150
+ function getLeafColumns(columns) {
13151
+ const leaves = [];
13152
+ function traverse(cols) {
13153
+ for (const col of cols) {
13154
+ if (isLeafColumn(col)) {
13155
+ leaves.push(col);
13156
+ } else if (col.children) {
13157
+ traverse(col.children);
13158
+ }
13159
+ }
13160
+ }
13161
+ traverse(columns);
13162
+ return leaves;
13163
+ }
13164
+ function getHeaderDepth(columns) {
13165
+ function getDepth(cols) {
13166
+ if (!cols || cols.length === 0) return 0;
13167
+ let maxDepth = 1;
13168
+ for (const col of cols) {
13169
+ if (col.children && col.children.length > 0) {
13170
+ maxDepth = Math.max(maxDepth, 1 + getDepth(col.children));
13171
+ }
13172
+ }
13173
+ return maxDepth;
13174
+ }
13175
+ return getDepth(columns);
13176
+ }
13177
+ function getColSpan(col) {
13178
+ if (col.colSpan !== void 0) return col.colSpan;
13179
+ if (isLeafColumn(col)) return 1;
13180
+ return col.children.reduce((sum, child) => sum + getColSpan(child), 0);
13181
+ }
13182
+ function getRowSpan(col, maxDepth, currentDepth) {
13183
+ if (col.rowSpan !== void 0) return col.rowSpan;
13184
+ if (isLeafColumn(col)) {
13185
+ return maxDepth - currentDepth + 1;
13186
+ }
13187
+ return 1;
13188
+ }
13189
+ function buildHeaderRows(columns) {
13190
+ const maxDepth = getHeaderDepth(columns);
13191
+ const rows = Array.from({ length: maxDepth }, () => []);
13192
+ function buildCell(col, rowIndex, colIndex) {
13193
+ const colSpan = getColSpan(col);
13194
+ const rowSpan = getRowSpan(col, maxDepth, rowIndex + 1);
13195
+ const isLeaf = isLeafColumn(col);
13196
+ rows[rowIndex].push({
13197
+ column: col,
13198
+ colSpan,
13199
+ rowSpan,
13200
+ isLeaf,
13201
+ rowIndex,
13202
+ colIndex
13203
+ });
13204
+ if (col.children && col.children.length > 0) {
13205
+ let childColIndex = colIndex;
13206
+ for (const child of col.children) {
13207
+ childColIndex = buildCell(child, rowIndex + 1, childColIndex);
13208
+ }
13209
+ }
13210
+ return colIndex + colSpan;
13211
+ }
13212
+ let currentColIndex = 0;
13213
+ for (const col of columns) {
13214
+ currentColIndex = buildCell(col, 0, currentColIndex);
13215
+ }
13216
+ return rows;
13217
+ }
13218
+ function filterVisibleColumns(columns, visibleKeys) {
13219
+ return columns.map((col) => {
13220
+ if (col.children) {
13221
+ const visibleChildren = filterVisibleColumns(col.children, visibleKeys);
13222
+ if (visibleChildren.length > 0) {
13223
+ return { ...col, children: visibleChildren };
13224
+ }
13225
+ return null;
13226
+ }
13227
+ return visibleKeys.has(col.key) ? col : null;
13228
+ }).filter((col) => col !== null);
13229
+ }
13230
+ function getLeafColumnsWithFixedInheritance(columns, inheritedFixed) {
13231
+ const leaves = [];
13232
+ for (const col of columns) {
13233
+ const effectiveFixed = col.fixed ?? inheritedFixed;
13234
+ if (isLeafColumn(col)) {
13235
+ leaves.push({ ...col, fixed: effectiveFixed });
13236
+ } else if (col.children) {
13237
+ leaves.push(...getLeafColumnsWithFixedInheritance(col.children, effectiveFixed));
13238
+ }
13239
+ }
13240
+ return leaves;
13241
+ }
13242
+
13146
13243
  // ../../components/ui/DataTable/components/Toolbar.tsx
13147
13244
  var import_jsx_runtime58 = require("react/jsx-runtime");
13148
13245
  function DataTableToolbar({
@@ -13177,36 +13274,39 @@ function DataTableToolbar({
13177
13274
  ]
13178
13275
  }
13179
13276
  ),
13180
- enableColumnVisibilityToggle && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
13181
- DropdownMenu_default,
13182
- {
13183
- trigger: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Button_default, { variant: "ghost", size: "sm", className: "h-8 px-2", children: [
13184
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("svg", { className: "w-4 h-4 mr-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
13185
- "path",
13277
+ enableColumnVisibilityToggle && (() => {
13278
+ const leafCols = getLeafColumns(columns);
13279
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
13280
+ DropdownMenu_default,
13281
+ {
13282
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Button_default, { variant: "ghost", size: "sm", className: "h-8 px-2", children: [
13283
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("svg", { className: "w-4 h-4 mr-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
13284
+ "path",
13285
+ {
13286
+ strokeLinecap: "round",
13287
+ strokeLinejoin: "round",
13288
+ strokeWidth: 2,
13289
+ d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"
13290
+ }
13291
+ ) }),
13292
+ labels?.columns || t("columns")
13293
+ ] }),
13294
+ children: leafCols.map((c) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
13295
+ DropdownMenuItem,
13186
13296
  {
13187
- strokeLinecap: "round",
13188
- strokeLinejoin: "round",
13189
- strokeWidth: 2,
13190
- d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"
13191
- }
13192
- ) }),
13193
- labels?.columns || t("columns")
13194
- ] }),
13195
- children: columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
13196
- DropdownMenuItem,
13197
- {
13198
- onClick: () => {
13199
- setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
13297
+ onClick: () => {
13298
+ setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
13299
+ },
13300
+ children: [
13301
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("input", { type: "checkbox", className: "mr-2 rounded-md border-border", readOnly: true, checked: visibleCols.includes(c.key) }),
13302
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "truncate", children: c.title })
13303
+ ]
13200
13304
  },
13201
- children: [
13202
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("input", { type: "checkbox", className: "mr-2 rounded-md border-border", readOnly: true, checked: visibleCols.includes(c.key) }),
13203
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "truncate", children: c.title })
13204
- ]
13205
- },
13206
- c.key
13207
- ))
13208
- }
13209
- ),
13305
+ c.key
13306
+ ))
13307
+ }
13308
+ );
13309
+ })(),
13210
13310
  enableHeaderAlignToggle && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
13211
13311
  DropdownMenu_default,
13212
13312
  {
@@ -13281,32 +13381,40 @@ var import_react34 = __toESM(require("react"), 1);
13281
13381
  // ../../components/ui/DataTable/utils/columns.ts
13282
13382
  function getColumnWidth(col, fallback = 150) {
13283
13383
  if (typeof col.width === "number") return col.width;
13284
- const raw = col.width ? String(col.width) : String(fallback);
13285
- const parsed = parseInt(raw, 10);
13286
- return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
13384
+ if (col.width) {
13385
+ const raw = String(col.width);
13386
+ const parsed = parseInt(raw, 10);
13387
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
13388
+ }
13389
+ if (col.children && col.children.length > 0) {
13390
+ return col.children.reduce((sum, child) => sum + getColumnWidth(child, fallback), 0);
13391
+ }
13392
+ return fallback;
13287
13393
  }
13288
13394
 
13289
13395
  // ../../components/ui/DataTable/hooks/useStickyColumns.ts
13290
- function useStickyColumns(visibleColumns) {
13396
+ function useStickyColumns(columns, visibleKeys) {
13397
+ const visibleColumns = import_react34.default.useMemo(() => filterVisibleColumns(columns, visibleKeys), [columns, visibleKeys]);
13398
+ const leafColumns = import_react34.default.useMemo(() => getLeafColumnsWithFixedInheritance(visibleColumns), [visibleColumns]);
13291
13399
  const stickyPositions = import_react34.default.useMemo(() => {
13292
13400
  const positions = {};
13293
13401
  let leftOffset = 0;
13294
- for (const col of visibleColumns) {
13402
+ for (const col of leafColumns) {
13295
13403
  if (col.fixed === "left") {
13296
13404
  positions[col.key] = { left: leftOffset };
13297
13405
  leftOffset += getColumnWidth(col);
13298
13406
  }
13299
13407
  }
13300
13408
  let rightOffset = 0;
13301
- for (let i = visibleColumns.length - 1; i >= 0; i--) {
13302
- const col = visibleColumns[i];
13409
+ for (let i = leafColumns.length - 1; i >= 0; i--) {
13410
+ const col = leafColumns[i];
13303
13411
  if (col.fixed === "right") {
13304
13412
  positions[col.key] = { right: rightOffset };
13305
13413
  rightOffset += getColumnWidth(col);
13306
13414
  }
13307
13415
  }
13308
13416
  return positions;
13309
- }, [visibleColumns]);
13417
+ }, [leafColumns]);
13310
13418
  const getStickyColumnStyle = import_react34.default.useCallback(
13311
13419
  (col) => {
13312
13420
  if (!col.fixed) return {};
@@ -13336,7 +13444,109 @@ function useStickyColumns(visibleColumns) {
13336
13444
  isStripedRow ? "bg-muted!" : "bg-card!"
13337
13445
  );
13338
13446
  }, []);
13339
- return { getStickyColumnStyle, getStickyHeaderClass, getStickyCellClass };
13447
+ const getStickyHeaderCellStyle = import_react34.default.useCallback(
13448
+ (headerCell) => {
13449
+ const col = headerCell.column;
13450
+ if (headerCell.isLeaf) {
13451
+ return getStickyColumnStyle(col);
13452
+ }
13453
+ const descendants = getLeafColumns([col]);
13454
+ const stickyDescendants = descendants.filter((d) => d.fixed);
13455
+ if (stickyDescendants.length === 0) return {};
13456
+ const firstSticky = stickyDescendants[0];
13457
+ const lastSticky = stickyDescendants[stickyDescendants.length - 1];
13458
+ if (firstSticky.fixed === "left") {
13459
+ const pos = stickyPositions[firstSticky.key];
13460
+ return pos?.left !== void 0 ? { left: pos.left } : {};
13461
+ }
13462
+ if (lastSticky.fixed === "right") {
13463
+ const pos = stickyPositions[lastSticky.key];
13464
+ return pos?.right !== void 0 ? { right: pos.right } : {};
13465
+ }
13466
+ return {};
13467
+ },
13468
+ [stickyPositions, getStickyColumnStyle]
13469
+ );
13470
+ return {
13471
+ getStickyColumnStyle,
13472
+ getStickyHeaderClass,
13473
+ getStickyCellClass,
13474
+ getStickyHeaderCellStyle
13475
+ };
13476
+ }
13477
+
13478
+ // ../../components/ui/DataTable/utils/validation.ts
13479
+ function validateColumns(columns) {
13480
+ const warnings = [];
13481
+ const keys = /* @__PURE__ */ new Set();
13482
+ function validate(cols, path = "") {
13483
+ for (const col of cols) {
13484
+ const fullPath = path ? `${path}.${col.key}` : col.key;
13485
+ if (keys.has(col.key)) {
13486
+ warnings.push(`Duplicate key "${col.key}" at ${fullPath}`);
13487
+ }
13488
+ keys.add(col.key);
13489
+ const isGroup = col.children && col.children.length > 0;
13490
+ if (isGroup) {
13491
+ if (col.dataIndex) {
13492
+ warnings.push(`Group column "${fullPath}" has dataIndex (will be ignored)`);
13493
+ }
13494
+ if (col.sortable) {
13495
+ warnings.push(`Group column "${fullPath}" has sortable (will be ignored)`);
13496
+ }
13497
+ if (col.filter) {
13498
+ warnings.push(`Group column "${fullPath}" has filter (will be ignored)`);
13499
+ }
13500
+ if (col.render) {
13501
+ warnings.push(`Group column "${fullPath}" has render function (will be ignored)`);
13502
+ }
13503
+ if (col.colSpan !== void 0) {
13504
+ const actualColSpan = getColSpan(col);
13505
+ if (col.colSpan !== actualColSpan) {
13506
+ warnings.push(
13507
+ `Column "${fullPath}" has colSpan=${col.colSpan} but structure suggests ${actualColSpan} (based on ${col.children.length} children)`
13508
+ );
13509
+ }
13510
+ }
13511
+ if (col.fixed) {
13512
+ const conflictingChildren = col.children.filter((c) => c.fixed && c.fixed !== col.fixed);
13513
+ if (conflictingChildren.length > 0) {
13514
+ warnings.push(
13515
+ `Group column "${fullPath}" has fixed="${col.fixed}" but children have different fixed values: ${conflictingChildren.map((c) => c.key).join(", ")}`
13516
+ );
13517
+ }
13518
+ }
13519
+ validate(col.children, fullPath);
13520
+ } else {
13521
+ if (col.children !== void 0) {
13522
+ warnings.push(`Leaf column "${fullPath}" has children property (should be omitted for leaf columns)`);
13523
+ }
13524
+ }
13525
+ }
13526
+ }
13527
+ validate(columns);
13528
+ const depth = getHeaderDepth(columns);
13529
+ if (depth > 4) {
13530
+ warnings.push(`Header depth is ${depth} rows. Consider simplifying - too many header rows may impact user experience.`);
13531
+ }
13532
+ function checkMixedSticky(cols, parentPath = "") {
13533
+ for (const col of cols) {
13534
+ if (col.children && col.children.length > 0) {
13535
+ const childrenFixed = col.children.map((c) => c.fixed);
13536
+ const hasStickyChild = childrenFixed.some((f) => f !== void 0);
13537
+ const hasNonStickyChild = childrenFixed.some((f) => f === void 0);
13538
+ if (hasStickyChild && hasNonStickyChild) {
13539
+ const fullPath = parentPath ? `${parentPath}.${col.key}` : col.key;
13540
+ warnings.push(
13541
+ `Group column "${fullPath}" has mixed sticky children (some fixed, some not). This may cause visual separation when scrolling.`
13542
+ );
13543
+ }
13544
+ checkMixedSticky(col.children, parentPath ? `${parentPath}.${col.key}` : col.key);
13545
+ }
13546
+ }
13547
+ }
13548
+ checkMixedSticky(columns);
13549
+ return warnings;
13340
13550
  }
13341
13551
 
13342
13552
  // ../../components/ui/DataTable/DataTable.tsx
@@ -13372,6 +13582,12 @@ function DataTable({
13372
13582
  const [density, setDensity] = import_react35.default.useState("normal");
13373
13583
  const [curPage, setCurPage] = import_react35.default.useState(page);
13374
13584
  const { curPageSize, setCurPageSize } = usePageSizeStorage({ pageSize, storageKey });
13585
+ import_react35.default.useEffect(() => {
13586
+ if (process.env.NODE_ENV === "development") {
13587
+ const warnings = validateColumns(columns);
13588
+ warnings.forEach((w) => console.warn(`[DataTable] ${w}`));
13589
+ }
13590
+ }, [columns]);
13375
13591
  import_react35.default.useEffect(() => {
13376
13592
  const newColKeys = columns.filter((c) => c.visible !== false).map((c) => c.key);
13377
13593
  setVisibleCols((prev) => {
@@ -13396,11 +13612,19 @@ function DataTable({
13396
13612
  const densityRowClass = density === "compact" ? "h-9" : density === "comfortable" ? "h-14" : "h-12";
13397
13613
  const cellPadding = density === "compact" ? "py-1.5 px-3" : density === "comfortable" ? "py-3 px-4" : "py-2.5 px-4";
13398
13614
  const visibleColsSet = import_react35.default.useMemo(() => new Set(visibleCols), [visibleCols]);
13399
- const visibleColumns = columns.filter((c) => visibleColsSet.has(c.key));
13400
- const totalColumnsWidth = import_react35.default.useMemo(() => {
13401
- return visibleColumns.reduce((sum, col) => sum + getColumnWidth(col), 0);
13615
+ const visibleColumns = import_react35.default.useMemo(() => {
13616
+ return filterVisibleColumns(columns, visibleColsSet);
13617
+ }, [columns, visibleColsSet]);
13618
+ const leafColumns = import_react35.default.useMemo(() => {
13619
+ return getLeafColumns(visibleColumns);
13402
13620
  }, [visibleColumns]);
13403
- const { getStickyCellClass, getStickyColumnStyle, getStickyHeaderClass } = useStickyColumns(visibleColumns);
13621
+ const totalColumnsWidth = import_react35.default.useMemo(() => {
13622
+ return leafColumns.reduce((sum, col) => sum + getColumnWidth(col), 0);
13623
+ }, [leafColumns]);
13624
+ const { getStickyCellClass, getStickyColumnStyle, getStickyHeaderClass, getStickyHeaderCellStyle } = useStickyColumns(
13625
+ columns,
13626
+ visibleColsSet
13627
+ );
13404
13628
  const getRowKey = (row, idx) => {
13405
13629
  if (!rowKey) return String(idx);
13406
13630
  if (typeof rowKey === "function") return String(rowKey(row));
@@ -13456,127 +13680,150 @@ function DataTable({
13456
13680
  }
13457
13681
  return null;
13458
13682
  };
13459
- const renderHeader = /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: visibleColumns.map((col, colIdx) => {
13460
- const prevCol = colIdx > 0 ? visibleColumns[colIdx - 1] : null;
13683
+ const headerRows = import_react35.default.useMemo(() => buildHeaderRows(visibleColumns), [visibleColumns]);
13684
+ const renderHeaderContent = (col, isLeaf) => {
13685
+ if (!isLeaf) {
13686
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13687
+ "div",
13688
+ {
13689
+ className: cn(
13690
+ "flex items-center gap-1 min-h-10",
13691
+ col.align === "right" && "justify-end",
13692
+ col.align === "center" && "justify-center",
13693
+ !col.align && "justify-start"
13694
+ ),
13695
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "font-medium text-sm whitespace-nowrap", children: col.title })
13696
+ }
13697
+ );
13698
+ }
13699
+ const isRightAlign = col.align === "right" || !col.align && headerAlign === "right";
13700
+ const isCenterAlign = col.align === "center" || !col.align && headerAlign === "center";
13701
+ const titleContent = /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "flex items-center gap-1", children: [
13702
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "font-medium text-sm whitespace-nowrap", children: col.title }),
13703
+ col.sortable && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13704
+ "button",
13705
+ {
13706
+ className: cn(
13707
+ "p-1 rounded-lg transition-all duration-200 hover:bg-accent",
13708
+ sort?.key === col.key ? "opacity-100 bg-accent" : "opacity-60 hover:opacity-100"
13709
+ ),
13710
+ onClick: () => {
13711
+ setCurPage(1);
13712
+ setSort((s) => {
13713
+ if (!s || s.key !== col.key) return { key: col.key, order: "asc" };
13714
+ if (s.order === "asc") return { key: col.key, order: "desc" };
13715
+ return null;
13716
+ });
13717
+ },
13718
+ "aria-label": "Sort",
13719
+ title: `Sort by ${String(col.title)}`,
13720
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", className: "inline-block", children: [
13721
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13722
+ "path",
13723
+ {
13724
+ d: "M7 8l3-3 3 3",
13725
+ stroke: "currentColor",
13726
+ strokeWidth: "1.5",
13727
+ strokeLinecap: "round",
13728
+ strokeLinejoin: "round",
13729
+ opacity: sort?.key === col.key && sort.order === "asc" ? 1 : 0.4
13730
+ }
13731
+ ),
13732
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13733
+ "path",
13734
+ {
13735
+ d: "M7 12l3 3 3-3",
13736
+ stroke: "currentColor",
13737
+ strokeWidth: "1.5",
13738
+ strokeLinecap: "round",
13739
+ strokeLinejoin: "round",
13740
+ opacity: sort?.key === col.key && sort.order === "desc" ? 1 : 0.4
13741
+ }
13742
+ )
13743
+ ] })
13744
+ }
13745
+ )
13746
+ ] });
13747
+ const filterContent = col.filter ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13748
+ Popover,
13749
+ {
13750
+ placement: "bottom-start",
13751
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13752
+ "button",
13753
+ {
13754
+ className: cn(
13755
+ "p-1.5 rounded-lg transition-all duration-200 hover:bg-accent",
13756
+ filters[col.key] ? "bg-accent text-primary" : "text-muted-foreground"
13757
+ ),
13758
+ "aria-label": "Filter",
13759
+ title: `Filter by ${String(col.title)}`,
13760
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_lucide_react27.Filter, { className: "w-4 h-4" })
13761
+ }
13762
+ ),
13763
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "p-3 w-64 space-y-3", children: [
13764
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "flex items-center justify-between", children: [
13765
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "text-sm font-medium", children: col.title }),
13766
+ filters[col.key] !== void 0 && filters[col.key] !== null && filters[col.key] !== "" && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13767
+ "button",
13768
+ {
13769
+ onClick: () => {
13770
+ setCurPage(1);
13771
+ setFilters((f) => ({ ...f, [col.key]: void 0 }));
13772
+ },
13773
+ className: "text-xs text-destructive hover:underline",
13774
+ children: t("clearFilter")
13775
+ }
13776
+ )
13777
+ ] }),
13778
+ renderFilterControl(col)
13779
+ ] })
13780
+ }
13781
+ ) : null;
13782
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13783
+ "div",
13784
+ {
13785
+ className: cn(
13786
+ "flex items-center gap-2 select-none min-h-10",
13787
+ isRightAlign && "justify-end",
13788
+ isCenterAlign && "justify-center",
13789
+ !isRightAlign && !isCenterAlign && "justify-start"
13790
+ ),
13791
+ children: isRightAlign ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
13792
+ filterContent,
13793
+ titleContent
13794
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
13795
+ titleContent,
13796
+ filterContent
13797
+ ] })
13798
+ }
13799
+ );
13800
+ };
13801
+ const renderHeader = /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_jsx_runtime59.Fragment, { children: headerRows.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: row.map((headerCell, cellIndex) => {
13802
+ const { column: col, colSpan, rowSpan, isLeaf } = headerCell;
13803
+ const prevCell = cellIndex > 0 ? row[cellIndex - 1] : null;
13804
+ const prevCol = prevCell?.column;
13461
13805
  const isAfterFixedLeft = prevCol?.fixed === "left";
13462
- const showBorderLeft = columnDividers && colIdx > 0 && !isAfterFixedLeft && !col.fixed;
13806
+ const showBorderLeft = columnDividers && cellIndex > 0 && !isAfterFixedLeft && !col.fixed;
13463
13807
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13464
13808
  TableHead,
13465
13809
  {
13466
- style: { width: col.width, ...getStickyColumnStyle(col) },
13810
+ colSpan,
13811
+ rowSpan,
13812
+ style: {
13813
+ width: col.width,
13814
+ ...getStickyHeaderCellStyle(headerCell)
13815
+ },
13467
13816
  className: cn(
13468
13817
  (col.align === "right" || !col.align && headerAlign === "right") && "text-right",
13469
13818
  (col.align === "center" || !col.align && headerAlign === "center") && "text-center",
13470
13819
  showBorderLeft && "border-l border-border/60",
13471
13820
  getStickyHeaderClass(col)
13472
13821
  ),
13473
- children: (() => {
13474
- const isRightAlign = col.align === "right" || !col.align && headerAlign === "right";
13475
- const isCenterAlign = col.align === "center" || !col.align && headerAlign === "center";
13476
- const titleContent = /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: cn("flex items-center gap-1", !col.fixed && "min-w-0 shrink"), children: [
13477
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: cn("font-medium text-sm", !col.fixed && "truncate"), children: col.title }),
13478
- col.sortable && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13479
- "button",
13480
- {
13481
- className: cn(
13482
- "p-1 rounded-lg transition-all duration-200 hover:bg-accent",
13483
- sort?.key === col.key ? "opacity-100 bg-accent" : "opacity-60 hover:opacity-100"
13484
- ),
13485
- onClick: () => {
13486
- setCurPage(1);
13487
- setSort((s) => {
13488
- if (!s || s.key !== col.key) return { key: col.key, order: "asc" };
13489
- if (s.order === "asc") return { key: col.key, order: "desc" };
13490
- return null;
13491
- });
13492
- },
13493
- "aria-label": "Sort",
13494
- title: `Sort by ${String(col.title)}`,
13495
- children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", className: "inline-block", children: [
13496
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13497
- "path",
13498
- {
13499
- d: "M7 8l3-3 3 3",
13500
- stroke: "currentColor",
13501
- strokeWidth: "1.5",
13502
- strokeLinecap: "round",
13503
- strokeLinejoin: "round",
13504
- opacity: sort?.key === col.key && sort.order === "asc" ? 1 : 0.4
13505
- }
13506
- ),
13507
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13508
- "path",
13509
- {
13510
- d: "M7 12l3 3 3-3",
13511
- stroke: "currentColor",
13512
- strokeWidth: "1.5",
13513
- strokeLinecap: "round",
13514
- strokeLinejoin: "round",
13515
- opacity: sort?.key === col.key && sort.order === "desc" ? 1 : 0.4
13516
- }
13517
- )
13518
- ] })
13519
- }
13520
- )
13521
- ] });
13522
- const filterContent = col.filter ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13523
- Popover,
13524
- {
13525
- placement: "bottom-start",
13526
- trigger: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13527
- "button",
13528
- {
13529
- className: cn(
13530
- "p-1.5 rounded-lg transition-all duration-200 hover:bg-accent",
13531
- filters[col.key] ? "bg-accent text-primary" : "text-muted-foreground"
13532
- ),
13533
- "aria-label": "Filter",
13534
- title: `Filter by ${String(col.title)}`,
13535
- children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_lucide_react27.Filter, { className: "w-4 h-4" })
13536
- }
13537
- ),
13538
- children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "p-3 w-64 space-y-3", children: [
13539
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "flex items-center justify-between", children: [
13540
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "text-sm font-medium", children: col.title }),
13541
- filters[col.key] !== void 0 && filters[col.key] !== null && filters[col.key] !== "" && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13542
- "button",
13543
- {
13544
- onClick: () => {
13545
- setCurPage(1);
13546
- setFilters((f) => ({ ...f, [col.key]: void 0 }));
13547
- },
13548
- className: "text-xs text-destructive hover:underline",
13549
- children: t("clearFilter")
13550
- }
13551
- )
13552
- ] }),
13553
- renderFilterControl(col)
13554
- ] })
13555
- }
13556
- ) : null;
13557
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13558
- "div",
13559
- {
13560
- className: cn(
13561
- "flex items-center gap-2 select-none min-h-10",
13562
- isRightAlign && "justify-end",
13563
- isCenterAlign && "justify-center",
13564
- !isRightAlign && !isCenterAlign && "justify-start"
13565
- ),
13566
- children: isRightAlign ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
13567
- filterContent,
13568
- titleContent
13569
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
13570
- titleContent,
13571
- filterContent
13572
- ] })
13573
- }
13574
- );
13575
- })()
13822
+ children: renderHeaderContent(col, isLeaf)
13576
13823
  },
13577
13824
  col.key
13578
13825
  );
13579
- }) });
13826
+ }) }, `header-row-${rowIndex}`)) });
13580
13827
  const processedData = import_react35.default.useMemo(() => {
13581
13828
  if (isServerMode) return data;
13582
13829
  let result = [...data];
@@ -13652,7 +13899,7 @@ function DataTable({
13652
13899
  style: { minWidth: totalColumnsWidth > 0 ? `${totalColumnsWidth}px` : void 0 },
13653
13900
  children: [
13654
13901
  /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableHeader, { children: renderHeader }),
13655
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableBody, { children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableCell, { colSpan: visibleColumns.length, className: "text-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "flex items-center justify-center gap-2 text-muted-foreground", children: [
13902
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableBody, { children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableCell, { colSpan: leafColumns.length, className: "text-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { className: "flex items-center justify-center gap-2 text-muted-foreground", children: [
13656
13903
  /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("svg", { className: "animate-spin h-4 w-4", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
13657
13904
  /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
13658
13905
  /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
@@ -13668,7 +13915,7 @@ function DataTable({
13668
13915
  t("loading"),
13669
13916
  "\u2026"
13670
13917
  ] })
13671
- ] }) }) }) : !displayedData || displayedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableCell, { colSpan: visibleColumns.length, className: "text-center py-6 text-muted-foreground", children: t("noData") }) }) : displayedData.map((row, idx) => {
13918
+ ] }) }) }) : !displayedData || displayedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(TableCell, { colSpan: leafColumns.length, className: "text-center py-6 text-muted-foreground", children: t("noData") }) }) : displayedData.map((row, idx) => {
13672
13919
  const isLastRow = idx === displayedData.length - 1;
13673
13920
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
13674
13921
  TableRow,
@@ -13678,10 +13925,10 @@ function DataTable({
13678
13925
  contentVisibility: "auto",
13679
13926
  containIntrinsicSize: density === "compact" ? "0 36px" : density === "comfortable" ? "0 56px" : "0 48px"
13680
13927
  },
13681
- children: visibleColumns.map((col, colIdx) => {
13928
+ children: leafColumns.map((col, colIdx) => {
13682
13929
  const value = col.dataIndex ? row[col.dataIndex] : void 0;
13683
13930
  const isStripedRow = striped && idx % 2 === 0;
13684
- const prevCol = colIdx > 0 ? visibleColumns[colIdx - 1] : null;
13931
+ const prevCol = colIdx > 0 ? leafColumns[colIdx - 1] : null;
13685
13932
  const isAfterFixedLeft = prevCol?.fixed === "left";
13686
13933
  const showBorderLeft = columnDividers && colIdx > 0 && !isAfterFixedLeft && !col.fixed;
13687
13934
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
@@ -13693,8 +13940,8 @@ function DataTable({
13693
13940
  col.align === "right" && "text-right",
13694
13941
  col.align === "center" && "text-center",
13695
13942
  showBorderLeft && "border-l border-border/60",
13696
- isLastRow && col === visibleColumns[0] && "rounded-bl-2xl md:rounded-bl-3xl",
13697
- isLastRow && col === visibleColumns[visibleColumns.length - 1] && "rounded-br-2xl md:rounded-br-3xl",
13943
+ isLastRow && col === leafColumns[0] && "rounded-bl-2xl md:rounded-bl-3xl",
13944
+ isLastRow && col === leafColumns[leafColumns.length - 1] && "rounded-br-2xl md:rounded-br-3xl",
13698
13945
  getStickyCellClass(col, isStripedRow),
13699
13946
  !col.fixed && isStripedRow && "bg-muted/50"
13700
13947
  ),