@versini/ui-datagrid 0.1.0 → 0.2.0

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 (43) hide show
  1. package/README.md +3 -43
  2. package/dist/DataGrid/DataGrid.d.ts +1 -1
  3. package/dist/DataGrid/DataGrid.js +41 -36
  4. package/dist/DataGrid/DataGridContext.js +1 -1
  5. package/dist/DataGrid/index.js +1 -1
  6. package/dist/DataGrid/utilities.d.ts +19 -3
  7. package/dist/DataGrid/utilities.js +38 -12
  8. package/dist/DataGridAnimated/AnimatedWrapper.js +1 -1
  9. package/dist/DataGridAnimated/index.js +1 -1
  10. package/dist/DataGridAnimated/useAnimatedHeight.js +1 -1
  11. package/dist/DataGridBody/DataGridBody.js +16 -5
  12. package/dist/DataGridBody/index.js +1 -1
  13. package/dist/DataGridCell/DataGridCell.d.ts +2 -5
  14. package/dist/DataGridCell/DataGridCell.js +27 -18
  15. package/dist/DataGridCell/index.js +1 -1
  16. package/dist/DataGridCellSort/DataGridCellSort.d.ts +1 -1
  17. package/dist/DataGridCellSort/DataGridCellSort.js +16 -11
  18. package/dist/DataGridCellSort/index.js +1 -1
  19. package/dist/DataGridConstants/DataGridConstants.js +1 -1
  20. package/dist/DataGridConstants/index.js +1 -1
  21. package/dist/DataGridFooter/DataGridFooter.d.ts +5 -1
  22. package/dist/DataGridFooter/DataGridFooter.js +17 -6
  23. package/dist/DataGridFooter/index.js +1 -1
  24. package/dist/DataGridHeader/DataGridHeader.d.ts +6 -1
  25. package/dist/DataGridHeader/DataGridHeader.js +32 -28
  26. package/dist/DataGridHeader/index.js +1 -1
  27. package/dist/DataGridInfinite/InfiniteScrollMarker.d.ts +2 -6
  28. package/dist/DataGridInfinite/InfiniteScrollMarker.js +7 -6
  29. package/dist/DataGridInfinite/index.js +1 -1
  30. package/dist/DataGridInfinite/useInfiniteScroll.d.ts +12 -1
  31. package/dist/DataGridInfinite/useInfiniteScroll.js +23 -4
  32. package/dist/DataGridRow/DataGridRow.d.ts +1 -1
  33. package/dist/DataGridRow/DataGridRow.js +61 -9
  34. package/dist/DataGridRow/index.js +1 -1
  35. package/dist/DataGridSorting/index.js +1 -1
  36. package/dist/DataGridSorting/sortingUtils.js +1 -1
  37. package/package.json +2 -7
  38. package/dist/DataGridVirtual/VirtualDataGrid.d.ts +0 -114
  39. package/dist/DataGridVirtual/VirtualDataGrid.js +0 -181
  40. package/dist/DataGridVirtual/index.d.ts +0 -6
  41. package/dist/DataGridVirtual/index.js +0 -22
  42. package/dist/DataGridVirtual/useVirtualDataGrid.d.ts +0 -112
  43. package/dist/DataGridVirtual/useVirtualDataGrid.js +0 -89
@@ -2,11 +2,15 @@ import type { DataGridFooterProps } from "../DataGrid/DataGridTypes";
2
2
  import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridConstants";
3
3
  /**
4
4
  * Generates classes for the DataGridFooter.
5
+ *
6
+ * When columns are provided (subgrid mode), uses display:contents so the
7
+ * footer doesn't break the parent grid flow.
5
8
  */
6
- export declare const getFooterClasses: ({ className, stickyFooter, mode, blurEffect, }: {
9
+ export declare const getFooterClasses: ({ className, stickyFooter, mode, blurEffect, hasColumns, }: {
7
10
  mode: ThemeMode;
8
11
  blurEffect?: BlurEffect;
9
12
  className?: string;
13
+ hasColumns?: boolean;
10
14
  stickyFooter?: boolean;
11
15
  }) => string;
12
16
  export declare const DataGridFooter: ({ className, children, ...rest }: DataGridFooterProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -27,9 +27,18 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
27
27
 
28
28
  /**
29
29
  * Generates classes for the DataGridFooter.
30
- */ const getFooterClasses = ({ className, stickyFooter, mode, blurEffect })=>{
30
+ *
31
+ * When columns are provided (subgrid mode), uses display:contents so the
32
+ * footer doesn't break the parent grid flow.
33
+ */ const getFooterClasses = ({ className, stickyFooter, mode, blurEffect, hasColumns })=>{
34
+ /**
35
+ * When columns are provided and not sticky, use display:contents so the
36
+ * rowgroup doesn't interfere with the grid flow. The rows will use subgrid.
37
+ */ if (hasColumns && !stickyFooter) {
38
+ return clsx("contents", className);
39
+ }
31
40
  const hasBlur = blurEffect && blurEffect !== BlurEffects.NONE;
32
- return clsx({
41
+ return clsx("flex flex-col", {
33
42
  /**
34
43
  * Absolute positioning like Panel's footer: absolute left-0 right-0 z-20
35
44
  * bottom-0 rounded-b-lg to match the wrapper's rounded-lg corners.
@@ -57,18 +66,20 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
57
66
  * DataGridFooter
58
67
  * ========================================================================== */ const DataGridFooter = ({ className, children, ...rest })=>{
59
68
  return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
60
- children: ({ mode, stickyFooter, blurEffect })=>/*#__PURE__*/ jsx(DataGridContext.Consumer, {
69
+ children: ({ mode, stickyFooter, blurEffect, columns })=>/*#__PURE__*/ jsx(DataGridContext.Consumer, {
61
70
  children: (ctx)=>/*#__PURE__*/ jsx(DataGridContext.Provider, {
62
71
  value: {
63
72
  ...ctx,
64
73
  cellWrapper: CellWrapper.FOOTER
65
74
  },
66
- children: /*#__PURE__*/ jsx("tfoot", {
75
+ children: /*#__PURE__*/ jsx("div", {
76
+ role: "rowgroup",
67
77
  className: getFooterClasses({
68
78
  className,
69
79
  stickyFooter,
70
80
  mode,
71
- blurEffect
81
+ blurEffect,
82
+ hasColumns: Boolean(columns)
72
83
  }),
73
84
  ...rest,
74
85
  children: children
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -3,11 +3,16 @@ import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridCo
3
3
  /**
4
4
  * Generates classes for the DataGridHeader. Uses absolute positioning like
5
5
  * Panel - header floats above the scrollable content.
6
+ *
7
+ * When columns are provided (subgrid mode), uses display:contents so the
8
+ * header doesn't break the parent grid flow.
6
9
  */
7
- export declare const getHeaderClasses: ({ className, stickyHeader, mode, blurEffect, }: {
10
+ export declare const getHeaderClasses: ({ className, stickyHeader, mode, blurEffect, hasCaption, hasColumns, }: {
8
11
  mode: ThemeMode;
9
12
  blurEffect?: BlurEffect;
10
13
  className?: string;
14
+ hasCaption?: boolean;
15
+ hasColumns?: boolean;
11
16
  stickyHeader?: boolean;
12
17
  }) => string;
13
18
  export declare const DataGridHeader: ({ className, children, ...rest }: DataGridHeaderProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,13 +1,13 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import clsx from "clsx";
8
8
  import { DataGridContext } from "../DataGrid/DataGridContext.js";
9
- import { getStickyBlurClasses } from "../DataGrid/utilities.js";
10
- import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants.js";
9
+ import { getCaptionBackgroundClasses, getCaptionCopyClasses, getStickyBlurClasses } from "../DataGrid/utilities.js";
10
+ import { CellWrapper } from "../DataGridConstants/DataGridConstants.js";
11
11
 
12
12
  ;// CONCATENATED MODULE: external "react/jsx-runtime"
13
13
 
@@ -28,29 +28,30 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
28
28
  /**
29
29
  * Generates classes for the DataGridHeader. Uses absolute positioning like
30
30
  * Panel - header floats above the scrollable content.
31
- */ const getHeaderClasses = ({ className, stickyHeader, mode, blurEffect })=>{
32
- const hasBlur = blurEffect && blurEffect !== BlurEffects.NONE;
33
- return clsx({
34
- /**
35
- * Absolute positioning like Panel's header: absolute left-0 right-0 z-20
36
- * top-0 rounded-t-lg to match the wrapper's rounded-lg corners.
37
- */ "absolute left-0 right-0 z-20 top-0 rounded-t-lg": stickyHeader,
38
- // Shadow for visual depth (downward glow).
39
- "shadow-[rgb(190_190_190_/20%)_0_0.5rem_1rem]": stickyHeader && mode === "dark",
40
- "shadow-[rgb(190_190_190_/20%)_0_0.5rem_1rem] dark:shadow-[rgb(65_65_65_/30%)_0_0.5rem_1rem]": stickyHeader && mode === "system",
41
- "shadow-[rgb(65_65_65_/30%)_0_0.5rem_1rem]": stickyHeader && mode === "light",
42
- "shadow-[rgb(65_65_65_/30%)_0_0.5rem_1rem] dark:shadow-[rgb(190_190_190_/20%)_0_0.5rem_1rem]": stickyHeader && mode === "alt-system",
43
- // Semi-transparent backgrounds for blur effect.
44
- "bg-table-head-dark/50": hasBlur && stickyHeader && (mode === "dark" || mode === "system"),
45
- "bg-table-head-light/50": hasBlur && stickyHeader && (mode === "light" || mode === "alt-system"),
46
- "dark:bg-table-head-light/50": hasBlur && stickyHeader && mode === "system",
47
- "dark:bg-table-head-dark/50": hasBlur && stickyHeader && mode === "alt-system",
48
- // Solid backgrounds when no blur effect.
49
- "bg-table-head-dark": !hasBlur && stickyHeader && (mode === "dark" || mode === "system"),
50
- "bg-table-head-light": !hasBlur && stickyHeader && (mode === "light" || mode === "alt-system"),
51
- "dark:bg-table-head-light": !hasBlur && stickyHeader && mode === "system",
52
- "dark:bg-table-head-dark": !hasBlur && stickyHeader && mode === "alt-system"
53
- }, stickyHeader && getStickyBlurClasses({
31
+ *
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
+ /**
36
+ * When columns are provided and not sticky, use display:contents so the
37
+ * rowgroup doesn't interfere with the grid flow. The rows will use subgrid.
38
+ */ if (hasColumns && !stickyHeader) {
39
+ return clsx("contents", getCaptionBackgroundClasses({
40
+ mode
41
+ }), getCaptionCopyClasses({
42
+ mode
43
+ }), className);
44
+ }
45
+ 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({
53
+ mode
54
+ }), stickyHeader && getStickyBlurClasses({
54
55
  blurEffect
55
56
  }), className);
56
57
  };
@@ -63,12 +64,15 @@ import { BlurEffects, CellWrapper } from "../DataGridConstants/DataGridConstants
63
64
  ...ctx,
64
65
  cellWrapper: CellWrapper.HEADER
65
66
  },
66
- children: /*#__PURE__*/ jsx("thead", {
67
+ children: /*#__PURE__*/ jsx("div", {
68
+ role: "rowgroup",
67
69
  className: getHeaderClasses({
68
70
  className,
69
71
  stickyHeader: ctx.stickyHeader,
70
72
  mode: ctx.mode,
71
- blurEffect: ctx.blurEffect
73
+ blurEffect: ctx.blurEffect,
74
+ hasCaption: ctx.hasCaption,
75
+ hasColumns: Boolean(ctx.columns)
72
76
  }),
73
77
  ...rest,
74
78
  children: children
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,8 +1,4 @@
1
1
  export type InfiniteScrollMarkerProps = {
2
- /**
3
- * Number of columns in the table (for proper colspan).
4
- */
5
- colSpan?: number;
6
2
  /**
7
3
  * Custom class name for the row.
8
4
  */
@@ -26,10 +22,10 @@ export type InfiniteScrollMarkerProps = {
26
22
  * {data.slice(0, visibleCount).map((item) => (
27
23
  * <DataGridRow key={item.id}>...</DataGridRow>
28
24
  * ))}
29
- * {hasMore && <InfiniteScrollMarker ref={markerRef} colSpan={5} />}
25
+ * {hasMore && <InfiniteScrollMarker ref={markerRef} />}
30
26
  * </DataGridBody>
31
27
  * );
32
28
  * ```
33
29
  *
34
30
  */
35
- export declare const InfiniteScrollMarker: import("react").ForwardRefExoticComponent<InfiniteScrollMarkerProps & import("react").RefAttributes<HTMLTableRowElement>>;
31
+ export declare const InfiniteScrollMarker: import("react").ForwardRefExoticComponent<InfiniteScrollMarkerProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -26,22 +26,23 @@ import { forwardRef } from "react";
26
26
  * {data.slice(0, visibleCount).map((item) => (
27
27
  * <DataGridRow key={item.id}>...</DataGridRow>
28
28
  * ))}
29
- * {hasMore && <InfiniteScrollMarker ref={markerRef} colSpan={5} />}
29
+ * {hasMore && <InfiniteScrollMarker ref={markerRef} />}
30
30
  * </DataGridBody>
31
31
  * );
32
32
  * ```
33
33
  *
34
- */ const InfiniteScrollMarker_InfiniteScrollMarker = /*#__PURE__*/ forwardRef(function InfiniteScrollMarker({ colSpan = 1, className, debug = false }, ref) {
35
- return /*#__PURE__*/ jsx("tr", {
34
+ */ const InfiniteScrollMarker_InfiniteScrollMarker = /*#__PURE__*/ forwardRef(function InfiniteScrollMarker({ className, debug = false }, ref) {
35
+ return /*#__PURE__*/ jsx("div", {
36
36
  ref: ref,
37
+ role: "row",
37
38
  className: className,
38
39
  "aria-hidden": "true",
39
40
  style: {
40
41
  height: debug ? "40px" : "1px",
41
42
  background: debug ? "red" : "transparent"
42
43
  },
43
- children: /*#__PURE__*/ jsx("td", {
44
- colSpan: colSpan,
44
+ children: /*#__PURE__*/ jsx("div", {
45
+ role: "gridcell",
45
46
  style: {
46
47
  padding: 0,
47
48
  border: 0
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Finds the nearest scrollable ancestor of an element.
3
+ * Returns null if no scrollable ancestor is found (uses viewport).
4
+ */
5
+ export declare function findScrollableAncestor(element: HTMLElement): Element | null;
1
6
  export type UseInfiniteScrollOptions = {
2
7
  /**
3
8
  * Number of items to show initially and to add on each scroll.
@@ -28,6 +33,12 @@ export type UseInfiniteScrollOptions = {
28
33
  * Callback when more items are loaded.
29
34
  */
30
35
  onLoadMore?: (newVisibleCount: number) => void;
36
+ /**
37
+ * The scroll container element for IntersectionObserver root.
38
+ * Required when the DataGrid is inside a scrollable container (e.g., with maxHeight).
39
+ * Pass null to use the viewport as root.
40
+ */
41
+ scrollContainer?: Element | null;
31
42
  };
32
43
  export type UseInfiniteScrollReturn = {
33
44
  /**
@@ -78,4 +89,4 @@ export type UseInfiniteScrollReturn = {
78
89
  * ```
79
90
  *
80
91
  */
81
- export declare function useInfiniteScroll({ batchSize, threshold, rootMargin, totalItems, initialVisibleCount, onLoadMore, }: UseInfiniteScrollOptions): UseInfiniteScrollReturn;
92
+ export declare function useInfiniteScroll({ batchSize, threshold, rootMargin, totalItems, initialVisibleCount, onLoadMore, scrollContainer, }: UseInfiniteScrollOptions): UseInfiniteScrollReturn;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -12,6 +12,21 @@ import { useCallback, useEffect, useRef, useState } from "react";
12
12
  const DEFAULT_BATCH_SIZE = 20;
13
13
  const DEFAULT_THRESHOLD = 5;
14
14
  const DEFAULT_ROOT_MARGIN = "20px";
15
+ /**
16
+ * Finds the nearest scrollable ancestor of an element.
17
+ * Returns null if no scrollable ancestor is found (uses viewport).
18
+ */ function findScrollableAncestor(element) {
19
+ let parent = element.parentElement;
20
+ while(parent){
21
+ const style = getComputedStyle(parent);
22
+ const overflowY = style.overflowY;
23
+ if (overflowY === "auto" || overflowY === "scroll") {
24
+ return parent;
25
+ }
26
+ parent = parent.parentElement;
27
+ }
28
+ return null;
29
+ }
15
30
  /**
16
31
  * Hook for implementing infinite scroll with IntersectionObserver.
17
32
  *
@@ -34,7 +49,7 @@ const DEFAULT_ROOT_MARGIN = "20px";
34
49
  * );
35
50
  * ```
36
51
  *
37
- */ function useInfiniteScroll({ batchSize = DEFAULT_BATCH_SIZE, threshold = DEFAULT_THRESHOLD, rootMargin = DEFAULT_ROOT_MARGIN, totalItems, initialVisibleCount, onLoadMore }) {
52
+ */ function useInfiniteScroll({ batchSize = DEFAULT_BATCH_SIZE, threshold = DEFAULT_THRESHOLD, rootMargin = DEFAULT_ROOT_MARGIN, totalItems, initialVisibleCount, onLoadMore, scrollContainer }) {
38
53
  const observerRef = useRef(null);
39
54
  const initialCount = initialVisibleCount ?? batchSize + threshold;
40
55
  const [visibleCount, setVisibleCount] = useState(Math.min(initialCount, totalItems));
@@ -66,7 +81,10 @@ const DEFAULT_ROOT_MARGIN = "20px";
66
81
  observerRef.current = null;
67
82
  }
68
83
  if (node && typeof IntersectionObserver !== "undefined") {
84
+ // Find the scrollable ancestor if not explicitly provided.
85
+ const root = scrollContainer ?? findScrollableAncestor(node);
69
86
  const options = {
87
+ root,
70
88
  rootMargin
71
89
  };
72
90
  observerRef.current = new IntersectionObserver(handleIntersection, options);
@@ -74,7 +92,8 @@ const DEFAULT_ROOT_MARGIN = "20px";
74
92
  }
75
93
  }, [
76
94
  handleIntersection,
77
- rootMargin
95
+ rootMargin,
96
+ scrollContainer
78
97
  ]);
79
98
  /**
80
99
  * Reset to initial count.
@@ -114,4 +133,4 @@ const DEFAULT_ROOT_MARGIN = "20px";
114
133
  };
115
134
  }
116
135
 
117
- export { useInfiniteScroll };
136
+ export { findScrollableAncestor, useInfiniteScroll };
@@ -1,2 +1,2 @@
1
1
  import type { DataGridRowProps } from "../DataGrid/DataGridTypes";
2
- export declare const DataGridRow: ({ className, children, ...rest }: DataGridRowProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const DataGridRow: ({ className, children, style: userStyle, ...rest }: DataGridRowProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,11 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
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 react 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,25 +24,28 @@ import { CellWrapper } from "../DataGridConstants/index.js";
21
24
 
22
25
 
23
26
 
27
+
24
28
  const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFooter })=>{
29
+ // Always use CSS Grid for proper column alignment, with vertical centering
30
+ const layoutClass = "grid items-center";
25
31
  if (cellWrapper === CellWrapper.HEADER || cellWrapper === CellWrapper.FOOTER) {
26
32
  /**
27
33
  * When in a sticky header/footer, don't apply background to row since the
28
- * background is applied to thead/tfoot for blur effect to work.
34
+ * background is applied to the rowgroup for blur effect to work.
29
35
  */ const isSticky = cellWrapper === CellWrapper.HEADER && stickyHeader || cellWrapper === CellWrapper.FOOTER && stickyFooter;
30
36
  if (isSticky) {
31
- // No background on row - it's on the thead/tfoot.
32
- return clsx(className);
37
+ // No background on row - it's on the rowgroup.
38
+ return clsx(layoutClass, className);
33
39
  }
34
40
  // Non-sticky header/footer still gets background on row.
35
- return clsx({
41
+ return clsx(layoutClass, {
36
42
  "bg-table-head-dark": mode === "dark" || mode === "system",
37
43
  "bg-table-head-light": mode === "light" || mode === "alt-system",
38
44
  "dark:bg-table-head-light": mode === "system",
39
45
  "dark:bg-table-head-dark": mode === "alt-system"
40
46
  }, className);
41
47
  }
42
- return clsx("border-b last:border-0", {
48
+ return clsx(layoutClass, "border-b last:border-0", {
43
49
  "border-table-dark": mode === "dark" || mode === "system",
44
50
  "border-table-light": mode === "light" || mode === "alt-system",
45
51
  "dark:border-table-light": mode === "system",
@@ -56,9 +62,50 @@ const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFoote
56
62
  };
57
63
  /* =============================================================================
58
64
  * DataGridRow
59
- * ========================================================================== */ const DataGridRow = ({ className, children, ...rest })=>{
65
+ * ========================================================================== */ const DataGridRow = ({ className, children, style: userStyle, ...rest })=>{
66
+ // Count the number of direct children to determine column count
67
+ const columnCount = react.Children.count(children);
60
68
  return /*#__PURE__*/ jsx(DataGridContext.Consumer, {
61
- children: ({ mode, cellWrapper, stickyHeader, stickyFooter })=>/*#__PURE__*/ jsx("tr", {
69
+ children: ({ mode, cellWrapper, stickyHeader, stickyFooter, columns })=>{
70
+ /**
71
+ * Determine if this row is inside a sticky header/footer.
72
+ * Sticky elements are absolutely positioned and outside the main grid
73
+ * flow, so they can't use subgrid.
74
+ */ const isInStickyHeader = cellWrapper === CellWrapper.HEADER && stickyHeader;
75
+ const isInStickyFooter = cellWrapper === CellWrapper.FOOTER && stickyFooter;
76
+ const isInStickyContext = isInStickyHeader || isInStickyFooter;
77
+ /**
78
+ * When columns are provided AND the row is inside the grid flow
79
+ * (not in a sticky header/footer), use CSS subgrid to inherit the
80
+ * column sizing from the parent grid.
81
+ *
82
+ * For sticky headers/footers, use the explicit column template since
83
+ * they're absolutely positioned outside the main grid.
84
+ *
85
+ * When columns are not provided, fall back to the original behavior
86
+ * where each row defines its own equal-width columns.
87
+ */ let rowStyle;
88
+ if (columns) {
89
+ if (isInStickyContext) {
90
+ // Sticky elements can't use subgrid, use explicit template
91
+ rowStyle = {
92
+ gridTemplateColumns: columns.join(" ")
93
+ };
94
+ } else {
95
+ // Normal flow: use subgrid to inherit from parent grid
96
+ rowStyle = {
97
+ gridColumn: "1 / -1",
98
+ gridTemplateColumns: "subgrid"
99
+ };
100
+ }
101
+ } else {
102
+ // No columns prop: use equal-width columns
103
+ rowStyle = {
104
+ gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`
105
+ };
106
+ }
107
+ return /*#__PURE__*/ jsx("div", {
108
+ role: "row",
62
109
  className: getRowClasses({
63
110
  mode,
64
111
  className,
@@ -66,9 +113,14 @@ const getRowClasses = ({ mode, className, cellWrapper, stickyHeader, stickyFoote
66
113
  stickyHeader,
67
114
  stickyFooter
68
115
  }),
116
+ style: {
117
+ ...rowStyle,
118
+ ...userStyle
119
+ },
69
120
  ...rest,
70
121
  children: children
71
- })
122
+ });
123
+ }
72
124
  });
73
125
  };
74
126
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-datagrid v0.1.0
2
+ @versini/ui-datagrid v0.2.0
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.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -56,10 +56,6 @@
56
56
  "./sorting": {
57
57
  "types": "./dist/DataGridSorting/index.d.ts",
58
58
  "import": "./dist/DataGridSorting/index.js"
59
- },
60
- "./virtual": {
61
- "types": "./dist/DataGridVirtual/index.d.ts",
62
- "import": "./dist/DataGridVirtual/index.js"
63
59
  }
64
60
  },
65
61
  "files": [
@@ -90,7 +86,6 @@
90
86
  },
91
87
  "dependencies": {
92
88
  "@tailwindcss/typography": "0.5.19",
93
- "@tanstack/react-virtual": "3.13.16",
94
89
  "@versini/ui-button": "11.3.2",
95
90
  "@versini/ui-icons": "4.16.1",
96
91
  "clsx": "2.1.1",
@@ -99,5 +94,5 @@
99
94
  "sideEffects": [
100
95
  "**/*.css"
101
96
  ],
102
- "gitHead": "d88af405285c38faec12d7cb52e575207b99173f"
97
+ "gitHead": "51fb2afed4cea9931d78243b1587e5699024d03e"
103
98
  }
@@ -1,114 +0,0 @@
1
- import { type BlurEffect, type ThemeMode } from "../DataGridConstants/DataGridConstants";
2
- export type VirtualDataGridColumn<T> = {
3
- /**
4
- * Unique identifier for the column.
5
- */
6
- id: string;
7
- /**
8
- * Header content.
9
- */
10
- header: React.ReactNode;
11
- /**
12
- * Cell renderer function.
13
- */
14
- cell: (item: T, index: number) => React.ReactNode;
15
- /**
16
- * Column width (CSS value).
17
- */
18
- width?: string | number;
19
- /**
20
- * Additional class name for cells in this column.
21
- */
22
- className?: string;
23
- };
24
- export type VirtualDataGridProps<T> = {
25
- /**
26
- * Array of data items.
27
- */
28
- data: T[];
29
- /**
30
- * Column definitions.
31
- */
32
- columns: VirtualDataGridColumn<T>[];
33
- /**
34
- * Height of the container (required for virtualization).
35
- */
36
- height: string | number;
37
- /**
38
- * Theme mode.
39
- * @default "system"
40
- */
41
- mode?: ThemeMode;
42
- /**
43
- * Use compact row height.
44
- * @default false
45
- */
46
- compact?: boolean;
47
- /**
48
- * Keep header fixed while scrolling.
49
- * @default true
50
- */
51
- stickyHeader?: boolean;
52
- /**
53
- * Blur effect for sticky header.
54
- * @default BlurEffects.NONE
55
- */
56
- blurEffect?: BlurEffect;
57
- /**
58
- * Caption text for accessibility.
59
- */
60
- caption?: string;
61
- /**
62
- * Additional class name for the wrapper.
63
- */
64
- wrapperClassName?: string;
65
- /**
66
- * Additional class name for the table.
67
- */
68
- className?: string;
69
- /**
70
- * Function to get a unique key for each row.
71
- */
72
- getRowKey?: (item: T, index: number) => string | number;
73
- /**
74
- * Callback when a row is clicked.
75
- */
76
- onRowClick?: (item: T, index: number) => void;
77
- /**
78
- * Index of the active row (for highlighting).
79
- */
80
- activeRowIndex?: number;
81
- /**
82
- * Estimated row height for virtualization.
83
- * @default 40
84
- */
85
- estimateSize?: number | ((index: number) => number);
86
- /**
87
- * Number of rows to render above/below visible area.
88
- * @default 5
89
- */
90
- overscan?: number;
91
- /**
92
- * Whether to use React's flushSync. Set to false for React 19.
93
- * @default true
94
- */
95
- useFlushSync?: boolean;
96
- };
97
- /**
98
- * A virtualized data grid component for rendering large datasets.
99
- *
100
- * @example
101
- * ```tsx
102
- * <VirtualDataGrid
103
- * data={items}
104
- * height="500px"
105
- * columns={[
106
- * { id: 'name', header: 'Name', cell: (item) => item.name },
107
- * { id: 'date', header: 'Date', cell: (item) => formatDate(item.date) },
108
- * ]}
109
- * getRowKey={(item) => item.id}
110
- * />
111
- * ```
112
- *
113
- */
114
- export declare function VirtualDataGrid<T>({ data, columns, height, mode, compact, stickyHeader, blurEffect, caption, wrapperClassName, className, getRowKey, onRowClick, activeRowIndex, estimateSize, overscan, useFlushSync, }: VirtualDataGridProps<T>): import("react/jsx-runtime").JSX.Element;