@versini/ui-datagrid 0.8.3 → 0.8.5

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/131.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -20,6 +20,31 @@ import { useRef, useContext, useLayoutEffect } from "./511.js";
20
20
 
21
21
 
22
22
 
23
+ /**
24
+ * Snap a CSS pixel value to the nearest device pixel.
25
+ *
26
+ * `getBoundingClientRect()` can produce sub-pixel widths that fluctuate
27
+ * slightly across renders (font rasterization, fractional transforms, zoom).
28
+ * For sticky header/footer alignment we don't need infinite precision — we need
29
+ * stability.
30
+ *
31
+ * By quantizing to the device pixel grid we reduce "width jitter" that would
32
+ * otherwise cause repeated state updates and re-renders.
33
+ *
34
+ * @internal
35
+ *
36
+ */ function quantizeWidthToDevicePixels(width, devicePixelRatio = 1) {
37
+ // Guard against invalid DPR values.
38
+ const dpr = Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;
39
+ /**
40
+ * Note: `window.devicePixelRatio` can be < 1 in some zoom/scaling scenarios.
41
+ * We intentionally accept any DPR > 0 rather than clamping to 1 so that we
42
+ * still snap to the real device pixel grid.
43
+ */ const quantum = 1 / dpr;
44
+ const snapped = Math.round(width / quantum) * quantum;
45
+ // Clamp floating error (e.g. 33.50000000004).
46
+ return Number(snapped.toFixed(4));
47
+ }
23
48
  /**
24
49
  * Hook to measure column widths from the first body row's cells. This is needed
25
50
  * because sticky header/footer are absolutely positioned and can't use CSS
@@ -48,9 +73,12 @@ import { useRef, useContext, useLayoutEffect } from "./511.js";
48
73
  return;
49
74
  }
50
75
  const measureColumns = ()=>{
51
- const widths = Array.from(cells).map((cell)=>cell.getBoundingClientRect().width);
52
- // Only update state if widths have actually changed to prevent infinite loops.
53
- const prevWidths = prevWidthsRef.current;
76
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio ?? 1 : 1;
77
+ const widths = Array.from(cells).map((cell)=>quantizeWidthToDevicePixels(cell.getBoundingClientRect().width, dpr));
78
+ /**
79
+ * Only update state if widths have actually changed to prevent infinite
80
+ * loops.
81
+ */ const prevWidths = prevWidthsRef.current;
54
82
  const hasChanged = widths.length !== prevWidths.length || widths.some((w, i)=>w !== prevWidths[i]);
55
83
  if (hasChanged) {
56
84
  prevWidthsRef.current = widths;
package/dist/298.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -209,7 +209,8 @@ const ROW_LAYOUT = "group grid items-center";
209
209
  }, className);
210
210
  };
211
211
  /**
212
- * Returns the appropriate ARIA role for the cell based on the cell wrapper type.
212
+ * Returns the appropriate ARIA role for the cell based on the cell wrapper
213
+ * type.
213
214
  */ const getCellRole = (cellWrapper)=>isHeaderCell(cellWrapper) ? "columnheader" : "gridcell";
214
215
 
215
216
  export { getCaptionClasses, getCellClasses, getCellRole, getDataGridClasses, getFooterClasses, getHeaderClasses, getRowClasses, getTextColorClassesForHeaderFooter };
package/dist/46.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
package/dist/511.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
package/dist/926.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,3 +1,18 @@
1
+ /**
2
+ * Snap a CSS pixel value to the nearest device pixel.
3
+ *
4
+ * `getBoundingClientRect()` can produce sub-pixel widths that fluctuate
5
+ * slightly across renders (font rasterization, fractional transforms, zoom).
6
+ * For sticky header/footer alignment we don't need infinite precision — we need
7
+ * stability.
8
+ *
9
+ * By quantizing to the device pixel grid we reduce "width jitter" that would
10
+ * otherwise cause repeated state updates and re-renders.
11
+ *
12
+ * @internal
13
+ *
14
+ */
15
+ export declare function quantizeWidthToDevicePixels(width: number, devicePixelRatio?: number): number;
1
16
  /**
2
17
  * Hook to measure column widths from the first body row's cells. This is needed
3
18
  * because sticky header/footer are absolutely positioned and can't use CSS
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -18,23 +18,23 @@ const getActiveIndicatorClasses = (active)=>{
18
18
  if (!active) {
19
19
  return "";
20
20
  }
21
- return clsx("relative", "focus-within:static", "focus-within:after:border-transparent", "after:absolute", "after:content-['']", "after:border-b-2", "after:-bottom-1", "after:left-0", "after:right-0", // the small border at the bottom of the button when it is active (sorted)
21
+ return clsx("relative", "focus-within:static", "focus-within:after:border-transparent", "after:absolute", "after:content-['']", "after:border-b-2", "after:-bottom-1", "after:left-0", "after:right-0", // the small border at the bottom of the button when it is active (sorted).
22
22
  "after:border-table-light");
23
23
  };
24
24
  const ButtonSort = ({ active = false, className, onClick, labelRight, children, blurEffect, mode })=>{
25
25
  const hasBlur = Boolean(blurEffect && blurEffect !== BlurEffects.NONE);
26
- const buttonClasses = clsx(// Base styles
27
- "flex items-center cursor-pointer", // Size - small
28
- "h-6 px-1 sm:px-2", // Font
29
- "text-sm font-bold", // Radius - small
30
- "rounded-xs", // Text color
26
+ const buttonClasses = clsx(// Base styles.
27
+ "flex items-center cursor-pointer", // Size - small.
28
+ "h-6 px-1 sm:px-2", // Font.
29
+ "text-sm font-bold", // Radius - small.
30
+ "rounded-xs", // Text color.
31
31
  getTextColorClassesForHeaderFooter({
32
32
  mode,
33
33
  hasBlur
34
- }), // Focus styles
35
- "focus:outline", "focus:outline-2", "focus:outline-offset-2", "focus:outline-focus-medium", // Hover styles
36
- "hover:text-copy-light-hover", "hover:bg-action-darker-hover", // Active styles
37
- "active:text-copy-medium-active", "active:bg-action-dark-active", // Custom className
34
+ }), // Focus styles.
35
+ "focus:outline", "focus:outline-2", "focus:outline-offset-2", "focus:outline-focus-medium", // Hover styles.
36
+ "hover:text-copy-light-hover", "hover:bg-action-darker-hover", // Active styles.
37
+ "active:text-copy-medium-active", "active:bg-action-dark-active", // Custom className.
38
38
  className);
39
39
  const wrapperClasses = getActiveIndicatorClasses(active);
40
40
  return /*#__PURE__*/ jsx("div", {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.8.3
2
+ @versini/ui-datagrid v0.8.5
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -121,6 +121,7 @@ export declare const getCellClasses: ({ cellWrapper, className, compact, align,
121
121
  borderRight?: boolean;
122
122
  }) => string;
123
123
  /**
124
- * Returns the appropriate ARIA role for the cell based on the cell wrapper type.
124
+ * Returns the appropriate ARIA role for the cell based on the cell wrapper
125
+ * type.
125
126
  */
126
127
  export declare const getCellRole: (cellWrapper?: CellWrapperType) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-datagrid",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -87,17 +87,17 @@
87
87
  },
88
88
  "devDependencies": {
89
89
  "@testing-library/jest-dom": "6.9.1",
90
- "@versini/ui-button": "11.3.6",
90
+ "@versini/ui-button": "11.3.8",
91
91
  "@versini/ui-types": "8.3.0"
92
92
  },
93
93
  "dependencies": {
94
94
  "@tailwindcss/typography": "0.5.19",
95
- "@versini/ui-icons": "4.17.0",
95
+ "@versini/ui-icons": "4.18.0",
96
96
  "clsx": "2.1.1",
97
97
  "tailwindcss": "4.1.18"
98
98
  },
99
99
  "sideEffects": [
100
100
  "**/*.css"
101
101
  ],
102
- "gitHead": "95f2bef12f132245b2f2a09fc23d6500467a7251"
102
+ "gitHead": "ac572f5092ff003ce2998798206a2f622984548a"
103
103
  }