@versini/ui-datagrid 0.3.2 → 0.3.4

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -41,6 +41,11 @@ import { getDataGridClasses } from "./utilities.js";
41
41
  * hard-coded Tailwind padding classes.
42
42
  */ const [headerHeight, setHeaderHeight] = useState(0);
43
43
  const [footerHeight, setFooterHeight] = useState(0);
44
+ /**
45
+ * Track measured column widths from the body. Used by sticky header/footer
46
+ * to sync column widths since absolutely positioned elements can't use
47
+ * CSS subgrid.
48
+ */ const [measuredColumnWidths, setMeasuredColumnWidths] = useState([]);
44
49
  /**
45
50
  * Registration callbacks with stable references. Called by
46
51
  * DataGridHeader/DataGridFooter on mount/unmount. Uses increment/decrement to
@@ -88,13 +93,15 @@ import { getDataGridClasses } from "./utilities.js";
88
93
  stickyFooter: effectiveStickyFooter,
89
94
  blurEffect,
90
95
  columns,
96
+ measuredColumnWidths,
91
97
  setCaptionId: handleSetCaptionId,
92
98
  registerHeader,
93
99
  unregisterHeader,
94
100
  registerFooter,
95
101
  unregisterFooter,
96
102
  setHeaderHeight,
97
- setFooterHeight
103
+ setFooterHeight,
104
+ setMeasuredColumnWidths
98
105
  }), [
99
106
  mode,
100
107
  compact,
@@ -102,6 +109,7 @@ import { getDataGridClasses } from "./utilities.js";
102
109
  effectiveStickyFooter,
103
110
  blurEffect,
104
111
  columns,
112
+ measuredColumnWidths,
105
113
  handleSetCaptionId,
106
114
  registerHeader,
107
115
  unregisterHeader,
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,10 +1,11 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import clsx from "clsx";
8
+ import { useContext, useLayoutEffect, useRef } from "react";
8
9
  import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
10
  import { CellWrapper } from "../DataGridConstants/index.js";
10
11
 
@@ -12,6 +13,8 @@ import { CellWrapper } from "../DataGridConstants/index.js";
12
13
 
13
14
  ;// CONCATENATED MODULE: external "clsx"
14
15
 
16
+ ;// CONCATENATED MODULE: external "react"
17
+
15
18
  ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
16
19
 
17
20
  ;// CONCATENATED MODULE: external "../DataGridConstants/index.js"
@@ -21,28 +24,73 @@ import { CellWrapper } from "../DataGridConstants/index.js";
21
24
 
22
25
 
23
26
 
27
+
24
28
  /* =============================================================================
25
29
  * DataGridBody
26
30
  * ========================================================================== */ const DataGridBody = ({ className, children, ...rest })=>{
27
- return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
28
- children: (ctx)=>{
29
- /**
30
- * When columns are provided, use display:contents so the body doesn't
31
- * interfere with the grid flow. Rows will use subgrid.
32
- */ const bodyClass = ctx.columns ? clsx("contents", className) : clsx("flex flex-col", className);
33
- return /*#__PURE__*/ jsx(DataGridContext.Provider, {
34
- value: {
35
- ...ctx,
36
- cellWrapper: CellWrapper.BODY
37
- },
38
- children: /*#__PURE__*/ jsx("div", {
39
- role: "rowgroup",
40
- className: bodyClass,
41
- ...rest,
42
- children: children
43
- })
44
- });
31
+ const ctx = useContext(DataGridContext);
32
+ const bodyRef = useRef(null);
33
+ /**
34
+ * Measure column widths from the first body row's cells. This is needed
35
+ * because sticky header/footer are absolutely positioned and can't use
36
+ * CSS subgrid. We measure the body cells (which ARE in the grid flow)
37
+ * and report the widths so header/footer can use the same pixel values.
38
+ */ useLayoutEffect(()=>{
39
+ const element = bodyRef.current;
40
+ const needsMeasurement = ctx.columns && (ctx.stickyHeader || ctx.stickyFooter);
41
+ if (!element || !needsMeasurement || !ctx.setMeasuredColumnWidths) {
42
+ return;
43
+ }
44
+ // Find the first row and its cells once, reuse for both measurement and observation
45
+ const firstRow = element.querySelector('[role="row"]');
46
+ if (!firstRow) {
47
+ return;
48
+ }
49
+ const cells = firstRow.querySelectorAll('[role="cell"], [role="columnheader"], [role="gridcell"]');
50
+ if (cells.length === 0) {
51
+ return;
52
+ }
53
+ const measureColumns = ()=>{
54
+ // Measure each cell's width
55
+ const widths = Array.from(cells).map((cell)=>cell.getBoundingClientRect().width);
56
+ ctx.setMeasuredColumnWidths?.(widths);
57
+ };
58
+ // Initial measurement
59
+ measureColumns();
60
+ // Set up ResizeObserver to re-measure when cells resize
61
+ const observer = new ResizeObserver(()=>{
62
+ measureColumns();
63
+ });
64
+ // Observe the body element for any size changes
65
+ observer.observe(element);
66
+ // Also observe the first row's cells directly for more accurate updates
67
+ for (const cell of cells){
68
+ observer.observe(cell);
45
69
  }
70
+ return ()=>observer.disconnect();
71
+ }, [
72
+ ctx.columns,
73
+ ctx.stickyHeader,
74
+ ctx.stickyFooter,
75
+ ctx.setMeasuredColumnWidths,
76
+ children
77
+ ]);
78
+ /**
79
+ * When columns are provided, use display:contents so the body doesn't
80
+ * interfere with the grid flow. Rows will use subgrid.
81
+ */ const bodyClass = ctx.columns ? clsx("contents", className) : clsx("flex flex-col", className);
82
+ return /*#__PURE__*/ jsx(DataGridContext.Provider, {
83
+ value: {
84
+ ...ctx,
85
+ cellWrapper: CellWrapper.BODY
86
+ },
87
+ children: /*#__PURE__*/ jsx("div", {
88
+ ref: bodyRef,
89
+ role: "rowgroup",
90
+ className: bodyClass,
91
+ ...rest,
92
+ children: children
93
+ })
46
94
  });
47
95
  };
48
96
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -43,10 +43,10 @@ const getCellClasses = ({ cellWrapper, className, compact, align, active, mode,
43
43
  "before:bg-table-active-dark dark:before:bg-table-active-light": active && mode === "system",
44
44
  "before:bg-table-active-light dark:before:bg-table-active-dark": active && mode === "alt-system"
45
45
  }, /**
46
- * Vertical borders. self-stretch ensures border spans full row height in grid
47
- * layout.
46
+ * Vertical borders and active indicator. self-stretch ensures border spans
47
+ * full row height in grid layout.
48
48
  */ {
49
- "self-stretch": borderLeft || borderRight,
49
+ "self-stretch": borderLeft || borderRight || active,
50
50
  "border-l border-l-table-dark": borderLeft && mode === "dark",
51
51
  "border-l border-l-table-light": borderLeft && mode === "light",
52
52
  "border-l border-l-table-dark dark:border-l-table-light": borderLeft && mode === "system",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -55,7 +55,7 @@ const getRowClasses = ({ mode, className, cellWrapper })=>{
55
55
  // Count the number of direct children to determine column count.
56
56
  const columnCount = react.Children.count(children);
57
57
  return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
58
- children: ({ mode, cellWrapper, stickyHeader, stickyFooter, columns })=>{
58
+ children: ({ mode, cellWrapper, stickyHeader, stickyFooter, columns, measuredColumnWidths })=>{
59
59
  /**
60
60
  * Determine if this row is inside a sticky header/footer. Sticky elements
61
61
  * are absolutely positioned and outside the main grid flow, so they can't
@@ -68,8 +68,9 @@ const getRowClasses = ({ mode, className, cellWrapper })=>{
68
68
  * sticky header/footer), use CSS subgrid to inherit the column sizing from
69
69
  * the parent grid.
70
70
  *
71
- * For sticky headers/footers, use the explicit column template since
72
- * they're absolutely positioned outside the main grid.
71
+ * For sticky headers/footers, use measured pixel widths from the body if
72
+ * available. This syncs the header/footer columns with the body columns
73
+ * since absolutely positioned elements can't use subgrid.
73
74
  *
74
75
  * When columns are not provided, fall back to the original behavior where
75
76
  * each row defines its own equal-width columns.
@@ -77,10 +78,21 @@ const getRowClasses = ({ mode, className, cellWrapper })=>{
77
78
  */ let rowStyle;
78
79
  if (columns) {
79
80
  if (isInStickyContext) {
80
- // Sticky elements can't use subgrid, use explicit template.
81
- rowStyle = {
82
- gridTemplateColumns: columns.join(" ")
83
- };
81
+ // Sticky elements can't use subgrid.
82
+ // Use measured widths from body if available for perfect alignment.
83
+ // Also check that widths are valid (non-zero) - in test environments
84
+ // or before layout, measurements may be zero.
85
+ const hasValidMeasurements = measuredColumnWidths && measuredColumnWidths.length === columns.length && measuredColumnWidths.some((w)=>w > 0);
86
+ if (hasValidMeasurements) {
87
+ rowStyle = {
88
+ gridTemplateColumns: measuredColumnWidths.map((w)=>`${w}px`).join(" ")
89
+ };
90
+ } else {
91
+ // Fallback to explicit template while measurements are pending.
92
+ rowStyle = {
93
+ gridTemplateColumns: columns.join(" ")
94
+ };
95
+ }
84
96
  } else {
85
97
  // Normal flow: use subgrid to inherit from parent grid.
86
98
  rowStyle = {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.3.2
2
+ @versini/ui-datagrid v0.3.4
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-datagrid",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -94,5 +94,5 @@
94
94
  "sideEffects": [
95
95
  "**/*.css"
96
96
  ],
97
- "gitHead": "598551d562037d7496d877be9a03b1ee8e687de5"
97
+ "gitHead": "d86931bf547783849423664618ad1e04635ef79b"
98
98
  }