@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.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/DataGrid/DataGrid.d.ts +2 -0
- package/dist/DataGrid/DataGrid.js +132 -0
- package/dist/DataGrid/DataGridContext.d.ts +2 -0
- package/dist/DataGrid/DataGridContext.js +16 -0
- package/dist/DataGrid/index.d.ts +2 -0
- package/dist/DataGrid/index.js +17 -0
- package/dist/DataGrid/utilities.d.ts +36 -0
- package/dist/DataGrid/utilities.js +99 -0
- package/dist/DataGridAnimated/AnimatedWrapper.d.ts +47 -0
- package/dist/DataGridAnimated/AnimatedWrapper.js +49 -0
- package/dist/DataGridAnimated/index.d.ts +4 -0
- package/dist/DataGridAnimated/index.js +17 -0
- package/dist/DataGridAnimated/useAnimatedHeight.d.ts +49 -0
- package/dist/DataGridAnimated/useAnimatedHeight.js +131 -0
- package/dist/DataGridBody/DataGridBody.d.ts +2 -0
- package/dist/DataGridBody/DataGridBody.js +38 -0
- package/dist/DataGridBody/index.d.ts +1 -0
- package/dist/DataGridBody/index.js +13 -0
- package/dist/DataGridCell/DataGridCell.d.ts +14 -0
- package/dist/DataGridCell/DataGridCell.js +77 -0
- package/dist/DataGridCell/index.d.ts +1 -0
- package/dist/DataGridCell/index.js +13 -0
- package/dist/DataGridCellSort/DataGridCellSort.d.ts +2 -0
- package/dist/DataGridCellSort/DataGridCellSort.js +107 -0
- package/dist/DataGridCellSort/index.d.ts +1 -0
- package/dist/DataGridCellSort/index.js +13 -0
- package/dist/DataGridConstants/DataGridConstants.d.ts +37 -0
- package/dist/DataGridConstants/DataGridConstants.js +38 -0
- package/dist/DataGridConstants/index.d.ts +1 -0
- package/dist/DataGridConstants/index.js +13 -0
- package/dist/DataGridFooter/DataGridFooter.d.ts +12 -0
- package/dist/DataGridFooter/DataGridFooter.js +81 -0
- package/dist/DataGridFooter/index.d.ts +1 -0
- package/dist/DataGridFooter/index.js +13 -0
- package/dist/DataGridHeader/DataGridHeader.d.ts +13 -0
- package/dist/DataGridHeader/DataGridHeader.js +80 -0
- package/dist/DataGridHeader/index.d.ts +1 -0
- package/dist/DataGridHeader/index.js +13 -0
- package/dist/DataGridInfinite/InfiniteScrollMarker.d.ts +35 -0
- package/dist/DataGridInfinite/InfiniteScrollMarker.js +53 -0
- package/dist/DataGridInfinite/index.d.ts +4 -0
- package/dist/DataGridInfinite/index.js +17 -0
- package/dist/DataGridInfinite/useInfiniteScroll.d.ts +81 -0
- package/dist/DataGridInfinite/useInfiniteScroll.js +117 -0
- package/dist/DataGridRow/DataGridRow.d.ts +2 -0
- package/dist/DataGridRow/DataGridRow.js +75 -0
- package/dist/DataGridRow/index.d.ts +1 -0
- package/dist/DataGridRow/index.js +13 -0
- package/dist/DataGridSorting/index.d.ts +2 -0
- package/dist/DataGridSorting/index.js +18 -0
- package/dist/DataGridSorting/sortingUtils.d.ts +138 -0
- package/dist/DataGridSorting/sortingUtils.js +234 -0
- package/dist/DataGridVirtual/VirtualDataGrid.d.ts +114 -0
- package/dist/DataGridVirtual/VirtualDataGrid.js +181 -0
- package/dist/DataGridVirtual/index.d.ts +6 -0
- package/dist/DataGridVirtual/index.js +22 -0
- package/dist/DataGridVirtual/useVirtualDataGrid.d.ts +112 -0
- package/dist/DataGridVirtual/useVirtualDataGrid.js +89 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Arno Versini
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# @versini/ui-datagrid
|
|
2
|
+
|
|
3
|
+
A high-performance data grid component library for React with support for:
|
|
4
|
+
|
|
5
|
+
- **Virtualization**: Efficiently render thousands of rows using TanStack Virtual
|
|
6
|
+
- **Infinite Scrolling**: Progressive loading with IntersectionObserver
|
|
7
|
+
- **Animated Height**: Smooth height transitions when content changes
|
|
8
|
+
- **Sorting**: Built-in sorting utilities and sortable column headers
|
|
9
|
+
- **Sticky Header/Footer**: With optional blur effects
|
|
10
|
+
- **Loading States**: Built-in loading overlay with spinner
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @versini/ui-datagrid
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Entry Points
|
|
19
|
+
|
|
20
|
+
Import only what you need for optimal bundle size:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// Core data grid
|
|
24
|
+
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
25
|
+
|
|
26
|
+
// Header, Body, Footer
|
|
27
|
+
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
28
|
+
import { DataGridBody } from "@versini/ui-datagrid/body";
|
|
29
|
+
import { DataGridFooter } from "@versini/ui-datagrid/footer";
|
|
30
|
+
|
|
31
|
+
// Row and Cell
|
|
32
|
+
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
33
|
+
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
34
|
+
import { DataGridCellSort } from "@versini/ui-datagrid/cell-sort";
|
|
35
|
+
|
|
36
|
+
// Infinite scroll (progressive loading)
|
|
37
|
+
import {
|
|
38
|
+
useInfiniteScroll,
|
|
39
|
+
InfiniteScrollMarker
|
|
40
|
+
} from "@versini/ui-datagrid/infinite";
|
|
41
|
+
|
|
42
|
+
// True virtualization for large datasets
|
|
43
|
+
import {
|
|
44
|
+
VirtualDataGrid,
|
|
45
|
+
useVirtualDataGrid
|
|
46
|
+
} from "@versini/ui-datagrid/virtual";
|
|
47
|
+
|
|
48
|
+
// Animated height wrapper
|
|
49
|
+
import {
|
|
50
|
+
AnimatedWrapper,
|
|
51
|
+
useAnimatedHeight
|
|
52
|
+
} from "@versini/ui-datagrid/animated";
|
|
53
|
+
|
|
54
|
+
// Sorting utilities
|
|
55
|
+
import {
|
|
56
|
+
sortByDate,
|
|
57
|
+
sortByNumber,
|
|
58
|
+
sortByString,
|
|
59
|
+
sortByBoolean,
|
|
60
|
+
sortItems,
|
|
61
|
+
getNextSortConfig,
|
|
62
|
+
getOppositeSortDirection,
|
|
63
|
+
toggleSortDirection,
|
|
64
|
+
SortDirections
|
|
65
|
+
} from "@versini/ui-datagrid/sorting";
|
|
66
|
+
|
|
67
|
+
// Constants
|
|
68
|
+
import {
|
|
69
|
+
SortDirections,
|
|
70
|
+
BlurEffects,
|
|
71
|
+
ThemeModes,
|
|
72
|
+
CellWrapper
|
|
73
|
+
} from "@versini/ui-datagrid/constants";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Basic Usage
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
80
|
+
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
81
|
+
import { DataGridBody } from "@versini/ui-datagrid/body";
|
|
82
|
+
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
83
|
+
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
84
|
+
|
|
85
|
+
function MyTable({ data }) {
|
|
86
|
+
return (
|
|
87
|
+
<DataGrid maxHeight="400px" stickyHeader>
|
|
88
|
+
<DataGridHeader>
|
|
89
|
+
<DataGridRow>
|
|
90
|
+
<DataGridCell>Name</DataGridCell>
|
|
91
|
+
<DataGridCell>Email</DataGridCell>
|
|
92
|
+
</DataGridRow>
|
|
93
|
+
</DataGridHeader>
|
|
94
|
+
<DataGridBody>
|
|
95
|
+
{data.map((item) => (
|
|
96
|
+
<DataGridRow key={item.id}>
|
|
97
|
+
<DataGridCell>{item.name}</DataGridCell>
|
|
98
|
+
<DataGridCell>{item.email}</DataGridCell>
|
|
99
|
+
</DataGridRow>
|
|
100
|
+
))}
|
|
101
|
+
</DataGridBody>
|
|
102
|
+
</DataGrid>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Infinite Scroll
|
|
108
|
+
|
|
109
|
+
For datasets with hundreds to thousands of rows, use progressive loading:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
113
|
+
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
114
|
+
import { DataGridBody } from "@versini/ui-datagrid/body";
|
|
115
|
+
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
116
|
+
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
117
|
+
import {
|
|
118
|
+
useInfiniteScroll,
|
|
119
|
+
InfiniteScrollMarker
|
|
120
|
+
} from "@versini/ui-datagrid/infinite";
|
|
121
|
+
import { AnimatedWrapper } from "@versini/ui-datagrid/animated";
|
|
122
|
+
|
|
123
|
+
function MyInfiniteTable({ data }) {
|
|
124
|
+
const { visibleCount, hasMore, markerRef } = useInfiniteScroll({
|
|
125
|
+
totalItems: data.length,
|
|
126
|
+
batchSize: 25
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const visibleData = data.slice(0, visibleCount);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<AnimatedWrapper dependency={visibleCount}>
|
|
133
|
+
<DataGrid maxHeight="500px" stickyHeader>
|
|
134
|
+
<DataGridHeader>
|
|
135
|
+
<DataGridRow>
|
|
136
|
+
<DataGridCell>Name</DataGridCell>
|
|
137
|
+
<DataGridCell>Date</DataGridCell>
|
|
138
|
+
</DataGridRow>
|
|
139
|
+
</DataGridHeader>
|
|
140
|
+
<DataGridBody>
|
|
141
|
+
{visibleData.map((item) => (
|
|
142
|
+
<DataGridRow key={item.id}>
|
|
143
|
+
<DataGridCell>{item.name}</DataGridCell>
|
|
144
|
+
<DataGridCell>{item.date}</DataGridCell>
|
|
145
|
+
</DataGridRow>
|
|
146
|
+
))}
|
|
147
|
+
{hasMore && <InfiniteScrollMarker ref={markerRef} colSpan={2} />}
|
|
148
|
+
</DataGridBody>
|
|
149
|
+
</DataGrid>
|
|
150
|
+
</AnimatedWrapper>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Virtualization for Large Datasets
|
|
156
|
+
|
|
157
|
+
For datasets with 1000+ rows, use true virtualization:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { VirtualDataGrid } from "@versini/ui-datagrid/virtual";
|
|
161
|
+
|
|
162
|
+
function MyVirtualTable({ data }) {
|
|
163
|
+
return (
|
|
164
|
+
<VirtualDataGrid
|
|
165
|
+
data={data}
|
|
166
|
+
height="500px"
|
|
167
|
+
columns={[
|
|
168
|
+
{ id: "name", header: "Name", cell: (item) => item.name },
|
|
169
|
+
{ id: "email", header: "Email", cell: (item) => item.email },
|
|
170
|
+
{ id: "date", header: "Date", cell: (item) => formatDate(item.date) }
|
|
171
|
+
]}
|
|
172
|
+
getRowKey={(item) => item.id}
|
|
173
|
+
estimateSize={48}
|
|
174
|
+
overscan={10}
|
|
175
|
+
/>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Sorting
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { useState, useMemo } from "react";
|
|
184
|
+
import { DataGrid } from "@versini/ui-datagrid/datagrid";
|
|
185
|
+
import { DataGridHeader } from "@versini/ui-datagrid/header";
|
|
186
|
+
import { DataGridBody } from "@versini/ui-datagrid/body";
|
|
187
|
+
import { DataGridRow } from "@versini/ui-datagrid/row";
|
|
188
|
+
import { DataGridCell } from "@versini/ui-datagrid/cell";
|
|
189
|
+
import { DataGridCellSort } from "@versini/ui-datagrid/cell-sort";
|
|
190
|
+
import {
|
|
191
|
+
sortItems,
|
|
192
|
+
getNextSortConfig,
|
|
193
|
+
SortDirections
|
|
194
|
+
} from "@versini/ui-datagrid/sorting";
|
|
195
|
+
|
|
196
|
+
function MySortableTable({ data }) {
|
|
197
|
+
const [sortConfig, setSortConfig] = useState({
|
|
198
|
+
sortedCell: "name",
|
|
199
|
+
sortDirection: SortDirections.ASC
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const sortedData = useMemo(() => {
|
|
203
|
+
return sortItems(
|
|
204
|
+
data,
|
|
205
|
+
sortConfig.sortedCell,
|
|
206
|
+
sortConfig.sortDirection,
|
|
207
|
+
"string"
|
|
208
|
+
);
|
|
209
|
+
}, [data, sortConfig]);
|
|
210
|
+
|
|
211
|
+
const handleSort = (cellId) => {
|
|
212
|
+
setSortConfig(getNextSortConfig(sortConfig, cellId));
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<DataGrid>
|
|
217
|
+
<DataGridHeader>
|
|
218
|
+
<DataGridRow>
|
|
219
|
+
<DataGridCellSort
|
|
220
|
+
cellId="name"
|
|
221
|
+
onSort={handleSort}
|
|
222
|
+
sortDirection={
|
|
223
|
+
sortConfig.sortedCell === "name"
|
|
224
|
+
? sortConfig.sortDirection
|
|
225
|
+
: false
|
|
226
|
+
}
|
|
227
|
+
sortedCell={sortConfig.sortedCell}
|
|
228
|
+
>
|
|
229
|
+
Name
|
|
230
|
+
</DataGridCellSort>
|
|
231
|
+
<DataGridCellSort
|
|
232
|
+
cellId="date"
|
|
233
|
+
onSort={handleSort}
|
|
234
|
+
sortDirection={
|
|
235
|
+
sortConfig.sortedCell === "date"
|
|
236
|
+
? sortConfig.sortDirection
|
|
237
|
+
: false
|
|
238
|
+
}
|
|
239
|
+
sortedCell={sortConfig.sortedCell}
|
|
240
|
+
>
|
|
241
|
+
Date
|
|
242
|
+
</DataGridCellSort>
|
|
243
|
+
</DataGridRow>
|
|
244
|
+
</DataGridHeader>
|
|
245
|
+
<DataGridBody>
|
|
246
|
+
{sortedData.map((item) => (
|
|
247
|
+
<DataGridRow key={item.id}>
|
|
248
|
+
<DataGridCell>{item.name}</DataGridCell>
|
|
249
|
+
<DataGridCell>{item.date}</DataGridCell>
|
|
250
|
+
</DataGridRow>
|
|
251
|
+
))}
|
|
252
|
+
</DataGridBody>
|
|
253
|
+
</DataGrid>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Props
|
|
259
|
+
|
|
260
|
+
### DataGrid
|
|
261
|
+
|
|
262
|
+
| Prop | Type | Default | Description |
|
|
263
|
+
| -------------- | ----------------------------------------------- | ---------- | ---------------------------------- |
|
|
264
|
+
| `mode` | `'dark' \| 'light' \| 'system' \| 'alt-system'` | `'system'` | Theme mode |
|
|
265
|
+
| `compact` | `boolean` | `false` | Reduced padding |
|
|
266
|
+
| `stickyHeader` | `boolean` | `false` | Fixed header while scrolling |
|
|
267
|
+
| `stickyFooter` | `boolean` | `false` | Fixed footer while scrolling |
|
|
268
|
+
| `blurEffect` | `'none' \| 'small' \| 'medium' \| 'large'` | `'none'` | Blur intensity for sticky elements |
|
|
269
|
+
| `maxHeight` | `string` | - | Max height (required for sticky) |
|
|
270
|
+
| `disabled` | `boolean` | `false` | Show loading overlay |
|
|
271
|
+
| `caption` | `ReactNode` | - | Table caption for accessibility |
|
|
272
|
+
|
|
273
|
+
### useInfiniteScroll
|
|
274
|
+
|
|
275
|
+
| Option | Type | Default | Description |
|
|
276
|
+
| ------------ | -------- | ------------ | -------------------------------- |
|
|
277
|
+
| `totalItems` | `number` | **required** | Total number of items |
|
|
278
|
+
| `batchSize` | `number` | `20` | Items to load per batch |
|
|
279
|
+
| `threshold` | `number` | `5` | Items before end to trigger load |
|
|
280
|
+
| `rootMargin` | `string` | `'20px'` | IntersectionObserver margin |
|
|
281
|
+
|
|
282
|
+
### VirtualDataGrid
|
|
283
|
+
|
|
284
|
+
| Prop | Type | Default | Description |
|
|
285
|
+
| -------------- | ---------------------------- | ------------ | ------------------------------- |
|
|
286
|
+
| `data` | `T[]` | **required** | Array of data items |
|
|
287
|
+
| `columns` | `VirtualDataGridColumn<T>[]` | **required** | Column definitions |
|
|
288
|
+
| `height` | `string \| number` | **required** | Container height |
|
|
289
|
+
| `estimateSize` | `number` | `40` | Estimated row height |
|
|
290
|
+
| `overscan` | `number` | `5` | Rows to render outside viewport |
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { DataGridProps } from "./DataGridTypes";
|
|
2
|
+
export declare const DataGrid: ({ caption, className, wrapperClassName, children, mode, compact, stickyHeader, stickyFooter, blurEffect, maxHeight, disabled, ...rest }: DataGridProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.1.0
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useId, useMemo } from "react";
|
|
8
|
+
import { BlurEffects } from "../DataGridConstants/DataGridConstants.js";
|
|
9
|
+
import { DataGridContext } from "./DataGridContext.js";
|
|
10
|
+
import { getDataGridClasses } from "./utilities.js";
|
|
11
|
+
|
|
12
|
+
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
13
|
+
|
|
14
|
+
;// CONCATENATED MODULE: external "react"
|
|
15
|
+
|
|
16
|
+
;// CONCATENATED MODULE: external "../DataGridConstants/DataGridConstants.js"
|
|
17
|
+
|
|
18
|
+
;// CONCATENATED MODULE: external "./DataGridContext.js"
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "./utilities.js"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/DataGrid/DataGrid.tsx
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/* =============================================================================
|
|
29
|
+
* DataGrid (main component)
|
|
30
|
+
* ========================================================================== */ const DataGrid = ({ caption, className, wrapperClassName, children, mode = "system", compact = false, stickyHeader = false, stickyFooter = false, blurEffect = BlurEffects.NONE, maxHeight, disabled = false, ...rest })=>{
|
|
31
|
+
const captionId = useId();
|
|
32
|
+
const classes = useMemo(()=>getDataGridClasses({
|
|
33
|
+
mode,
|
|
34
|
+
className,
|
|
35
|
+
wrapperClassName,
|
|
36
|
+
stickyHeader,
|
|
37
|
+
stickyFooter,
|
|
38
|
+
disabled
|
|
39
|
+
}), [
|
|
40
|
+
mode,
|
|
41
|
+
className,
|
|
42
|
+
wrapperClassName,
|
|
43
|
+
stickyHeader,
|
|
44
|
+
stickyFooter,
|
|
45
|
+
disabled
|
|
46
|
+
]);
|
|
47
|
+
const contextValue = useMemo(()=>({
|
|
48
|
+
mode,
|
|
49
|
+
compact,
|
|
50
|
+
stickyHeader,
|
|
51
|
+
stickyFooter,
|
|
52
|
+
blurEffect,
|
|
53
|
+
disabled
|
|
54
|
+
}), [
|
|
55
|
+
mode,
|
|
56
|
+
compact,
|
|
57
|
+
stickyHeader,
|
|
58
|
+
stickyFooter,
|
|
59
|
+
blurEffect,
|
|
60
|
+
disabled
|
|
61
|
+
]);
|
|
62
|
+
const wrapperStyle = maxHeight ? {
|
|
63
|
+
maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight
|
|
64
|
+
} : undefined;
|
|
65
|
+
/**
|
|
66
|
+
* When sticky header/footer is enabled, use Panel-like structure: - Outer
|
|
67
|
+
* wrapper has overflow-hidden - Scrollable content area in the middle with
|
|
68
|
+
* padding - Header/footer are absolutely positioned.
|
|
69
|
+
*/ const hasSticky = stickyHeader || stickyFooter;
|
|
70
|
+
return /*#__PURE__*/ jsx(DataGridContext.Provider, {
|
|
71
|
+
value: contextValue,
|
|
72
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
73
|
+
className: classes.inner,
|
|
74
|
+
children: [
|
|
75
|
+
disabled && /*#__PURE__*/ jsxs(Fragment, {
|
|
76
|
+
children: [
|
|
77
|
+
/*#__PURE__*/ jsx("div", {
|
|
78
|
+
className: classes.overlay,
|
|
79
|
+
"aria-hidden": "true"
|
|
80
|
+
}),
|
|
81
|
+
/*#__PURE__*/ jsx("div", {
|
|
82
|
+
className: classes.spinnerWrapper,
|
|
83
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
84
|
+
className: classes.spinner,
|
|
85
|
+
role: "status",
|
|
86
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
87
|
+
className: "sr-only",
|
|
88
|
+
children: "Loading..."
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ jsx("div", {
|
|
95
|
+
className: classes.wrapper,
|
|
96
|
+
style: wrapperStyle,
|
|
97
|
+
children: hasSticky ? /*#__PURE__*/ jsx("div", {
|
|
98
|
+
className: classes.scrollableContent,
|
|
99
|
+
style: wrapperStyle,
|
|
100
|
+
children: /*#__PURE__*/ jsxs("table", {
|
|
101
|
+
className: classes.table,
|
|
102
|
+
"aria-describedby": caption ? captionId : undefined,
|
|
103
|
+
...rest,
|
|
104
|
+
children: [
|
|
105
|
+
caption && /*#__PURE__*/ jsx("caption", {
|
|
106
|
+
id: captionId,
|
|
107
|
+
className: classes.caption,
|
|
108
|
+
children: caption
|
|
109
|
+
}),
|
|
110
|
+
children
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
}) : /*#__PURE__*/ jsxs("table", {
|
|
114
|
+
className: classes.table,
|
|
115
|
+
"aria-describedby": caption ? captionId : undefined,
|
|
116
|
+
...rest,
|
|
117
|
+
children: [
|
|
118
|
+
caption && /*#__PURE__*/ jsx("caption", {
|
|
119
|
+
id: captionId,
|
|
120
|
+
className: classes.caption,
|
|
121
|
+
children: caption
|
|
122
|
+
}),
|
|
123
|
+
children
|
|
124
|
+
]
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export { DataGrid };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.1.0
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createContext } from "react";
|
|
7
|
+
|
|
8
|
+
;// CONCATENATED MODULE: external "react"
|
|
9
|
+
|
|
10
|
+
;// CONCATENATED MODULE: ./src/DataGrid/DataGridContext.ts
|
|
11
|
+
|
|
12
|
+
const DataGridContext = createContext({
|
|
13
|
+
mode: "system"
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export { DataGridContext };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.1.0
|
|
3
|
+
© 2026 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DataGrid } from "./DataGrid.js";
|
|
7
|
+
import { DataGridContext } from "./DataGridContext.js";
|
|
8
|
+
|
|
9
|
+
;// CONCATENATED MODULE: external "./DataGrid.js"
|
|
10
|
+
|
|
11
|
+
;// CONCATENATED MODULE: external "./DataGridContext.js"
|
|
12
|
+
|
|
13
|
+
;// CONCATENATED MODULE: ./src/DataGrid/index.ts
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export { DataGrid, DataGridContext };
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Generates classes for the main DataGrid wrapper and table. When sticky
|
|
10
|
+
* header/footer is enabled, uses the Panel-like approach:
|
|
11
|
+
* - Outer wrapper has overflow-hidden
|
|
12
|
+
* - Scrollable area is a separate inner container
|
|
13
|
+
* - Header/footer are absolutely positioned overlays
|
|
14
|
+
*/
|
|
15
|
+
export declare const getDataGridClasses: ({ mode, className, wrapperClassName, stickyHeader, stickyFooter, disabled, }: {
|
|
16
|
+
mode: ThemeMode;
|
|
17
|
+
className?: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
stickyFooter?: boolean;
|
|
20
|
+
stickyHeader?: boolean;
|
|
21
|
+
wrapperClassName?: string;
|
|
22
|
+
}) => {
|
|
23
|
+
overlay: string;
|
|
24
|
+
inner: string;
|
|
25
|
+
spinnerWrapper: string;
|
|
26
|
+
spinner: string;
|
|
27
|
+
wrapper: string;
|
|
28
|
+
/**
|
|
29
|
+
* Scrollable content area - like Panel's scrollableContent Has padding to
|
|
30
|
+
* make room for absolutely positioned header/footer rounded-[inherit] clips
|
|
31
|
+
* the scrollbar at the rounded corners.
|
|
32
|
+
*/
|
|
33
|
+
scrollableContent: string;
|
|
34
|
+
table: string;
|
|
35
|
+
caption: string;
|
|
36
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-datagrid v0.1.0
|
|
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/DataGrid/utilities.ts
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates classes for the loading spinner.
|
|
18
|
+
*/ const getSpinnerClasses = ({ mode })=>{
|
|
19
|
+
return clsx("size-8", "align-[-0.125em]", "border-4", "inline-block animate-spin rounded-full border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite]", {
|
|
20
|
+
"text-copy-dark": mode === "light",
|
|
21
|
+
"text-copy-light": mode === "dark",
|
|
22
|
+
"text-copy-dark dark:text-copy-light": mode === "alt-system",
|
|
23
|
+
"text-copy-light dark:text-copy-dark": mode === "system"
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Generates classes for the loading overlay.
|
|
28
|
+
*/ const getOverlayClasses = ({ mode })=>{
|
|
29
|
+
return {
|
|
30
|
+
inner: "relative",
|
|
31
|
+
overlay: clsx("absolute inset-0 z-20 cursor-not-allowed", "backdrop-blur-xs bg-white/30 dark:bg-black/30"),
|
|
32
|
+
spinnerWrapper: clsx("absolute z-30 top-0 left-0 right-0 h-[min(100%,100vh)]", "flex items-center justify-center", "pointer-events-none"),
|
|
33
|
+
spinner: getSpinnerClasses({
|
|
34
|
+
mode
|
|
35
|
+
})
|
|
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
|
+
* Generates classes for the main DataGrid wrapper and table. When sticky
|
|
49
|
+
* header/footer is enabled, uses the Panel-like approach:
|
|
50
|
+
* - Outer wrapper has overflow-hidden
|
|
51
|
+
* - Scrollable area is a separate inner container
|
|
52
|
+
* - Header/footer are absolutely positioned overlays
|
|
53
|
+
*/ const getDataGridClasses = ({ mode, className, wrapperClassName, stickyHeader, stickyFooter, disabled })=>{
|
|
54
|
+
const overlayClasses = disabled ? getOverlayClasses({
|
|
55
|
+
mode
|
|
56
|
+
}) : null;
|
|
57
|
+
const hasSticky = stickyHeader || stickyFooter;
|
|
58
|
+
return {
|
|
59
|
+
overlay: overlayClasses?.overlay ?? "",
|
|
60
|
+
inner: overlayClasses?.inner ?? "",
|
|
61
|
+
spinnerWrapper: overlayClasses?.spinnerWrapper ?? "",
|
|
62
|
+
spinner: overlayClasses?.spinner ?? "",
|
|
63
|
+
// Outer wrapper - overflow-hidden when sticky, like Panel's outerWrapper.
|
|
64
|
+
wrapper: clsx("not-prose relative w-full rounded-lg shadow-md", {
|
|
65
|
+
"overflow-x-auto": !hasSticky && !disabled,
|
|
66
|
+
"overflow-hidden": hasSticky || disabled,
|
|
67
|
+
"bg-surface-darker": mode === "dark" || mode === "system",
|
|
68
|
+
"bg-surface-light": mode === "light" || mode === "alt-system",
|
|
69
|
+
"dark:bg-surface-light": mode === "system",
|
|
70
|
+
"dark:bg-surface-darker": mode === "alt-system",
|
|
71
|
+
"text-copy-light": mode === "dark",
|
|
72
|
+
"text-copy-dark": mode === "light",
|
|
73
|
+
"text-copy-light dark:text-copy-dark": mode === "system",
|
|
74
|
+
"text-copy-dark dark:text-copy-light": mode === "alt-system"
|
|
75
|
+
}, wrapperClassName),
|
|
76
|
+
/**
|
|
77
|
+
* Scrollable content area - like Panel's scrollableContent Has padding to
|
|
78
|
+
* make room for absolutely positioned header/footer rounded-[inherit] clips
|
|
79
|
+
* the scrollbar at the rounded corners.
|
|
80
|
+
*/ scrollableContent: clsx("overflow-y-auto overflow-x-hidden rounded-[inherit]", {
|
|
81
|
+
"pt-10": stickyHeader,
|
|
82
|
+
"pb-10": stickyFooter
|
|
83
|
+
}),
|
|
84
|
+
table: clsx("my-0 w-full text-left text-sm", className, {
|
|
85
|
+
"text-copy-light": mode === "dark",
|
|
86
|
+
"text-copy-dark": mode === "light",
|
|
87
|
+
"text-copy-light dark:text-copy-dark": mode === "system",
|
|
88
|
+
"text-copy-dark dark:text-copy-light": mode === "alt-system"
|
|
89
|
+
}),
|
|
90
|
+
caption: clsx("py-2 text-sm font-bold", {
|
|
91
|
+
"text-copy-light": mode === "dark",
|
|
92
|
+
"text-copy-dark": mode === "light",
|
|
93
|
+
"text-copy-light dark:text-copy-dark": mode === "system",
|
|
94
|
+
"text-copy-dark dark:text-copy-light": mode === "alt-system"
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export { getDataGridClasses, getStickyBlurClasses };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type AnimatedWrapperProps = {
|
|
2
|
+
/**
|
|
3
|
+
* The content to wrap (typically a DataGrid).
|
|
4
|
+
*/
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* Value that triggers animation when changed. Typically the count of items or
|
|
8
|
+
* a key that changes.
|
|
9
|
+
*/
|
|
10
|
+
dependency: unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Animation duration in milliseconds.
|
|
13
|
+
* @default 300
|
|
14
|
+
*/
|
|
15
|
+
duration?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Whether animation is enabled.
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Additional class name for the wrapper.
|
|
23
|
+
*/
|
|
24
|
+
className?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Wrapper component that animates height changes when content changes.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const { visibleCount } = useInfiniteScroll({ totalItems: data.length });
|
|
32
|
+
*
|
|
33
|
+
* return (
|
|
34
|
+
* <AnimatedWrapper dependency={visibleCount}>
|
|
35
|
+
* <DataGrid>
|
|
36
|
+
* <DataGridBody>
|
|
37
|
+
* {data.slice(0, visibleCount).map((item) => (
|
|
38
|
+
* <DataGridRow key={item.id}>...</DataGridRow>
|
|
39
|
+
* ))}
|
|
40
|
+
* </DataGridBody>
|
|
41
|
+
* </DataGrid>
|
|
42
|
+
* </AnimatedWrapper>
|
|
43
|
+
* );
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
*/
|
|
47
|
+
export declare const AnimatedWrapper: ({ children, dependency, duration, enabled, className, }: AnimatedWrapperProps) => import("react/jsx-runtime").JSX.Element;
|