@versini/ui-datagrid 0.1.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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/dist/DataGrid/DataGrid.d.ts +2 -0
  4. package/dist/DataGrid/DataGrid.js +132 -0
  5. package/dist/DataGrid/DataGridContext.d.ts +2 -0
  6. package/dist/DataGrid/DataGridContext.js +16 -0
  7. package/dist/DataGrid/index.d.ts +2 -0
  8. package/dist/DataGrid/index.js +17 -0
  9. package/dist/DataGrid/utilities.d.ts +36 -0
  10. package/dist/DataGrid/utilities.js +99 -0
  11. package/dist/DataGridAnimated/AnimatedWrapper.d.ts +47 -0
  12. package/dist/DataGridAnimated/AnimatedWrapper.js +49 -0
  13. package/dist/DataGridAnimated/index.d.ts +4 -0
  14. package/dist/DataGridAnimated/index.js +17 -0
  15. package/dist/DataGridAnimated/useAnimatedHeight.d.ts +49 -0
  16. package/dist/DataGridAnimated/useAnimatedHeight.js +131 -0
  17. package/dist/DataGridBody/DataGridBody.d.ts +2 -0
  18. package/dist/DataGridBody/DataGridBody.js +38 -0
  19. package/dist/DataGridBody/index.d.ts +1 -0
  20. package/dist/DataGridBody/index.js +13 -0
  21. package/dist/DataGridCell/DataGridCell.d.ts +14 -0
  22. package/dist/DataGridCell/DataGridCell.js +77 -0
  23. package/dist/DataGridCell/index.d.ts +1 -0
  24. package/dist/DataGridCell/index.js +13 -0
  25. package/dist/DataGridCellSort/DataGridCellSort.d.ts +2 -0
  26. package/dist/DataGridCellSort/DataGridCellSort.js +107 -0
  27. package/dist/DataGridCellSort/index.d.ts +1 -0
  28. package/dist/DataGridCellSort/index.js +13 -0
  29. package/dist/DataGridConstants/DataGridConstants.d.ts +37 -0
  30. package/dist/DataGridConstants/DataGridConstants.js +38 -0
  31. package/dist/DataGridConstants/index.d.ts +1 -0
  32. package/dist/DataGridConstants/index.js +13 -0
  33. package/dist/DataGridFooter/DataGridFooter.d.ts +12 -0
  34. package/dist/DataGridFooter/DataGridFooter.js +81 -0
  35. package/dist/DataGridFooter/index.d.ts +1 -0
  36. package/dist/DataGridFooter/index.js +13 -0
  37. package/dist/DataGridHeader/DataGridHeader.d.ts +13 -0
  38. package/dist/DataGridHeader/DataGridHeader.js +80 -0
  39. package/dist/DataGridHeader/index.d.ts +1 -0
  40. package/dist/DataGridHeader/index.js +13 -0
  41. package/dist/DataGridInfinite/InfiniteScrollMarker.d.ts +35 -0
  42. package/dist/DataGridInfinite/InfiniteScrollMarker.js +53 -0
  43. package/dist/DataGridInfinite/index.d.ts +4 -0
  44. package/dist/DataGridInfinite/index.js +17 -0
  45. package/dist/DataGridInfinite/useInfiniteScroll.d.ts +81 -0
  46. package/dist/DataGridInfinite/useInfiniteScroll.js +117 -0
  47. package/dist/DataGridRow/DataGridRow.d.ts +2 -0
  48. package/dist/DataGridRow/DataGridRow.js +75 -0
  49. package/dist/DataGridRow/index.d.ts +1 -0
  50. package/dist/DataGridRow/index.js +13 -0
  51. package/dist/DataGridSorting/index.d.ts +2 -0
  52. package/dist/DataGridSorting/index.js +18 -0
  53. package/dist/DataGridSorting/sortingUtils.d.ts +138 -0
  54. package/dist/DataGridSorting/sortingUtils.js +234 -0
  55. package/dist/DataGridVirtual/VirtualDataGrid.d.ts +114 -0
  56. package/dist/DataGridVirtual/VirtualDataGrid.js +181 -0
  57. package/dist/DataGridVirtual/index.d.ts +6 -0
  58. package/dist/DataGridVirtual/index.js +22 -0
  59. package/dist/DataGridVirtual/useVirtualDataGrid.d.ts +112 -0
  60. package/dist/DataGridVirtual/useVirtualDataGrid.js +89 -0
  61. package/package.json +103 -0
@@ -0,0 +1,138 @@
1
+ import { type SortDirection } from "../DataGridConstants/DataGridConstants";
2
+ /**
3
+ * Gets the opposite sort direction.
4
+ */
5
+ export declare function getOppositeSortDirection(direction: SortDirection): SortDirection;
6
+ /**
7
+ * Toggles the sort direction or returns default if no current direction.
8
+ */
9
+ export declare function toggleSortDirection(currentDirection?: SortDirection, defaultDirection?: SortDirection): SortDirection;
10
+ /**
11
+ * Comparator function type for sorting.
12
+ */
13
+ export type SortComparator<T> = (a: T, b: T) => number;
14
+ /**
15
+ * Creates a sort comparator for dates. Handles Date objects, ISO strings, and
16
+ * timestamps.
17
+ *
18
+ * @param direction - Sort direction (asc or desc)
19
+ * @param nullPosition - Where to place null/undefined values ('start' or 'end')
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const items = [{ date: '2024-01-15' }, { date: '2024-01-10' }];
24
+ * const sorted = items.sort((a, b) =>
25
+ * sortByDate('asc')(a.date, b.date)
26
+ * );
27
+ * ```
28
+ *
29
+ */
30
+ export declare function sortByDate(direction: SortDirection, nullPosition?: "start" | "end"): SortComparator<Date | string | number | null | undefined>;
31
+ /**
32
+ * Creates a sort comparator for numbers.
33
+ *
34
+ * @param direction - Sort direction (asc or desc)
35
+ * @param nullPosition - Where to place null/undefined values ('start' or 'end')
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const items = [{ count: 5 }, { count: 2 }, { count: 10 }];
40
+ * const sorted = items.sort((a, b) =>
41
+ * sortByNumber('desc')(a.count, b.count)
42
+ * );
43
+ * ```
44
+ *
45
+ */
46
+ export declare function sortByNumber(direction: SortDirection, nullPosition?: "start" | "end"): SortComparator<number | null | undefined>;
47
+ /**
48
+ * Creates a sort comparator for strings. Uses localeCompare for proper Unicode
49
+ * sorting.
50
+ *
51
+ * @param direction - Sort direction (asc or desc)
52
+ * @param options - Options for string comparison
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const items = [{ name: 'Zebra' }, { name: 'apple' }];
57
+ * const sorted = items.sort((a, b) =>
58
+ * sortByString('asc', { caseInsensitive: true })(a.name, b.name)
59
+ * );
60
+ * ```
61
+ *
62
+ */
63
+ export declare function sortByString(direction: SortDirection, options?: {
64
+ nullPosition?: "start" | "end";
65
+ caseInsensitive?: boolean;
66
+ locale?: string;
67
+ }): SortComparator<string | null | undefined>;
68
+ /**
69
+ * Creates a sort comparator for booleans.
70
+ *
71
+ * @param direction - Sort direction (asc = false first, desc = true first)
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const items = [{ active: true }, { active: false }];
76
+ * const sorted = items.sort((a, b) =>
77
+ * sortByBoolean('desc')(a.active, b.active)
78
+ * );
79
+ * // Result: [{ active: true }, { active: false }]
80
+ * ```
81
+ *
82
+ */
83
+ export declare function sortByBoolean(direction: SortDirection): SortComparator<boolean | null | undefined>;
84
+ /**
85
+ * Type for sort configuration.
86
+ */
87
+ export type SortConfig<T extends string = string> = {
88
+ /**
89
+ * The column/field currently being sorted.
90
+ */
91
+ sortedCell: T;
92
+ /**
93
+ * Current sort direction.
94
+ */
95
+ sortDirection: SortDirection;
96
+ };
97
+ /**
98
+ * Helper to create a sort handler function. Returns the new sort config when a
99
+ * column header is clicked.
100
+ *
101
+ * @param currentConfig - Current sort configuration
102
+ * @param cellId - ID of the clicked cell
103
+ * @param defaultDirection - Default direction when clicking a new column
104
+ *
105
+ * @example
106
+ * ```tsx
107
+ * const [sortConfig, setSortConfig] = useState<SortConfig>({
108
+ * sortedCell: 'date',
109
+ * sortDirection: 'desc',
110
+ * });
111
+ *
112
+ * const handleSort = (cellId: string) => {
113
+ * setSortConfig(getNextSortConfig(sortConfig, cellId));
114
+ * };
115
+ * ```
116
+ *
117
+ */
118
+ export declare function getNextSortConfig<T extends string = string>(currentConfig: SortConfig<T>, cellId: T, defaultDirection?: SortDirection): SortConfig<T>;
119
+ /**
120
+ * Generic sort function that sorts an array by a specified key.
121
+ *
122
+ * @param items - Array to sort
123
+ * @param key - Key to sort by
124
+ * @param direction - Sort direction
125
+ * @param sortType - Type of sorting ('date', 'number', 'string', 'boolean')
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * const items = [
130
+ * { id: 1, name: 'Alice', createdAt: '2024-01-01' },
131
+ * { id: 2, name: 'Bob', createdAt: '2024-01-15' },
132
+ * ];
133
+ *
134
+ * const sorted = sortItems(items, 'createdAt', 'desc', 'date');
135
+ * ```
136
+ *
137
+ */
138
+ export declare function sortItems<T extends Record<string, unknown>>(items: T[], key: keyof T, direction: SortDirection, sortType?: "date" | "number" | "string" | "boolean"): T[];
@@ -0,0 +1,234 @@
1
+ /*!
2
+ @versini/ui-datagrid v0.1.0
3
+ © 2026 gizmette.com
4
+ */
5
+
6
+ import { SortDirections } from "../DataGridConstants/DataGridConstants.js";
7
+
8
+ ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
9
+
10
+ ;// CONCATENATED MODULE: ./src/DataGridSorting/sortingUtils.ts
11
+
12
+ /**
13
+ * Gets the opposite sort direction.
14
+ */ function getOppositeSortDirection(direction) {
15
+ return direction === SortDirections.ASC ? SortDirections.DESC : SortDirections.ASC;
16
+ }
17
+ /**
18
+ * Toggles the sort direction or returns default if no current direction.
19
+ */ function toggleSortDirection(currentDirection, defaultDirection = SortDirections.ASC) {
20
+ if (!currentDirection) {
21
+ return defaultDirection;
22
+ }
23
+ return getOppositeSortDirection(currentDirection);
24
+ }
25
+ /**
26
+ * Creates a sort comparator for dates. Handles Date objects, ISO strings, and
27
+ * timestamps.
28
+ *
29
+ * @param direction - Sort direction (asc or desc)
30
+ * @param nullPosition - Where to place null/undefined values ('start' or 'end')
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const items = [{ date: '2024-01-15' }, { date: '2024-01-10' }];
35
+ * const sorted = items.sort((a, b) =>
36
+ * sortByDate('asc')(a.date, b.date)
37
+ * );
38
+ * ```
39
+ *
40
+ */ function sortByDate(direction, nullPosition = "end") {
41
+ return (a, b)=>{
42
+ const aIsNull = a === null || a === undefined;
43
+ const bIsNull = b === null || b === undefined;
44
+ // Handle null values.
45
+ if (aIsNull && bIsNull) {
46
+ return 0;
47
+ }
48
+ if (aIsNull) {
49
+ return nullPosition === "start" ? -1 : 1;
50
+ }
51
+ if (bIsNull) {
52
+ return nullPosition === "start" ? 1 : -1;
53
+ }
54
+ const aTime = a instanceof Date ? a.getTime() : new Date(a).getTime();
55
+ const bTime = b instanceof Date ? b.getTime() : new Date(b).getTime();
56
+ // Handle invalid dates.
57
+ if (Number.isNaN(aTime) && Number.isNaN(bTime)) {
58
+ return 0;
59
+ }
60
+ if (Number.isNaN(aTime)) {
61
+ return nullPosition === "start" ? -1 : 1;
62
+ }
63
+ if (Number.isNaN(bTime)) {
64
+ return nullPosition === "start" ? 1 : -1;
65
+ }
66
+ return direction === SortDirections.ASC ? aTime - bTime : bTime - aTime;
67
+ };
68
+ }
69
+ /**
70
+ * Creates a sort comparator for numbers.
71
+ *
72
+ * @param direction - Sort direction (asc or desc)
73
+ * @param nullPosition - Where to place null/undefined values ('start' or 'end')
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const items = [{ count: 5 }, { count: 2 }, { count: 10 }];
78
+ * const sorted = items.sort((a, b) =>
79
+ * sortByNumber('desc')(a.count, b.count)
80
+ * );
81
+ * ```
82
+ *
83
+ */ function sortByNumber(direction, nullPosition = "end") {
84
+ return (a, b)=>{
85
+ const aIsNull = a === null || a === undefined;
86
+ const bIsNull = b === null || b === undefined;
87
+ // Handle null values.
88
+ if (aIsNull && bIsNull) {
89
+ return 0;
90
+ }
91
+ if (aIsNull) {
92
+ return nullPosition === "start" ? -1 : 1;
93
+ }
94
+ if (bIsNull) {
95
+ return nullPosition === "start" ? 1 : -1;
96
+ }
97
+ // Handle NaN.
98
+ const aIsNaN = Number.isNaN(a);
99
+ const bIsNaN = Number.isNaN(b);
100
+ if (aIsNaN && bIsNaN) {
101
+ return 0;
102
+ }
103
+ if (aIsNaN) {
104
+ return nullPosition === "start" ? -1 : 1;
105
+ }
106
+ if (bIsNaN) {
107
+ return nullPosition === "start" ? 1 : -1;
108
+ }
109
+ return direction === SortDirections.ASC ? a - b : b - a;
110
+ };
111
+ }
112
+ /**
113
+ * Creates a sort comparator for strings. Uses localeCompare for proper Unicode
114
+ * sorting.
115
+ *
116
+ * @param direction - Sort direction (asc or desc)
117
+ * @param options - Options for string comparison
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const items = [{ name: 'Zebra' }, { name: 'apple' }];
122
+ * const sorted = items.sort((a, b) =>
123
+ * sortByString('asc', { caseInsensitive: true })(a.name, b.name)
124
+ * );
125
+ * ```
126
+ *
127
+ */ function sortByString(direction, options) {
128
+ const { nullPosition = "end", caseInsensitive = true, locale } = options ?? {};
129
+ return (a, b)=>{
130
+ const aIsNull = a === null || a === undefined || a === "";
131
+ const bIsNull = b === null || b === undefined || b === "";
132
+ // Handle null/empty values.
133
+ if (aIsNull && bIsNull) {
134
+ return 0;
135
+ }
136
+ if (aIsNull) {
137
+ return nullPosition === "start" ? -1 : 1;
138
+ }
139
+ if (bIsNull) {
140
+ return nullPosition === "start" ? 1 : -1;
141
+ }
142
+ const compareOptions = {
143
+ sensitivity: caseInsensitive ? "base" : "variant"
144
+ };
145
+ const comparison = locale ? a.localeCompare(b, locale, compareOptions) : a.localeCompare(b, undefined, compareOptions);
146
+ return direction === SortDirections.ASC ? comparison : -comparison;
147
+ };
148
+ }
149
+ /**
150
+ * Creates a sort comparator for booleans.
151
+ *
152
+ * @param direction - Sort direction (asc = false first, desc = true first)
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const items = [{ active: true }, { active: false }];
157
+ * const sorted = items.sort((a, b) =>
158
+ * sortByBoolean('desc')(a.active, b.active)
159
+ * );
160
+ * // Result: [{ active: true }, { active: false }]
161
+ * ```
162
+ *
163
+ */ function sortByBoolean(direction) {
164
+ return (a, b)=>{
165
+ // Treat null/undefined as false for comparison.
166
+ const aVal = a === true ? 1 : 0;
167
+ const bVal = b === true ? 1 : 0;
168
+ return direction === SortDirections.ASC ? aVal - bVal : bVal - aVal;
169
+ };
170
+ }
171
+ /**
172
+ * Helper to create a sort handler function. Returns the new sort config when a
173
+ * column header is clicked.
174
+ *
175
+ * @param currentConfig - Current sort configuration
176
+ * @param cellId - ID of the clicked cell
177
+ * @param defaultDirection - Default direction when clicking a new column
178
+ *
179
+ * @example
180
+ * ```tsx
181
+ * const [sortConfig, setSortConfig] = useState<SortConfig>({
182
+ * sortedCell: 'date',
183
+ * sortDirection: 'desc',
184
+ * });
185
+ *
186
+ * const handleSort = (cellId: string) => {
187
+ * setSortConfig(getNextSortConfig(sortConfig, cellId));
188
+ * };
189
+ * ```
190
+ *
191
+ */ function getNextSortConfig(currentConfig, cellId, defaultDirection = SortDirections.DESC) {
192
+ if (currentConfig.sortedCell === cellId) {
193
+ // Toggle direction on same column.
194
+ return {
195
+ sortedCell: cellId,
196
+ sortDirection: getOppositeSortDirection(currentConfig.sortDirection)
197
+ };
198
+ }
199
+ // New column, use default direction.
200
+ return {
201
+ sortedCell: cellId,
202
+ sortDirection: defaultDirection
203
+ };
204
+ }
205
+ /**
206
+ * Generic sort function that sorts an array by a specified key.
207
+ *
208
+ * @param items - Array to sort
209
+ * @param key - Key to sort by
210
+ * @param direction - Sort direction
211
+ * @param sortType - Type of sorting ('date', 'number', 'string', 'boolean')
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * const items = [
216
+ * { id: 1, name: 'Alice', createdAt: '2024-01-01' },
217
+ * { id: 2, name: 'Bob', createdAt: '2024-01-15' },
218
+ * ];
219
+ *
220
+ * const sorted = sortItems(items, 'createdAt', 'desc', 'date');
221
+ * ```
222
+ *
223
+ */ function sortItems(items, key, direction, sortType = "string") {
224
+ const comparator = sortType === "date" ? sortByDate(direction) : sortType === "number" ? sortByNumber(direction) : sortType === "boolean" ? sortByBoolean(direction) : sortByString(direction);
225
+ return [
226
+ ...items
227
+ ].sort((a, b)=>{
228
+ const aVal = a[key];
229
+ const bVal = b[key];
230
+ return comparator(aVal, bVal);
231
+ });
232
+ }
233
+
234
+ export { getNextSortConfig, getOppositeSortDirection, sortByBoolean, sortByDate, sortByNumber, sortByString, sortItems, toggleSortDirection };
@@ -0,0 +1,114 @@
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;
@@ -0,0 +1,181 @@
1
+ /*!
2
+ @versini/ui-datagrid v0.1.0
3
+ © 2026 gizmette.com
4
+ */
5
+
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import clsx from "clsx";
8
+ import { useCallback, useMemo } from "react";
9
+ import { DataGridContext } from "../DataGrid/DataGridContext.js";
10
+ import { getDataGridClasses } from "../DataGrid/utilities.js";
11
+ import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
12
+ import { getHeaderClasses } from "../DataGridHeader/DataGridHeader.js";
13
+ import { useVirtualDataGrid } from "./useVirtualDataGrid.js";
14
+
15
+ ;// CONCATENATED MODULE: external "react/jsx-runtime"
16
+
17
+ ;// CONCATENATED MODULE: external "clsx"
18
+
19
+ ;// CONCATENATED MODULE: external "react"
20
+
21
+ ;// CONCATENATED MODULE: external "../DataGrid/DataGridContext.js"
22
+
23
+ ;// CONCATENATED MODULE: external "../DataGrid/utilities.js"
24
+
25
+ ;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
26
+
27
+ ;// CONCATENATED MODULE: external "../DataGridHeader/DataGridHeader.js"
28
+
29
+ ;// CONCATENATED MODULE: external "./useVirtualDataGrid.js"
30
+
31
+ ;// CONCATENATED MODULE: ./src/DataGridVirtual/VirtualDataGrid.tsx
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ /**
41
+ * A virtualized data grid component for rendering large datasets.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * <VirtualDataGrid
46
+ * data={items}
47
+ * height="500px"
48
+ * columns={[
49
+ * { id: 'name', header: 'Name', cell: (item) => item.name },
50
+ * { id: 'date', header: 'Date', cell: (item) => formatDate(item.date) },
51
+ * ]}
52
+ * getRowKey={(item) => item.id}
53
+ * />
54
+ * ```
55
+ *
56
+ */ function VirtualDataGrid({ data, columns, height, mode = "system", compact = false, stickyHeader = true, blurEffect = BlurEffects.NONE, caption, wrapperClassName, className, getRowKey, onRowClick, activeRowIndex, estimateSize = 40, overscan = 5, useFlushSync = true }) {
57
+ // Memoize the getItemKey function to prevent infinite re-renders.
58
+ const getItemKey = useCallback((index)=>getRowKey ? getRowKey(data[index], index) : index, [
59
+ getRowKey,
60
+ data
61
+ ]);
62
+ const { scrollContainerRef, totalSize, virtualItems, measureElement } = useVirtualDataGrid({
63
+ data,
64
+ estimateSize,
65
+ overscan,
66
+ getItemKey,
67
+ useFlushSync
68
+ });
69
+ const classes = useMemo(()=>getDataGridClasses({
70
+ mode,
71
+ className,
72
+ wrapperClassName,
73
+ stickyHeader,
74
+ stickyFooter: false,
75
+ disabled: false
76
+ }), [
77
+ mode,
78
+ className,
79
+ wrapperClassName,
80
+ stickyHeader
81
+ ]);
82
+ const headerClasses = useMemo(()=>getHeaderClasses({
83
+ stickyHeader,
84
+ mode,
85
+ blurEffect
86
+ }), [
87
+ stickyHeader,
88
+ mode,
89
+ blurEffect
90
+ ]);
91
+ const contextValue = useMemo(()=>({
92
+ mode,
93
+ compact
94
+ }), [
95
+ mode,
96
+ compact
97
+ ]);
98
+ const containerStyle = useMemo(()=>({
99
+ height: typeof height === "number" ? `${height}px` : height,
100
+ overflow: "auto"
101
+ }), [
102
+ height
103
+ ]);
104
+ /**
105
+ * The tbody needs relative positioning for absolute row children and a height
106
+ * equal to the total virtualized size.
107
+ */ const tbodyStyle = useMemo(()=>({
108
+ display: "block",
109
+ height: `${totalSize}px`,
110
+ position: "relative"
111
+ }), [
112
+ totalSize
113
+ ]);
114
+ // Header row needs to be display: table to maintain column widths.
115
+ const theadStyle = useMemo(()=>({
116
+ display: "table",
117
+ width: "100%",
118
+ tableLayout: "fixed"
119
+ }), []);
120
+ return /*#__PURE__*/ jsx(DataGridContext.Provider, {
121
+ value: contextValue,
122
+ children: /*#__PURE__*/ jsx("div", {
123
+ className: classes.wrapper,
124
+ children: /*#__PURE__*/ jsx("div", {
125
+ ref: scrollContainerRef,
126
+ style: containerStyle,
127
+ children: /*#__PURE__*/ jsxs("table", {
128
+ className: classes.table,
129
+ "aria-label": caption,
130
+ children: [
131
+ caption && /*#__PURE__*/ jsx("caption", {
132
+ className: classes.caption,
133
+ children: caption
134
+ }),
135
+ /*#__PURE__*/ jsx("thead", {
136
+ className: headerClasses,
137
+ style: theadStyle,
138
+ children: /*#__PURE__*/ jsx("tr", {
139
+ children: columns.map((column)=>/*#__PURE__*/ jsx("th", {
140
+ style: {
141
+ width: column.width
142
+ },
143
+ className: clsx("px-4 py-3 font-semibold", column.className),
144
+ children: column.header
145
+ }, column.id))
146
+ })
147
+ }),
148
+ /*#__PURE__*/ jsx("tbody", {
149
+ style: tbodyStyle,
150
+ children: virtualItems.map((virtualRow)=>{
151
+ const isActive = activeRowIndex === virtualRow.index;
152
+ return /*#__PURE__*/ jsx("tr", {
153
+ ref: measureElement,
154
+ "data-index": virtualRow.index,
155
+ onClick: onRowClick ? ()=>onRowClick(virtualRow.data, virtualRow.index) : undefined,
156
+ className: clsx("absolute flex w-full", {
157
+ "cursor-pointer": !!onRowClick,
158
+ "bg-action-dark/10 dark:bg-action-light/10": isActive
159
+ }),
160
+ style: {
161
+ height: `${virtualRow.size}px`,
162
+ transform: `translateY(${virtualRow.start}px)`
163
+ },
164
+ children: columns.map((column)=>/*#__PURE__*/ jsx("td", {
165
+ className: clsx(compact ? "px-2 py-1" : "px-4 py-3", column.className),
166
+ style: {
167
+ width: column.width
168
+ },
169
+ children: column.cell(virtualRow.data, virtualRow.index)
170
+ }, column.id))
171
+ }, virtualRow.key);
172
+ })
173
+ })
174
+ ]
175
+ })
176
+ })
177
+ })
178
+ });
179
+ }
180
+
181
+ export { VirtualDataGrid };
@@ -0,0 +1,6 @@
1
+ export type { VirtualItem, Virtualizer } from "@tanstack/react-virtual";
2
+ export { useVirtualizer, useWindowVirtualizer } from "@tanstack/react-virtual";
3
+ export type { UseVirtualDataGridOptions, UseVirtualDataGridReturn, } from "./useVirtualDataGrid";
4
+ export { useVirtualDataGrid } from "./useVirtualDataGrid";
5
+ export type { VirtualDataGridColumn, VirtualDataGridProps, } from "./VirtualDataGrid";
6
+ export { VirtualDataGrid } from "./VirtualDataGrid";
@@ -0,0 +1,22 @@
1
+ /*!
2
+ @versini/ui-datagrid v0.1.0
3
+ © 2026 gizmette.com
4
+ */
5
+
6
+ import { useVirtualizer, useWindowVirtualizer } from "@tanstack/react-virtual";
7
+ import { useVirtualDataGrid } from "./useVirtualDataGrid.js";
8
+ import { VirtualDataGrid } from "./VirtualDataGrid.js";
9
+
10
+ ;// CONCATENATED MODULE: external "@tanstack/react-virtual"
11
+
12
+ ;// CONCATENATED MODULE: external "./useVirtualDataGrid.js"
13
+
14
+ ;// CONCATENATED MODULE: external "./VirtualDataGrid.js"
15
+
16
+ ;// CONCATENATED MODULE: ./src/DataGridVirtual/index.ts
17
+ // Re-export from @tanstack/react-virtual for convenience.
18
+
19
+
20
+
21
+
22
+ export { VirtualDataGrid, useVirtualDataGrid, useVirtualizer, useWindowVirtualizer };