@versini/ui-datagrid 0.2.0 → 0.2.1

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 (33) hide show
  1. package/dist/DataGrid/DataGrid.d.ts +1 -1
  2. package/dist/DataGrid/DataGrid.js +70 -35
  3. package/dist/DataGrid/DataGridContext.js +1 -1
  4. package/dist/DataGrid/index.js +1 -1
  5. package/dist/DataGrid/utilities.d.ts +9 -21
  6. package/dist/DataGrid/utilities.js +11 -47
  7. package/dist/DataGridAnimated/AnimatedWrapper.js +1 -1
  8. package/dist/DataGridAnimated/index.js +1 -1
  9. package/dist/DataGridAnimated/useAnimatedHeight.js +1 -1
  10. package/dist/DataGridBody/DataGridBody.js +1 -1
  11. package/dist/DataGridBody/index.js +1 -1
  12. package/dist/DataGridCell/DataGridCell.js +1 -1
  13. package/dist/DataGridCell/index.js +1 -1
  14. package/dist/DataGridCellSort/DataGridCellSort.js +1 -1
  15. package/dist/DataGridCellSort/index.js +1 -1
  16. package/dist/DataGridConstants/DataGridConstants.js +1 -1
  17. package/dist/DataGridConstants/index.js +1 -1
  18. package/dist/DataGridFooter/DataGridFooter.d.ts +4 -1
  19. package/dist/DataGridFooter/DataGridFooter.js +12 -17
  20. package/dist/DataGridFooter/index.js +1 -1
  21. package/dist/DataGridHeader/DataGridHeader.d.ts +20 -7
  22. package/dist/DataGridHeader/DataGridHeader.js +99 -41
  23. package/dist/DataGridHeader/index.js +1 -1
  24. package/dist/DataGridInfinite/InfiniteScrollMarker.js +1 -1
  25. package/dist/DataGridInfinite/index.js +1 -1
  26. package/dist/DataGridInfinite/useInfiniteScroll.js +1 -1
  27. package/dist/DataGridRow/DataGridRow.js +5 -18
  28. package/dist/DataGridRow/index.js +1 -1
  29. package/dist/DataGridSorting/index.js +1 -1
  30. package/dist/DataGridSorting/sortingUtils.js +1 -1
  31. package/dist/common/utilities.d.ts +15 -0
  32. package/dist/common/utilities.js +48 -0
  33. package/package.json +2 -2
@@ -1,2 +1,2 @@
1
1
  import type { DataGridProps } from "./DataGridTypes";
2
- export declare const DataGrid: ({ caption, className, wrapperClassName, children, mode, compact, stickyHeader, stickyFooter, blurEffect, maxHeight, disabled, columns, ...rest }: DataGridProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const DataGrid: ({ className, wrapperClassName, children, mode, compact, stickyHeader, stickyFooter, blurEffect, maxHeight, disabled, columns, ...rest }: DataGridProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,10 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- import { useId, useMemo } from "react";
7
+ import { Children, isValidElement, useCallback, useMemo, useState } from "react";
8
8
  import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
9
9
  import { DataGridContext } from "./DataGridContext.js";
10
10
  import { getDataGridClasses } from "./utilities.js";
@@ -25,47 +25,89 @@ import { getDataGridClasses } from "./utilities.js";
25
25
 
26
26
 
27
27
 
28
+ /**
29
+ * Helper to check component type by displayName.
30
+ * This allows tree-shaking since we don't need to import the actual component.
31
+ */ const isComponentType = (child, displayName)=>{
32
+ if (!/*#__PURE__*/ isValidElement(child)) {
33
+ return false;
34
+ }
35
+ const type = child.type;
36
+ return type?.displayName === displayName;
37
+ };
28
38
  /* =============================================================================
29
39
  * DataGrid (main component)
30
- * ========================================================================== */ const DataGrid = ({ caption, className, wrapperClassName, children, mode = "system", compact = false, stickyHeader = false, stickyFooter = false, blurEffect = BlurEffects.NONE, maxHeight, disabled = false, columns, ...rest })=>{
31
- const captionId = useId();
40
+ * ========================================================================== */ const DataGrid = ({ className, wrapperClassName, children, mode = "system", compact = false, stickyHeader = false, stickyFooter = false, blurEffect = BlurEffects.NONE, maxHeight, disabled = false, columns, ...rest })=>{
41
+ /**
42
+ * Auto-detect if DataGridHeader and DataGridFooter children exist.
43
+ * This prevents applying sticky styles when the component doesn't exist.
44
+ * Uses displayName comparison to enable tree-shaking.
45
+ */ const hasHeader = useMemo(()=>{
46
+ return Children.toArray(children).some((child)=>isComponentType(child, "DataGridHeader"));
47
+ }, [
48
+ children
49
+ ]);
50
+ const hasFooter = useMemo(()=>{
51
+ return Children.toArray(children).some((child)=>isComponentType(child, "DataGridFooter"));
52
+ }, [
53
+ children
54
+ ]);
55
+ /**
56
+ * Only apply sticky behavior if both the prop is true AND the
57
+ * corresponding component exists. This prevents adding padding/styles
58
+ * for non-existent headers/footers.
59
+ */ const effectiveStickyHeader = stickyHeader && hasHeader;
60
+ const effectiveStickyFooter = stickyFooter && hasFooter;
61
+ /**
62
+ * Auto-detect if any DataGridHeader child has a caption prop.
63
+ * This eliminates the need for a separate hasCaption prop.
64
+ */ const hasCaption = useMemo(()=>{
65
+ return Children.toArray(children).some((child)=>isComponentType(child, "DataGridHeader") && /*#__PURE__*/ isValidElement(child) && Boolean(child.props.caption));
66
+ }, [
67
+ children
68
+ ]);
69
+ /**
70
+ * State to hold the caption ID registered by DataGridHeader.
71
+ * Used for aria-labelledby on the grid element for accessibility.
72
+ */ const [captionId, setCaptionId] = useState(undefined);
73
+ const handleSetCaptionId = useCallback((id)=>{
74
+ setCaptionId(id);
75
+ }, []);
32
76
  const classes = useMemo(()=>getDataGridClasses({
33
77
  mode,
34
78
  className,
35
79
  wrapperClassName,
36
- stickyHeader,
37
- stickyFooter,
80
+ stickyHeader: effectiveStickyHeader,
81
+ stickyFooter: effectiveStickyFooter,
38
82
  disabled,
39
- hasCaption: Boolean(caption),
83
+ hasCaption,
40
84
  hasColumns: Boolean(columns)
41
85
  }), [
42
86
  mode,
43
87
  className,
44
88
  wrapperClassName,
45
- stickyHeader,
46
- stickyFooter,
89
+ effectiveStickyHeader,
90
+ effectiveStickyFooter,
47
91
  disabled,
48
- caption,
92
+ hasCaption,
49
93
  columns
50
94
  ]);
51
95
  const contextValue = useMemo(()=>({
52
96
  mode,
53
97
  compact,
54
- stickyHeader,
55
- stickyFooter,
98
+ stickyHeader: effectiveStickyHeader,
99
+ stickyFooter: effectiveStickyFooter,
56
100
  blurEffect,
57
- disabled,
58
- hasCaption: Boolean(caption),
59
- columns
101
+ columns,
102
+ setCaptionId: handleSetCaptionId
60
103
  }), [
61
104
  mode,
62
105
  compact,
63
- stickyHeader,
64
- stickyFooter,
106
+ effectiveStickyHeader,
107
+ effectiveStickyFooter,
65
108
  blurEffect,
66
- disabled,
67
- caption,
68
- columns
109
+ columns,
110
+ handleSetCaptionId
69
111
  ]);
70
112
  const wrapperStyle = maxHeight ? {
71
113
  maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight
@@ -74,7 +116,7 @@ import { getDataGridClasses } from "./utilities.js";
74
116
  * When sticky header/footer is enabled, use Panel-like structure: - Outer
75
117
  * wrapper has overflow-hidden - Scrollable content area in the middle with
76
118
  * padding - Header/footer are absolutely positioned.
77
- */ const hasSticky = stickyHeader || stickyFooter;
119
+ */ const hasSticky = effectiveStickyHeader || effectiveStickyFooter;
78
120
  /**
79
121
  * When columns are provided, apply grid-template-columns at the grid level
80
122
  * so all rows can use subgrid to inherit the same column sizing.
@@ -83,8 +125,8 @@ import { getDataGridClasses } from "./utilities.js";
83
125
  } : undefined;
84
126
  const gridContent = /*#__PURE__*/ jsx("div", {
85
127
  role: "grid",
128
+ "aria-labelledby": captionId,
86
129
  className: classes.grid,
87
- "aria-labelledby": caption ? captionId : undefined,
88
130
  style: gridStyle,
89
131
  ...rest,
90
132
  children: children
@@ -113,21 +155,14 @@ import { getDataGridClasses } from "./utilities.js";
113
155
  })
114
156
  ]
115
157
  }),
116
- /*#__PURE__*/ jsxs("div", {
158
+ /*#__PURE__*/ jsx("div", {
117
159
  className: classes.wrapper,
118
160
  style: wrapperStyle,
119
- children: [
120
- caption && /*#__PURE__*/ jsx("div", {
121
- id: captionId,
122
- className: classes.caption,
123
- children: caption
124
- }),
125
- hasSticky ? /*#__PURE__*/ jsx("div", {
126
- className: classes.scrollableContent,
127
- style: wrapperStyle,
128
- children: gridContent
129
- }) : gridContent
130
- ]
161
+ children: hasSticky ? /*#__PURE__*/ jsx("div", {
162
+ className: classes.scrollableContent,
163
+ style: wrapperStyle,
164
+ children: gridContent
165
+ }) : gridContent
131
166
  })
132
167
  ]
133
168
  })
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,22 +1,11 @@
1
- import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridConstants";
2
- /**
3
- * Common classes for sticky header/footer with optional blur effect.
4
- */
5
- export declare const getStickyBlurClasses: ({ blurEffect, }: {
6
- blurEffect?: BlurEffect;
7
- }) => string;
8
- export declare const getCaptionBackgroundClasses: ({ mode }: {
9
- mode: ThemeMode;
10
- }) => string;
11
- export declare const getCaptionCopyClasses: ({ mode }: {
12
- mode: ThemeMode;
13
- }) => string;
1
+ import { type ThemeMode } from "../DataGridConstants/DataGridConstants";
14
2
  /**
15
3
  * Generates classes for the main DataGrid wrapper and grid. When sticky
16
- * header/footer is enabled, uses the Panel-like approach:
4
+ * header/footer is enabled:
17
5
  * - Outer wrapper has overflow-hidden
18
- * - Scrollable area is a separate inner container
19
- * - Header/footer are absolutely positioned overlays
6
+ * - Scrollable area is a separate inner container with padding for header/footer
7
+ * - Header/footer use position:absolute to overlay scrollbar
8
+ * - Padding is auto-calculated based on whether caption is present
20
9
  *
21
10
  * When columns prop is provided, the grid uses CSS Grid layout with subgrid
22
11
  * support for proper column alignment across all rows.
@@ -37,10 +26,10 @@ export declare const getDataGridClasses: ({ mode, className, wrapperClassName, s
37
26
  spinner: string;
38
27
  wrapper: string;
39
28
  /**
40
- * Scrollable content area - like Panel's scrollableContent Has padding to
41
- * make room for absolutely positioned header/footer rounded-[inherit] clips
42
- * the scrollbar at the rounded corners.
43
- * When there's a caption, add extra padding for caption (~36px) + header (~40px).
29
+ * Scrollable content area with padding for absolute-positioned header/footer.
30
+ * Header height varies based on whether caption is present:
31
+ * - pt-10 (40px): header row only
32
+ * - pt-19 (76px): header row + caption
44
33
  */
45
34
  scrollableContent: string;
46
35
  /**
@@ -48,5 +37,4 @@ export declare const getDataGridClasses: ({ mode, className, wrapperClassName, s
48
37
  * consistent column alignment. Otherwise, use flexbox.
49
38
  */
50
39
  grid: string;
51
- caption: string;
52
40
  };
@@ -1,18 +1,14 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
6
  import clsx from "clsx";
7
- import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
8
7
 
9
8
  ;// CONCATENATED MODULE: external "clsx"
10
9
 
11
- ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
12
-
13
10
  ;// CONCATENATED MODULE: ./src/DataGrid/utilities.ts
14
11
 
15
-
16
12
  /**
17
13
  * Generates classes for the loading spinner.
18
14
  */ const getSpinnerClasses = ({ mode })=>{
@@ -35,37 +31,13 @@ import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
35
31
  })
36
32
  };
37
33
  };
38
- /**
39
- * Common classes for sticky header/footer with optional blur effect.
40
- */ const getStickyBlurClasses = ({ blurEffect })=>{
41
- return clsx({
42
- "backdrop-blur-sm": blurEffect === BlurEffects.SMALL,
43
- "backdrop-blur-md": blurEffect === BlurEffects.MEDIUM,
44
- "backdrop-blur-lg": blurEffect === BlurEffects.LARGE
45
- });
46
- };
47
- const getCaptionBackgroundClasses = ({ mode })=>{
48
- return clsx({
49
- "bg-surface-darker": mode === "dark" || mode === "system",
50
- "bg-surface-light": mode === "light" || mode === "alt-system",
51
- "dark:bg-surface-light": mode === "system",
52
- "dark:bg-surface-darker": mode === "alt-system"
53
- });
54
- };
55
- const getCaptionCopyClasses = ({ mode })=>{
56
- return clsx({
57
- "text-copy-light": mode === "dark",
58
- "text-copy-dark": mode === "light",
59
- "text-copy-light dark:text-copy-dark": mode === "system",
60
- "text-copy-dark dark:text-copy-light": mode === "alt-system"
61
- });
62
- };
63
34
  /**
64
35
  * Generates classes for the main DataGrid wrapper and grid. When sticky
65
- * header/footer is enabled, uses the Panel-like approach:
36
+ * header/footer is enabled:
66
37
  * - Outer wrapper has overflow-hidden
67
- * - Scrollable area is a separate inner container
68
- * - Header/footer are absolutely positioned overlays
38
+ * - Scrollable area is a separate inner container with padding for header/footer
39
+ * - Header/footer use position:absolute to overlay scrollbar
40
+ * - Padding is auto-calculated based on whether caption is present
69
41
  *
70
42
  * When columns prop is provided, the grid uses CSS Grid layout with subgrid
71
43
  * support for proper column alignment across all rows.
@@ -93,10 +65,10 @@ const getCaptionCopyClasses = ({ mode })=>{
93
65
  "text-copy-dark dark:text-copy-light": mode === "alt-system"
94
66
  }, wrapperClassName),
95
67
  /**
96
- * Scrollable content area - like Panel's scrollableContent Has padding to
97
- * make room for absolutely positioned header/footer rounded-[inherit] clips
98
- * the scrollbar at the rounded corners.
99
- * When there's a caption, add extra padding for caption (~36px) + header (~40px).
68
+ * Scrollable content area with padding for absolute-positioned header/footer.
69
+ * Header height varies based on whether caption is present:
70
+ * - pt-10 (40px): header row only
71
+ * - pt-19 (76px): header row + caption
100
72
  */ scrollableContent: clsx("overflow-y-auto overflow-x-hidden rounded-[inherit]", {
101
73
  "pt-10": stickyHeader && !hasCaption,
102
74
  "pt-19": stickyHeader && hasCaption,
@@ -110,16 +82,8 @@ const getCaptionCopyClasses = ({ mode })=>{
110
82
  "text-copy-dark": mode === "light",
111
83
  "text-copy-light dark:text-copy-dark": mode === "system",
112
84
  "text-copy-dark dark:text-copy-light": mode === "alt-system"
113
- }),
114
- caption: clsx("py-2 text-sm text-center font-bold", {
115
- // When stickyHeader is enabled, caption is absolutely positioned at top
116
- "absolute left-0 right-0 z-20 top-0 rounded-t-lg": stickyHeader
117
- }, getCaptionBackgroundClasses({
118
- mode
119
- }), getCaptionCopyClasses({
120
- mode
121
- }))
85
+ })
122
86
  };
123
87
  };
124
88
 
125
- export { getCaptionBackgroundClasses, getCaptionCopyClasses, getDataGridClasses, getStickyBlurClasses };
89
+ export { getDataGridClasses };
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -13,4 +13,7 @@ export declare const getFooterClasses: ({ className, stickyFooter, mode, blurEff
13
13
  hasColumns?: boolean;
14
14
  stickyFooter?: boolean;
15
15
  }) => string;
16
- export declare const DataGridFooter: ({ className, children, ...rest }: DataGridFooterProps) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const DataGridFooter: {
17
+ ({ className, children, ...rest }: DataGridFooterProps): import("react/jsx-runtime").JSX.Element;
18
+ displayName: string;
19
+ };
@@ -1,21 +1,21 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
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 { getHeaderFooterBackgroundClasses, getStickyBlurClasses } from "../common/utilities.js";
8
9
  import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { getStickyBlurClasses } from "../DataGrid/utilities.js";
10
10
  import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants.js";
11
11
 
12
12
  ;// CONCATENATED MODULE: external "react/jsx-runtime"
13
13
 
14
14
  ;// CONCATENATED MODULE: external "clsx"
15
15
 
16
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
16
+ ;// CONCATENATED MODULE: external "../common/utilities.js"
17
17
 
18
- ;// CONCATENATED MODULE: external "../DataGrid/utilities.js"
18
+ ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
19
19
 
20
20
  ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
21
21
 
@@ -37,7 +37,7 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
37
37
  */ if (hasColumns && !stickyFooter) {
38
38
  return clsx("contents", className);
39
39
  }
40
- const hasBlur = blurEffect && blurEffect !== BlurEffects.NONE;
40
+ const hasBlur = Boolean(blurEffect && blurEffect !== BlurEffects.NONE);
41
41
  return clsx("flex flex-col", {
42
42
  /**
43
43
  * Absolute positioning like Panel's footer: absolute left-0 right-0 z-20
@@ -47,18 +47,12 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
47
47
  "shadow-[rgb(190_190_190_/20%)_0_-0.5rem_1rem]": stickyFooter && mode === "dark",
48
48
  "shadow-[rgb(190_190_190_/20%)_0_-0.5rem_1rem] dark:shadow-[rgb(65_65_65_/30%)_0_-0.5rem_1rem]": stickyFooter && mode === "system",
49
49
  "shadow-[rgb(65_65_65_/30%)_0_-0.5rem_1rem]": stickyFooter && mode === "light",
50
- "shadow-[rgb(65_65_65_/30%)_0_-0.5rem_1rem] dark:shadow-[rgb(190_190_190_/20%)_0_-0.5rem_1rem]": stickyFooter && mode === "alt-system",
51
- // Semi-transparent backgrounds for blur effect.
52
- "bg-table-head-dark/50": hasBlur && stickyFooter && (mode === "dark" || mode === "system"),
53
- "bg-table-head-light/50": hasBlur && stickyFooter && (mode === "light" || mode === "alt-system"),
54
- "dark:bg-table-head-light/50": hasBlur && stickyFooter && mode === "system",
55
- "dark:bg-table-head-dark/50": hasBlur && stickyFooter && mode === "alt-system",
56
- // Solid backgrounds when no blur effect.
57
- "bg-table-head-dark": !hasBlur && stickyFooter && (mode === "dark" || mode === "system"),
58
- "bg-table-head-light": !hasBlur && stickyFooter && (mode === "light" || mode === "alt-system"),
59
- "dark:bg-table-head-light": !hasBlur && stickyFooter && mode === "system",
60
- "dark:bg-table-head-dark": !hasBlur && stickyFooter && mode === "alt-system"
61
- }, stickyFooter && getStickyBlurClasses({
50
+ "shadow-[rgb(65_65_65_/30%)_0_-0.5rem_1rem] dark:shadow-[rgb(190_190_190_/20%)_0_-0.5rem_1rem]": stickyFooter && mode === "alt-system"
51
+ }, getHeaderFooterBackgroundClasses({
52
+ mode,
53
+ hasBlur,
54
+ sticky: Boolean(stickyFooter)
55
+ }), stickyFooter && getStickyBlurClasses({
62
56
  blurEffect
63
57
  }), className);
64
58
  };
@@ -88,5 +82,6 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
88
82
  })
89
83
  });
90
84
  };
85
+ DataGridFooter.displayName = "DataGridFooter";
91
86
 
92
87
  export { DataGridFooter, getFooterClasses };
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,18 +1,31 @@
1
1
  import type { DataGridHeaderProps } from "../DataGrid/DataGridTypes";
2
2
  import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridConstants";
3
3
  /**
4
- * Generates classes for the DataGridHeader. Uses absolute positioning like
5
- * Panel - header floats above the scrollable content.
4
+ * Generates classes for the DataGridHeader wrapper.
6
5
  *
7
- * When columns are provided (subgrid mode), uses display:contents so the
8
- * header doesn't break the parent grid flow.
6
+ * When stickyHeader is true, uses position:absolute so the header overlays
7
+ * the scrollbar area. The parent DataGrid auto-detects caption presence and
8
+ * applies appropriate padding to the scrollable content.
9
+ *
10
+ * When columns are provided (subgrid mode) and not sticky, uses display:contents
11
+ * so the header doesn't break the parent grid flow.
9
12
  */
10
- export declare const getHeaderClasses: ({ className, stickyHeader, mode, blurEffect, hasCaption, hasColumns, }: {
13
+ export declare const getHeaderClasses: ({ className, stickyHeader, mode, blurEffect, hasColumns, }: {
11
14
  mode: ThemeMode;
12
15
  blurEffect?: BlurEffect;
13
16
  className?: string;
14
- hasCaption?: boolean;
15
17
  hasColumns?: boolean;
16
18
  stickyHeader?: boolean;
17
19
  }) => string;
18
- export declare const DataGridHeader: ({ className, children, ...rest }: DataGridHeaderProps) => import("react/jsx-runtime").JSX.Element;
20
+ /**
21
+ * Generates classes for the caption element inside DataGridHeader.
22
+ * When hasColumns is true (subgrid mode), adds col-span-full to span all columns.
23
+ */
24
+ export declare const getCaptionClasses: ({ captionClassName, hasColumns, }: {
25
+ captionClassName?: string;
26
+ hasColumns?: boolean;
27
+ }) => string;
28
+ export declare const DataGridHeader: {
29
+ ({ caption, captionClassName, className, children, ...rest }: DataGridHeaderProps): import("react/jsx-runtime").JSX.Element;
30
+ displayName: string;
31
+ };
@@ -1,21 +1,24 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
- import { jsx } from "react/jsx-runtime";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import clsx from "clsx";
8
+ import { useEffect, useId } from "react";
9
+ import { getHeaderFooterBackgroundClasses, getHeaderFooterCopyClasses, getStickyBlurClasses } from "../common/utilities.js";
8
10
  import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { getCaptionBackgroundClasses, getCaptionCopyClasses, getStickyBlurClasses } from "../DataGrid/utilities.js";
10
- import { CellWrapper } from "../DataGridConstants/DataGridConstants.js";
11
+ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants.js";
11
12
 
12
13
  ;// CONCATENATED MODULE: external "react/jsx-runtime"
13
14
 
14
15
  ;// CONCATENATED MODULE: external "clsx"
15
16
 
16
- ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
17
+ ;// CONCATENATED MODULE: external "react"
18
+
19
+ ;// CONCATENATED MODULE: external "../common/utilities.js"
17
20
 
18
- ;// CONCATENATED MODULE: external "../DataGrid/utilities.js"
21
+ ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
19
22
 
20
23
  ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
21
24
 
@@ -25,60 +28,115 @@ import { CellWrapper } from "../DataGridConstants/DataGridConstants.js";
25
28
 
26
29
 
27
30
 
31
+
28
32
  /**
29
- * Generates classes for the DataGridHeader. Uses absolute positioning like
30
- * Panel - header floats above the scrollable content.
33
+ * Generates classes for the DataGridHeader wrapper.
31
34
  *
32
- * When columns are provided (subgrid mode), uses display:contents so the
33
- * header doesn't break the parent grid flow.
34
- */ const getHeaderClasses = ({ className, stickyHeader, mode, blurEffect, hasCaption, hasColumns })=>{
35
+ * When stickyHeader is true, uses position:absolute so the header overlays
36
+ * the scrollbar area. The parent DataGrid auto-detects caption presence and
37
+ * applies appropriate padding to the scrollable content.
38
+ *
39
+ * When columns are provided (subgrid mode) and not sticky, uses display:contents
40
+ * so the header doesn't break the parent grid flow.
41
+ */ const getHeaderClasses = ({ className, stickyHeader, mode, blurEffect, hasColumns })=>{
42
+ const hasBlur = Boolean(blurEffect && blurEffect !== BlurEffects.NONE);
35
43
  /**
36
44
  * When columns are provided and not sticky, use display:contents so the
37
45
  * rowgroup doesn't interfere with the grid flow. The rows will use subgrid.
38
46
  */ if (hasColumns && !stickyHeader) {
39
- return clsx("contents", getCaptionBackgroundClasses({
40
- mode
41
- }), getCaptionCopyClasses({
47
+ return clsx("contents", getHeaderFooterBackgroundClasses({
48
+ mode,
49
+ hasBlur,
50
+ sticky: Boolean(stickyHeader)
51
+ }), getHeaderFooterCopyClasses({
42
52
  mode
43
53
  }), className);
44
54
  }
45
55
  return clsx("flex flex-col", {
46
- "absolute left-0 right-0 z-20": stickyHeader,
47
- "top-0": stickyHeader && !hasCaption,
48
- "top-9": stickyHeader && hasCaption,
49
- "rounded-t-lg": stickyHeader && !hasCaption
50
- }, getCaptionBackgroundClasses({
51
- mode
52
- }), getCaptionCopyClasses({
56
+ // Absolute positioning to overlay scrollbar, matching footer behavior
57
+ "absolute left-0 right-0 z-20 top-0 rounded-t-lg": stickyHeader
58
+ }, getHeaderFooterBackgroundClasses({
59
+ mode,
60
+ hasBlur,
61
+ sticky: Boolean(stickyHeader)
62
+ }), getHeaderFooterCopyClasses({
53
63
  mode
54
64
  }), stickyHeader && getStickyBlurClasses({
55
65
  blurEffect
56
66
  }), className);
57
67
  };
68
+ /**
69
+ * Generates classes for the caption element inside DataGridHeader.
70
+ * When hasColumns is true (subgrid mode), adds col-span-full to span all columns.
71
+ */ const getCaptionClasses = ({ captionClassName, hasColumns })=>{
72
+ return clsx("py-2 text-sm text-center font-bold", // In subgrid mode, caption needs to span all columns
73
+ hasColumns && "col-span-full", captionClassName);
74
+ };
58
75
  /* =============================================================================
59
76
  * DataGridHeader
60
- * ========================================================================== */ const DataGridHeader = ({ className, children, ...rest })=>{
77
+ * ========================================================================== */ const DataGridHeader = ({ caption, captionClassName, className, children, ...rest })=>{
78
+ const captionId = useId();
61
79
  return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
62
- children: (ctx)=>/*#__PURE__*/ jsx(DataGridContext.Provider, {
63
- value: {
64
- ...ctx,
65
- cellWrapper: CellWrapper.HEADER
66
- },
67
- children: /*#__PURE__*/ jsx("div", {
68
- role: "rowgroup",
69
- className: getHeaderClasses({
70
- className,
71
- stickyHeader: ctx.stickyHeader,
72
- mode: ctx.mode,
73
- blurEffect: ctx.blurEffect,
74
- hasCaption: ctx.hasCaption,
75
- hasColumns: Boolean(ctx.columns)
76
- }),
77
- ...rest,
78
- children: children
79
- })
80
+ children: (ctx)=>/*#__PURE__*/ jsx(DataGridHeaderInner, {
81
+ caption: caption,
82
+ captionClassName: captionClassName,
83
+ captionId: captionId,
84
+ className: className,
85
+ ctx: ctx,
86
+ ...rest,
87
+ children: children
80
88
  })
81
89
  });
82
90
  };
91
+ DataGridHeader.displayName = "DataGridHeader";
92
+ /**
93
+ * Inner component to handle the useEffect for registering captionId.
94
+ * Separated to avoid hooks inside Consumer render prop.
95
+ */ const DataGridHeaderInner = ({ caption, captionClassName, captionId, className, ctx, children, ...rest })=>{
96
+ const hasColumns = Boolean(ctx.columns);
97
+ // Register the caption ID with the parent DataGrid for aria-labelledby
98
+ useEffect(()=>{
99
+ if (caption && ctx.setCaptionId) {
100
+ ctx.setCaptionId(captionId);
101
+ }
102
+ return ()=>{
103
+ if (ctx.setCaptionId) {
104
+ ctx.setCaptionId(undefined);
105
+ }
106
+ };
107
+ }, [
108
+ caption,
109
+ captionId,
110
+ ctx.setCaptionId
111
+ ]);
112
+ return /*#__PURE__*/ jsx(DataGridContext.Provider, {
113
+ value: {
114
+ ...ctx,
115
+ cellWrapper: CellWrapper.HEADER
116
+ },
117
+ children: /*#__PURE__*/ jsxs("div", {
118
+ role: "rowgroup",
119
+ className: getHeaderClasses({
120
+ className,
121
+ stickyHeader: ctx.stickyHeader,
122
+ mode: ctx.mode,
123
+ blurEffect: ctx.blurEffect,
124
+ hasColumns
125
+ }),
126
+ ...rest,
127
+ children: [
128
+ caption && /*#__PURE__*/ jsx("div", {
129
+ id: captionId,
130
+ className: getCaptionClasses({
131
+ captionClassName,
132
+ hasColumns
133
+ }),
134
+ children: caption
135
+ }),
136
+ children
137
+ ]
138
+ })
139
+ });
140
+ };
83
141
 
84
- export { DataGridHeader, getHeaderClasses };
142
+ export { DataGridHeader, getCaptionClasses, getHeaderClasses };
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -25,25 +25,14 @@ import { CellWrapper } from "../DataGridConstants/index.js";
25
25
 
26
26
 
27
27
 
28
- const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFooter })=>{
28
+ const getRowClasses = ({ mode, className, cellWrapper })=>{
29
29
  // Always use CSS Grid for proper column alignment, with vertical centering
30
30
  const layoutClass = "grid items-center";
31
31
  if (cellWrapper === CellWrapper.HEADER || cellWrapper === CellWrapper.FOOTER) {
32
32
  /**
33
33
  * When in a sticky header/footer, don't apply background to row since the
34
34
  * background is applied to the rowgroup for blur effect to work.
35
- */ const isSticky = cellWrapper === CellWrapper.HEADER && stickyHeader || cellWrapper === CellWrapper.FOOTER && stickyFooter;
36
- if (isSticky) {
37
- // No background on row - it's on the rowgroup.
38
- return clsx(layoutClass, className);
39
- }
40
- // Non-sticky header/footer still gets background on row.
41
- return clsx(layoutClass, {
42
- "bg-table-head-dark": mode === "dark" || mode === "system",
43
- "bg-table-head-light": mode === "light" || mode === "alt-system",
44
- "dark:bg-table-head-light": mode === "system",
45
- "dark:bg-table-head-dark": mode === "alt-system"
46
- }, className);
35
+ */ return clsx(layoutClass, className);
47
36
  }
48
37
  return clsx(layoutClass, "border-b last:border-0", {
49
38
  "border-table-dark": mode === "dark" || mode === "system",
@@ -57,7 +46,7 @@ const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFoote
57
46
  "odd:bg-table-dark-odd even:bg-table-dark-even dark:odd:bg-table-light-odd dark:even:bg-table-light-even": mode === "system",
58
47
  "hover:bg-table-dark-hover dark:hover:bg-table-light-hover": mode === "system",
59
48
  "odd:bg-table-light-odd even:bg-table-light-even dark:odd:bg-table-dark-odd dark:even:bg-table-dark-even": mode === "alt-system",
60
- "hover:bg-table-light-hover dark:hover:bg-table-dark-hover": mode === "alt-system"
49
+ "hover:bg-table-light-hover dark::hover:bg-table-dark-hover": mode === "alt-system"
61
50
  }, className);
62
51
  };
63
52
  /* =============================================================================
@@ -109,9 +98,7 @@ const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFoote
109
98
  className: getRowClasses({
110
99
  mode,
111
100
  className,
112
- cellWrapper,
113
- stickyHeader,
114
- stickyFooter
101
+ cellWrapper
115
102
  }),
116
103
  style: {
117
104
  ...rowStyle,
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.2.0
2
+ @versini/ui-datagrid v0.2.1
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -0,0 +1,15 @@
1
+ import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridConstants";
2
+ export declare const getHeaderFooterBackgroundClasses: ({ mode, hasBlur, sticky, }: {
3
+ mode: ThemeMode;
4
+ hasBlur: boolean;
5
+ sticky: boolean;
6
+ }) => string;
7
+ export declare const getHeaderFooterCopyClasses: ({ mode }: {
8
+ mode: ThemeMode;
9
+ }) => string;
10
+ /**
11
+ * Common classes for sticky header/footer with optional blur effect.
12
+ */
13
+ export declare const getStickyBlurClasses: ({ blurEffect, }: {
14
+ blurEffect?: BlurEffect;
15
+ }) => string;
@@ -0,0 +1,48 @@
1
+ /*!
2
+ @versini/ui-datagrid v0.2.1
3
+ © 2026 gizmette.com
4
+ */
5
+
6
+ import clsx from "clsx";
7
+ import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
8
+
9
+ ;// CONCATENATED MODULE: external "clsx"
10
+
11
+ ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
12
+
13
+ ;// CONCATENATED MODULE: ./src/common/utilities.ts
14
+
15
+
16
+ const getHeaderFooterBackgroundClasses = ({ mode, hasBlur, sticky })=>{
17
+ return clsx({
18
+ // Semi-transparent backgrounds for blur effect.
19
+ "bg-surface-darkest/50": hasBlur && sticky && (mode === "dark" || mode === "system"),
20
+ "bg-surface-light/50": hasBlur && sticky && (mode === "light" || mode === "alt-system"),
21
+ "dark:bg-surface-light/50": hasBlur && sticky && mode === "system",
22
+ "dark:bg-surface-darkest/50": hasBlur && sticky && mode === "alt-system",
23
+ // Solid backgrounds when no blur effect.
24
+ "bg-surface-darkest": !hasBlur && (mode === "dark" || mode === "system"),
25
+ "bg-surface-light": !hasBlur && (mode === "light" || mode === "alt-system"),
26
+ "dark:bg-surface-light": !hasBlur && mode === "system",
27
+ "dark:bg-surface-darkest": !hasBlur && mode === "alt-system"
28
+ });
29
+ };
30
+ const getHeaderFooterCopyClasses = ({ mode })=>{
31
+ return clsx({
32
+ "text-copy-light": mode === "dark",
33
+ "text-copy-dark": mode === "light",
34
+ "text-copy-light dark:text-copy-dark": mode === "system",
35
+ "text-copy-dark dark:text-copy-light": mode === "alt-system"
36
+ });
37
+ };
38
+ /**
39
+ * Common classes for sticky header/footer with optional blur effect.
40
+ */ const getStickyBlurClasses = ({ blurEffect })=>{
41
+ return clsx({
42
+ "backdrop-blur-sm": blurEffect === BlurEffects.SMALL,
43
+ "backdrop-blur-md": blurEffect === BlurEffects.MEDIUM,
44
+ "backdrop-blur-lg": blurEffect === BlurEffects.LARGE
45
+ });
46
+ };
47
+
48
+ export { getHeaderFooterBackgroundClasses, getHeaderFooterCopyClasses, getStickyBlurClasses };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-datagrid",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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": "51fb2afed4cea9931d78243b1587e5699024d03e"
97
+ "gitHead": "7c76dd8543839e693f0ad12980f8de01ad2ab123"
98
98
  }