@datum-cloud/datum-ui 0.3.0-alpha.9d90881 → 0.3.0-alpha.ffa8392
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/features/data-table/adapters/nuqs-adapter.d.ts +13 -12
- package/dist/components/features/data-table/adapters/nuqs-adapter.d.ts.map +1 -1
- package/dist/components/features/data-table/components/bulk-actions.d.ts.map +1 -1
- package/dist/components/features/data-table/components/content.d.ts.map +1 -1
- package/dist/components/features/data-table/components/pagination.d.ts.map +1 -1
- package/dist/components/features/data-table/core/client-provider.d.ts +10 -2
- package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
- package/dist/components/features/data-table/core/data-table-context.d.ts +8 -3
- package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -1
- package/dist/components/features/data-table/core/filter-engine.d.ts +16 -0
- package/dist/components/features/data-table/core/filter-engine.d.ts.map +1 -0
- package/dist/components/features/data-table/core/server-provider.d.ts +10 -2
- package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -1
- package/dist/components/features/data-table/core/store.d.ts +3 -0
- package/dist/components/features/data-table/core/store.d.ts.map +1 -0
- package/dist/components/features/data-table/filters/checkbox-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/filters/date-picker-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/filters/select-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/index.d.ts +1 -1
- package/dist/components/features/data-table/hooks/index.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-client.d.ts +17 -20
- package/dist/components/features/data-table/hooks/use-data-table-client.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-context.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-server.d.ts +27 -19
- package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-selectors.d.ts +81 -0
- package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -0
- package/dist/components/features/data-table/index.d.ts +3 -2
- package/dist/components/features/data-table/index.d.ts.map +1 -1
- package/dist/components/features/data-table/types.d.ts +47 -0
- package/dist/components/features/data-table/types.d.ts.map +1 -1
- package/dist/data-table/index.mjs +795 -480
- package/package.json +3 -3
- package/dist/components/features/data-table/hooks/use-inline-contents.d.ts +0 -7
- package/dist/components/features/data-table/hooks/use-inline-contents.d.ts.map +0 -1
|
@@ -13,10 +13,10 @@ import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as Tab
|
|
|
13
13
|
import { t as CalendarDatePicker } from "../calendar-date-picker-BBAg78Lg.mjs";
|
|
14
14
|
import { cva } from "class-variance-authority";
|
|
15
15
|
import { ArrowDown, ArrowUp, ArrowUpDown, Check, ChevronDown, ChevronLeft, ChevronRight, MoreHorizontal, X } from "lucide-react";
|
|
16
|
-
import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
16
|
+
import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
17
17
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
-
import { parseAsInteger,
|
|
19
|
-
import { flexRender, getCoreRowModel,
|
|
18
|
+
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
|
19
|
+
import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
20
20
|
|
|
21
21
|
//#region src/components/features/data-table/constants.ts
|
|
22
22
|
const DEFAULT_PAGE_SIZE = 20;
|
|
@@ -32,15 +32,32 @@ const DEFAULT_LOADING_ROWS = 5;
|
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region src/components/features/data-table/adapters/nuqs-adapter.ts
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* Serialize SortingState to URL-friendly string.
|
|
36
|
+
* Format: "name" (asc), "-name" (desc), comma-separated for multi-sort.
|
|
37
|
+
* Example: "-department,name" → [{id:"department",desc:true},{id:"name",desc:false}]
|
|
37
38
|
*/
|
|
38
|
-
function
|
|
39
|
-
if (
|
|
40
|
-
return
|
|
39
|
+
function serializeSorting(sorting) {
|
|
40
|
+
if (sorting.length === 0) return "";
|
|
41
|
+
return sorting.map((s) => s.desc ? `-${s.id}` : s.id).join(",");
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse URL sort string back to SortingState.
|
|
45
|
+
*/
|
|
46
|
+
function parseSorting(value) {
|
|
47
|
+
if (!value) return [];
|
|
48
|
+
return value.split(",").filter(Boolean).map((part) => {
|
|
49
|
+
if (part.startsWith("-")) return {
|
|
50
|
+
id: part.slice(1),
|
|
51
|
+
desc: true
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
id: part,
|
|
55
|
+
desc: false
|
|
56
|
+
};
|
|
57
|
+
});
|
|
41
58
|
}
|
|
42
59
|
const coreSearchParams = {
|
|
43
|
-
sort:
|
|
60
|
+
sort: parseAsString.withDefault(""),
|
|
44
61
|
q: parseAsString.withDefault(""),
|
|
45
62
|
page: parseAsInteger.withDefault(0),
|
|
46
63
|
size: parseAsInteger.withDefault(DEFAULT_PAGE_SIZE)
|
|
@@ -48,22 +65,21 @@ const coreSearchParams = {
|
|
|
48
65
|
/**
|
|
49
66
|
* Hook that creates a StateAdapter backed by nuqs URL query state.
|
|
50
67
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
68
|
+
* URL format:
|
|
69
|
+
* - `?sort=name` (asc) or `?sort=-name` (desc), comma-separated for multi-sort
|
|
70
|
+
* - `?q=search` for search text
|
|
71
|
+
* - `?page=0&size=20` for pagination
|
|
72
|
+
* - Custom filter keys as declared in options
|
|
53
73
|
*
|
|
54
74
|
* Requires `nuqs` to be installed in the consumer app.
|
|
55
75
|
*
|
|
56
76
|
* @example
|
|
57
77
|
* ```tsx
|
|
58
|
-
* // Zero config — all defaults synced to URL
|
|
59
|
-
* const stateAdapter = useNuqsAdapter()
|
|
60
|
-
* const tableState = useDataTableClient({ data, columns, stateAdapter })
|
|
61
|
-
*
|
|
62
|
-
* // With custom filters synced to URL
|
|
63
|
-
* import { parseAsString } from 'nuqs'
|
|
64
|
-
*
|
|
65
78
|
* const stateAdapter = useNuqsAdapter({
|
|
66
|
-
* filters: {
|
|
79
|
+
* filters: {
|
|
80
|
+
* status: parseAsString.withDefault(''),
|
|
81
|
+
* department: parseAsArrayOf(parseAsString).withDefault([]),
|
|
82
|
+
* },
|
|
67
83
|
* })
|
|
68
84
|
* const tableState = useDataTableClient({ data, columns, stateAdapter })
|
|
69
85
|
* ```
|
|
@@ -75,7 +91,7 @@ function useNuqsAdapter(options = {}) {
|
|
|
75
91
|
const [filterState, setFilterState] = useQueryStates(hasFilters ? filterParsers : { _dt: parseAsString.withDefault("") });
|
|
76
92
|
return useMemo(() => ({
|
|
77
93
|
read: () => ({
|
|
78
|
-
sorting: coreState.sort,
|
|
94
|
+
sorting: parseSorting(coreState.sort),
|
|
79
95
|
search: coreState.q,
|
|
80
96
|
pageIndex: coreState.page,
|
|
81
97
|
pageSize: coreState.size,
|
|
@@ -83,19 +99,24 @@ function useNuqsAdapter(options = {}) {
|
|
|
83
99
|
}),
|
|
84
100
|
write: (state) => {
|
|
85
101
|
setCoreState({
|
|
86
|
-
sort: state.sorting,
|
|
102
|
+
sort: serializeSorting(state.sorting),
|
|
87
103
|
q: state.search,
|
|
88
104
|
page: state.pageIndex ?? 0,
|
|
89
105
|
size: state.pageSize ?? DEFAULT_PAGE_SIZE
|
|
90
106
|
});
|
|
91
|
-
if (hasFilters)
|
|
107
|
+
if (hasFilters && filterParsers) {
|
|
108
|
+
const update = {};
|
|
109
|
+
for (const key of Object.keys(filterParsers)) update[key] = state.filters?.[key] ?? null;
|
|
110
|
+
setFilterState(update);
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
113
|
}), [
|
|
94
114
|
coreState,
|
|
95
115
|
filterState,
|
|
96
116
|
hasFilters,
|
|
97
117
|
setCoreState,
|
|
98
|
-
setFilterState
|
|
118
|
+
setFilterState,
|
|
119
|
+
filterParsers
|
|
99
120
|
]);
|
|
100
121
|
}
|
|
101
122
|
|
|
@@ -131,22 +152,439 @@ function withSelectionColumn(columns, options = {}) {
|
|
|
131
152
|
return [createSelectionColumn(options), ...columns];
|
|
132
153
|
}
|
|
133
154
|
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/components/features/data-table/core/filter-engine.ts
|
|
157
|
+
const FILTER_STRATEGIES = {
|
|
158
|
+
"checkbox": (cellValue, filterValue) => {
|
|
159
|
+
if (filterValue == null) return true;
|
|
160
|
+
if (Array.isArray(filterValue) && filterValue.length === 0) return true;
|
|
161
|
+
if (!Array.isArray(filterValue)) return cellValue === filterValue;
|
|
162
|
+
if (Array.isArray(cellValue)) return cellValue.some((v) => filterValue.includes(v));
|
|
163
|
+
return filterValue.includes(cellValue);
|
|
164
|
+
},
|
|
165
|
+
"select": (cellValue, filterValue) => {
|
|
166
|
+
if (filterValue == null || filterValue === "") return true;
|
|
167
|
+
return cellValue === filterValue;
|
|
168
|
+
},
|
|
169
|
+
"date-gte": (cellValue, filterValue) => {
|
|
170
|
+
if (!filterValue) return true;
|
|
171
|
+
const cell = new Date(cellValue);
|
|
172
|
+
const filter = new Date(filterValue);
|
|
173
|
+
if (Number.isNaN(cell.getTime()) || Number.isNaN(filter.getTime())) return true;
|
|
174
|
+
return cell >= filter;
|
|
175
|
+
},
|
|
176
|
+
"date-lte": (cellValue, filterValue) => {
|
|
177
|
+
if (!filterValue) return true;
|
|
178
|
+
const cell = new Date(cellValue);
|
|
179
|
+
const filter = new Date(filterValue);
|
|
180
|
+
if (Number.isNaN(cell.getTime()) || Number.isNaN(filter.getTime())) return true;
|
|
181
|
+
return cell <= filter;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
function resolveStrategy(strategy) {
|
|
185
|
+
if (!strategy) return void 0;
|
|
186
|
+
if (typeof strategy === "function") return strategy;
|
|
187
|
+
return FILTER_STRATEGIES[strategy];
|
|
188
|
+
}
|
|
189
|
+
function applyFilters(data, filters, search, registeredFilters, customFilterFns, searchConfig) {
|
|
190
|
+
const hasFilters = Object.keys(filters).length > 0;
|
|
191
|
+
const hasSearch = search.length > 0;
|
|
192
|
+
if (!hasFilters && !hasSearch) return data;
|
|
193
|
+
return data.filter((row) => {
|
|
194
|
+
if (hasFilters) for (const [column, value] of Object.entries(filters)) {
|
|
195
|
+
const fn = customFilterFns[column] ?? resolveStrategy(registeredFilters.get(column));
|
|
196
|
+
if (!fn) {
|
|
197
|
+
console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const cellValue = row[column];
|
|
201
|
+
if (!fn(cellValue, value)) return false;
|
|
202
|
+
}
|
|
203
|
+
if (hasSearch) {
|
|
204
|
+
const query = search.toLowerCase();
|
|
205
|
+
if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
|
|
206
|
+
if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
|
|
207
|
+
const cellValue = row[col];
|
|
208
|
+
return cellValue != null && String(cellValue).toLowerCase().includes(query);
|
|
209
|
+
});
|
|
210
|
+
return Object.values(row).some((val) => {
|
|
211
|
+
if (val == null) return false;
|
|
212
|
+
return String(val).toLowerCase().includes(query);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/components/features/data-table/core/store.ts
|
|
221
|
+
function createDataTableStore(options) {
|
|
222
|
+
const registeredFilters = /* @__PURE__ */ new Map();
|
|
223
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
224
|
+
function computeFilteredData(s) {
|
|
225
|
+
if (s.mode === "server") return s.data;
|
|
226
|
+
return applyFilters(s.data, s.filters, s.search, registeredFilters, options.filterFns ?? {}, {
|
|
227
|
+
searchFn: options.searchFn,
|
|
228
|
+
searchableColumns: options.searchableColumns
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
let state = {
|
|
232
|
+
data: options.data,
|
|
233
|
+
filteredData: options.data,
|
|
234
|
+
sorting: options.defaultSort ?? [],
|
|
235
|
+
filters: options.defaultFilters ?? {},
|
|
236
|
+
search: "",
|
|
237
|
+
rowSelection: {},
|
|
238
|
+
pageIndex: 0,
|
|
239
|
+
pageSize: options.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
240
|
+
mode: options.mode,
|
|
241
|
+
isLoading: false,
|
|
242
|
+
error: null,
|
|
243
|
+
inlineContents: []
|
|
244
|
+
};
|
|
245
|
+
if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
|
|
246
|
+
...state,
|
|
247
|
+
filteredData: computeFilteredData(state)
|
|
248
|
+
};
|
|
249
|
+
function notify() {
|
|
250
|
+
for (const listener of listeners) listener();
|
|
251
|
+
}
|
|
252
|
+
function setState(next) {
|
|
253
|
+
state = next;
|
|
254
|
+
notify();
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
getSnapshot: () => state,
|
|
258
|
+
subscribe: (listener) => {
|
|
259
|
+
listeners.add(listener);
|
|
260
|
+
return () => listeners.delete(listener);
|
|
261
|
+
},
|
|
262
|
+
setData: (data) => {
|
|
263
|
+
const next = {
|
|
264
|
+
...state,
|
|
265
|
+
data,
|
|
266
|
+
pageIndex: 0,
|
|
267
|
+
rowSelection: {}
|
|
268
|
+
};
|
|
269
|
+
setState({
|
|
270
|
+
...next,
|
|
271
|
+
filteredData: computeFilteredData(next)
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
setServerData: (data) => {
|
|
275
|
+
setState({
|
|
276
|
+
...state,
|
|
277
|
+
data,
|
|
278
|
+
filteredData: data
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
setTable: (_t) => {},
|
|
282
|
+
setSorting: (sorting) => {
|
|
283
|
+
setState({
|
|
284
|
+
...state,
|
|
285
|
+
sorting,
|
|
286
|
+
rowSelection: {}
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
setFilter: (key, value) => {
|
|
290
|
+
const next = {
|
|
291
|
+
...state,
|
|
292
|
+
filters: {
|
|
293
|
+
...state.filters,
|
|
294
|
+
[key]: value
|
|
295
|
+
},
|
|
296
|
+
rowSelection: {},
|
|
297
|
+
pageIndex: 0
|
|
298
|
+
};
|
|
299
|
+
setState({
|
|
300
|
+
...next,
|
|
301
|
+
filteredData: computeFilteredData(next)
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
clearFilter: (key) => {
|
|
305
|
+
const filters = Object.fromEntries(Object.entries(state.filters).filter(([k]) => k !== key));
|
|
306
|
+
const next = {
|
|
307
|
+
...state,
|
|
308
|
+
filters,
|
|
309
|
+
rowSelection: {},
|
|
310
|
+
pageIndex: 0
|
|
311
|
+
};
|
|
312
|
+
setState({
|
|
313
|
+
...next,
|
|
314
|
+
filteredData: computeFilteredData(next)
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
clearAllFilters: () => {
|
|
318
|
+
const next = {
|
|
319
|
+
...state,
|
|
320
|
+
filters: {},
|
|
321
|
+
rowSelection: {},
|
|
322
|
+
pageIndex: 0
|
|
323
|
+
};
|
|
324
|
+
setState({
|
|
325
|
+
...next,
|
|
326
|
+
filteredData: computeFilteredData(next)
|
|
327
|
+
});
|
|
328
|
+
},
|
|
329
|
+
setSearch: (search) => {
|
|
330
|
+
const next = {
|
|
331
|
+
...state,
|
|
332
|
+
search,
|
|
333
|
+
rowSelection: {},
|
|
334
|
+
pageIndex: 0
|
|
335
|
+
};
|
|
336
|
+
setState({
|
|
337
|
+
...next,
|
|
338
|
+
filteredData: computeFilteredData(next)
|
|
339
|
+
});
|
|
340
|
+
},
|
|
341
|
+
clearSearch: () => {
|
|
342
|
+
const next = {
|
|
343
|
+
...state,
|
|
344
|
+
search: "",
|
|
345
|
+
rowSelection: {},
|
|
346
|
+
pageIndex: 0
|
|
347
|
+
};
|
|
348
|
+
setState({
|
|
349
|
+
...next,
|
|
350
|
+
filteredData: computeFilteredData(next)
|
|
351
|
+
});
|
|
352
|
+
},
|
|
353
|
+
setRowSelection: (rowSelection) => {
|
|
354
|
+
setState({
|
|
355
|
+
...state,
|
|
356
|
+
rowSelection
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
setPageIndex: (pageIndex) => {
|
|
360
|
+
setState({
|
|
361
|
+
...state,
|
|
362
|
+
pageIndex,
|
|
363
|
+
rowSelection: {}
|
|
364
|
+
});
|
|
365
|
+
},
|
|
366
|
+
setPageSize: (pageSize) => {
|
|
367
|
+
setState({
|
|
368
|
+
...state,
|
|
369
|
+
pageSize,
|
|
370
|
+
pageIndex: 0,
|
|
371
|
+
rowSelection: {}
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
setLoading: (isLoading) => {
|
|
375
|
+
setState({
|
|
376
|
+
...state,
|
|
377
|
+
isLoading
|
|
378
|
+
});
|
|
379
|
+
},
|
|
380
|
+
setError: (error) => {
|
|
381
|
+
setState({
|
|
382
|
+
...state,
|
|
383
|
+
error
|
|
384
|
+
});
|
|
385
|
+
},
|
|
386
|
+
registerFilter: (column, strategy) => {
|
|
387
|
+
registeredFilters.set(column, strategy);
|
|
388
|
+
const filteredData = computeFilteredData(state);
|
|
389
|
+
setState({
|
|
390
|
+
...state,
|
|
391
|
+
filteredData
|
|
392
|
+
});
|
|
393
|
+
},
|
|
394
|
+
unregisterFilter: (column) => {
|
|
395
|
+
registeredFilters.delete(column);
|
|
396
|
+
if (column in state.filters) {
|
|
397
|
+
const filteredData = computeFilteredData(state);
|
|
398
|
+
setState({
|
|
399
|
+
...state,
|
|
400
|
+
filteredData
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
registerInlineContent: (entry) => {
|
|
405
|
+
const existing = state.inlineContents.findIndex((e) => e.id === entry.id);
|
|
406
|
+
const inlineContents = existing >= 0 ? state.inlineContents.map((e, i) => i === existing ? entry : e) : [...state.inlineContents, entry];
|
|
407
|
+
setState({
|
|
408
|
+
...state,
|
|
409
|
+
inlineContents
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
unregisterInlineContent: (id) => {
|
|
413
|
+
const inlineContents = state.inlineContents.filter((e) => e.id !== id);
|
|
414
|
+
setState({
|
|
415
|
+
...state,
|
|
416
|
+
inlineContents
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
134
422
|
//#endregion
|
|
135
423
|
//#region src/components/features/data-table/core/data-table-context.tsx
|
|
424
|
+
const DataTableStoreContext = createContext(null);
|
|
425
|
+
const TableInstanceContext = createContext(null);
|
|
136
426
|
const DataTableContext = createContext(null);
|
|
427
|
+
function useDataTableStore() {
|
|
428
|
+
const store = use(DataTableStoreContext);
|
|
429
|
+
if (!store) throw new Error("useDataTableStore must be used within a <DataTable.Client> or <DataTable.Server> provider");
|
|
430
|
+
return store;
|
|
431
|
+
}
|
|
432
|
+
function useTableInstance() {
|
|
433
|
+
const table = use(TableInstanceContext);
|
|
434
|
+
if (!table) throw new Error("useTableInstance must be used within a <DataTable.Client> or <DataTable.Server> provider");
|
|
435
|
+
return table;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/components/features/data-table/hooks/use-selectors.ts
|
|
440
|
+
function shallowEqual(a, b) {
|
|
441
|
+
const keysA = Object.keys(a);
|
|
442
|
+
const keysB = Object.keys(b);
|
|
443
|
+
if (keysA.length !== keysB.length) return false;
|
|
444
|
+
for (const key of keysA) {
|
|
445
|
+
const va = a[key];
|
|
446
|
+
const vb = b[key];
|
|
447
|
+
if (va === vb) continue;
|
|
448
|
+
if (Array.isArray(va) && Array.isArray(vb)) {
|
|
449
|
+
if (va.length !== vb.length) return false;
|
|
450
|
+
for (let i = 0; i < va.length; i++) if (va[i] !== vb[i]) return false;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
function useSliceSelector(selector) {
|
|
458
|
+
const store = useDataTableStore();
|
|
459
|
+
const cachedRef = useRef(null);
|
|
460
|
+
const getSnapshot = useCallback(() => {
|
|
461
|
+
const next = selector(store.getSnapshot());
|
|
462
|
+
if (cachedRef.current && shallowEqual(cachedRef.current, next)) return cachedRef.current;
|
|
463
|
+
cachedRef.current = next;
|
|
464
|
+
return next;
|
|
465
|
+
}, [store, selector]);
|
|
466
|
+
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
467
|
+
}
|
|
468
|
+
function useDataTableFilters() {
|
|
469
|
+
const store = useDataTableStore();
|
|
470
|
+
return useSliceSelector(useCallback((state) => ({
|
|
471
|
+
filters: state.filters,
|
|
472
|
+
setFilter: store.setFilter,
|
|
473
|
+
clearFilter: store.clearFilter,
|
|
474
|
+
clearAllFilters: store.clearAllFilters,
|
|
475
|
+
registerFilter: store.registerFilter,
|
|
476
|
+
unregisterFilter: store.unregisterFilter
|
|
477
|
+
}), [store]));
|
|
478
|
+
}
|
|
479
|
+
function useDataTableSearch() {
|
|
480
|
+
const store = useDataTableStore();
|
|
481
|
+
return useSliceSelector(useCallback((state) => ({
|
|
482
|
+
search: state.search,
|
|
483
|
+
setSearch: store.setSearch,
|
|
484
|
+
clearSearch: store.clearSearch
|
|
485
|
+
}), [store]));
|
|
486
|
+
}
|
|
487
|
+
function useDataTableSorting() {
|
|
488
|
+
const store = useDataTableStore();
|
|
489
|
+
return useSliceSelector(useCallback((state) => ({
|
|
490
|
+
sorting: state.sorting,
|
|
491
|
+
setSorting: store.setSorting
|
|
492
|
+
}), [store]));
|
|
493
|
+
}
|
|
494
|
+
function useDataTableSelection() {
|
|
495
|
+
const store = useDataTableStore();
|
|
496
|
+
const table = useTableInstance();
|
|
497
|
+
return useSliceSelector(useCallback((state) => ({
|
|
498
|
+
rowSelection: state.rowSelection,
|
|
499
|
+
setRowSelection: store.setRowSelection,
|
|
500
|
+
selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
|
|
501
|
+
}), [store, table]));
|
|
502
|
+
}
|
|
503
|
+
function useDataTablePagination() {
|
|
504
|
+
const store = useDataTableStore();
|
|
505
|
+
const table = useTableInstance();
|
|
506
|
+
const nextPage = useCallback(() => table.nextPage(), [table]);
|
|
507
|
+
const prevPage = useCallback(() => table.previousPage(), [table]);
|
|
508
|
+
return useSliceSelector(useCallback((state) => ({
|
|
509
|
+
canNextPage: table.getCanNextPage(),
|
|
510
|
+
canPrevPage: table.getCanPreviousPage(),
|
|
511
|
+
nextPage,
|
|
512
|
+
prevPage,
|
|
513
|
+
pageIndex: state.pageIndex,
|
|
514
|
+
pageCount: table.getPageCount(),
|
|
515
|
+
setPageIndex: store.setPageIndex,
|
|
516
|
+
pageSize: state.pageSize,
|
|
517
|
+
setPageSize: store.setPageSize,
|
|
518
|
+
totalRows: state.filteredData.length
|
|
519
|
+
}), [
|
|
520
|
+
store,
|
|
521
|
+
table,
|
|
522
|
+
nextPage,
|
|
523
|
+
prevPage
|
|
524
|
+
]));
|
|
525
|
+
}
|
|
526
|
+
function useDataTableRows() {
|
|
527
|
+
const table = useTableInstance();
|
|
528
|
+
return useSliceSelector(useCallback((_state) => ({
|
|
529
|
+
rows: table.getRowModel().rows,
|
|
530
|
+
headerGroups: table.getHeaderGroups(),
|
|
531
|
+
totalColumns: table.getAllColumns().length
|
|
532
|
+
}), [table]));
|
|
533
|
+
}
|
|
534
|
+
function useDataTableLoading() {
|
|
535
|
+
return useSliceSelector(useCallback((state) => ({
|
|
536
|
+
isLoading: state.isLoading,
|
|
537
|
+
error: state.error
|
|
538
|
+
}), []));
|
|
539
|
+
}
|
|
540
|
+
function useDataTableInlineContents() {
|
|
541
|
+
const store = useDataTableStore();
|
|
542
|
+
return useSliceSelector(useCallback((state) => ({
|
|
543
|
+
inlineContents: state.inlineContents,
|
|
544
|
+
registerInlineContent: store.registerInlineContent,
|
|
545
|
+
unregisterInlineContent: store.unregisterInlineContent
|
|
546
|
+
}), [store]));
|
|
547
|
+
}
|
|
137
548
|
function useDataTableContext() {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
549
|
+
const store = useDataTableStore();
|
|
550
|
+
const table = useTableInstance();
|
|
551
|
+
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
552
|
+
return {
|
|
553
|
+
table,
|
|
554
|
+
mode: state.mode,
|
|
555
|
+
sorting: state.sorting,
|
|
556
|
+
filters: state.filters,
|
|
557
|
+
search: state.search,
|
|
558
|
+
rowSelection: state.rowSelection,
|
|
559
|
+
isLoading: state.isLoading,
|
|
560
|
+
setSorting: store.setSorting,
|
|
561
|
+
setFilter: store.setFilter,
|
|
562
|
+
clearFilter: store.clearFilter,
|
|
563
|
+
clearAllFilters: store.clearAllFilters,
|
|
564
|
+
setSearch: store.setSearch,
|
|
565
|
+
clearSearch: store.clearSearch,
|
|
566
|
+
setRowSelection: store.setRowSelection,
|
|
567
|
+
pagination: {
|
|
568
|
+
canNextPage: table.getCanNextPage(),
|
|
569
|
+
canPrevPage: table.getCanPreviousPage(),
|
|
570
|
+
nextPage: () => table.nextPage(),
|
|
571
|
+
prevPage: () => table.previousPage(),
|
|
572
|
+
pageIndex: state.pageIndex,
|
|
573
|
+
pageCount: table.getPageCount(),
|
|
574
|
+
setPageIndex: store.setPageIndex,
|
|
575
|
+
pageSize: state.pageSize,
|
|
576
|
+
setPageSize: store.setPageSize
|
|
577
|
+
},
|
|
578
|
+
inlineContents: state.inlineContents,
|
|
579
|
+
registerInlineContent: store.registerInlineContent,
|
|
580
|
+
unregisterInlineContent: store.unregisterInlineContent
|
|
581
|
+
};
|
|
141
582
|
}
|
|
142
583
|
|
|
143
584
|
//#endregion
|
|
144
585
|
//#region src/components/features/data-table/components/bulk-actions.tsx
|
|
145
586
|
function DataTableBulkActions({ children, className }) {
|
|
146
|
-
const {
|
|
147
|
-
const selectedRows = useMemo(() => {
|
|
148
|
-
return table.getFilteredSelectedRowModel().rows.map((row) => row.original);
|
|
149
|
-
}, [table, rowSelection]);
|
|
587
|
+
const { selectedRows } = useDataTableSelection();
|
|
150
588
|
if (selectedRows.length === 0) return null;
|
|
151
589
|
return /* @__PURE__ */ jsx("div", {
|
|
152
590
|
"data-slot": "dt-bulk-actions",
|
|
@@ -170,7 +608,7 @@ function DataTableColumnHeader({ column, title, className }) {
|
|
|
170
608
|
children: /* @__PURE__ */ jsxs("button", {
|
|
171
609
|
type: "button",
|
|
172
610
|
className: "flex items-center gap-1 hover:text-foreground -ml-3 h-8 px-3 cursor-pointer",
|
|
173
|
-
onClick:
|
|
611
|
+
onClick: column.getToggleSortingHandler(),
|
|
174
612
|
children: [/* @__PURE__ */ jsx("span", { children: title }), sorted === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-4" }) : sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-4" })]
|
|
175
613
|
})
|
|
176
614
|
});
|
|
@@ -197,10 +635,10 @@ function renderInlineContentRow(entry, colSpan, rows) {
|
|
|
197
635
|
}, entry.id);
|
|
198
636
|
}
|
|
199
637
|
function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName }) {
|
|
200
|
-
const {
|
|
201
|
-
const
|
|
638
|
+
const { rows, headerGroups, totalColumns } = useDataTableRows();
|
|
639
|
+
const { inlineContents } = useDataTableInlineContents();
|
|
202
640
|
const openInlineContents = useMemo(() => inlineContents.filter((e) => e.open), [inlineContents]);
|
|
203
|
-
const colSpan =
|
|
641
|
+
const colSpan = totalColumns;
|
|
204
642
|
return /* @__PURE__ */ jsx("div", {
|
|
205
643
|
className: cn("datum-ui-data-table", className),
|
|
206
644
|
"data-slot": "dt",
|
|
@@ -211,7 +649,7 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
|
|
|
211
649
|
children: [/* @__PURE__ */ jsx(TableHeader, {
|
|
212
650
|
className: cn(headerClassName),
|
|
213
651
|
"data-slot": "dt-header",
|
|
214
|
-
children:
|
|
652
|
+
children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(TableRow, {
|
|
215
653
|
className: cn(headerRowClassName),
|
|
216
654
|
"data-slot": "dt-header-row",
|
|
217
655
|
children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, {
|
|
@@ -228,6 +666,7 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
|
|
|
228
666
|
if (rowEntry) return renderInlineContentRow(rowEntry, colSpan, rows);
|
|
229
667
|
return /* @__PURE__ */ jsx(TableRow, {
|
|
230
668
|
className: cn(resolveClassName(rowClassName, row)),
|
|
669
|
+
style: { transitionProperty: "none" },
|
|
231
670
|
"data-slot": "dt-row",
|
|
232
671
|
"data-state": row.getIsSelected() ? "selected" : void 0,
|
|
233
672
|
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
|
|
@@ -254,7 +693,7 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
|
|
|
254
693
|
//#region src/components/features/data-table/components/inline-content.tsx
|
|
255
694
|
function DataTableInlineContent({ position, rowId, open, onClose, className, children }) {
|
|
256
695
|
const id = useId();
|
|
257
|
-
const { registerInlineContent, unregisterInlineContent } =
|
|
696
|
+
const { registerInlineContent, unregisterInlineContent } = useDataTableInlineContents();
|
|
258
697
|
const initialRender = useRef(true);
|
|
259
698
|
useEffect(() => {
|
|
260
699
|
registerInlineContent({
|
|
@@ -325,57 +764,116 @@ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className
|
|
|
325
764
|
|
|
326
765
|
//#endregion
|
|
327
766
|
//#region src/components/features/data-table/components/pagination.tsx
|
|
767
|
+
/**
|
|
768
|
+
* Generates page numbers with ellipsis for large page counts.
|
|
769
|
+
* Shows up to 7 items: first, last, current +/- 1 neighbor, and ellipsis gaps.
|
|
770
|
+
*/
|
|
771
|
+
function getPageNumbers(currentPage, totalPages) {
|
|
772
|
+
if (totalPages <= 7) return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
773
|
+
const pages = [1];
|
|
774
|
+
const current = currentPage + 1;
|
|
775
|
+
if (current <= 4) {
|
|
776
|
+
for (let i = 2; i <= 5; i++) pages.push(i);
|
|
777
|
+
pages.push("...");
|
|
778
|
+
pages.push(totalPages);
|
|
779
|
+
} else if (current >= totalPages - 3) {
|
|
780
|
+
pages.push("...");
|
|
781
|
+
for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i);
|
|
782
|
+
} else {
|
|
783
|
+
pages.push("...");
|
|
784
|
+
for (let i = current - 1; i <= current + 1; i++) pages.push(i);
|
|
785
|
+
pages.push("...");
|
|
786
|
+
pages.push(totalPages);
|
|
787
|
+
}
|
|
788
|
+
return pages;
|
|
789
|
+
}
|
|
328
790
|
function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
|
|
329
|
-
const {
|
|
791
|
+
const { canNextPage, canPrevPage, nextPage, prevPage, pageIndex, pageCount, setPageIndex, pageSize, setPageSize, totalRows } = useDataTablePagination();
|
|
792
|
+
const isClientMode = pageCount > 0;
|
|
793
|
+
const startRow = pageIndex * pageSize + 1;
|
|
794
|
+
const endRow = Math.min((pageIndex + 1) * pageSize, totalRows);
|
|
795
|
+
const pageNumbers = useMemo(() => getPageNumbers(pageIndex, pageCount), [pageIndex, pageCount]);
|
|
330
796
|
return /* @__PURE__ */ jsxs("div", {
|
|
331
|
-
className: cn("flex items-center justify-between px-2 py-4", className),
|
|
797
|
+
className: cn("flex flex-col-reverse items-center justify-between gap-4 px-2 py-4 sm:flex-row", className),
|
|
332
798
|
"data-slot": "dt-pagination",
|
|
333
799
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
334
|
-
className: "flex items-center gap-
|
|
335
|
-
children: [/* @__PURE__ */
|
|
336
|
-
className: "text-sm text-muted-foreground",
|
|
337
|
-
children:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
800
|
+
className: "flex items-center gap-4",
|
|
801
|
+
children: [isClientMode && totalRows > 0 && /* @__PURE__ */ jsxs("span", {
|
|
802
|
+
className: "text-sm text-muted-foreground whitespace-nowrap",
|
|
803
|
+
children: [
|
|
804
|
+
"Showing",
|
|
805
|
+
" ",
|
|
806
|
+
startRow,
|
|
807
|
+
" ",
|
|
808
|
+
"to",
|
|
809
|
+
" ",
|
|
810
|
+
endRow,
|
|
811
|
+
" ",
|
|
812
|
+
"of",
|
|
813
|
+
" ",
|
|
814
|
+
totalRows,
|
|
815
|
+
" ",
|
|
816
|
+
"rows"
|
|
817
|
+
]
|
|
818
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
819
|
+
className: "flex items-center gap-2",
|
|
820
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
821
|
+
className: "text-sm text-muted-foreground whitespace-nowrap",
|
|
822
|
+
children: "Rows per page"
|
|
823
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
824
|
+
value: String(pageSize),
|
|
825
|
+
onValueChange: (value) => setPageSize(Number(value)),
|
|
826
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
827
|
+
className: "h-8 w-[70px]",
|
|
828
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: String(pageSize) })
|
|
829
|
+
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
830
|
+
side: "top",
|
|
831
|
+
children: pageSizes.map((size) => /* @__PURE__ */ jsx(SelectItem, {
|
|
832
|
+
value: String(size),
|
|
833
|
+
children: size
|
|
834
|
+
}, size))
|
|
835
|
+
})]
|
|
350
836
|
})]
|
|
351
837
|
})]
|
|
352
838
|
}), /* @__PURE__ */ jsxs("div", {
|
|
353
|
-
className: "flex items-center gap-
|
|
839
|
+
className: "flex items-center gap-1",
|
|
354
840
|
children: [
|
|
355
|
-
|
|
356
|
-
|
|
841
|
+
/* @__PURE__ */ jsx(Button, {
|
|
842
|
+
variant: "outline",
|
|
843
|
+
size: "icon",
|
|
844
|
+
className: "size-8",
|
|
845
|
+
onClick: prevPage,
|
|
846
|
+
disabled: !canPrevPage,
|
|
847
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
848
|
+
}),
|
|
849
|
+
isClientMode && pageCount > 1 ? pageNumbers.map((page, index) => {
|
|
850
|
+
if (page === "...") return /* @__PURE__ */ jsx("span", {
|
|
851
|
+
className: "px-2 text-sm text-muted-foreground",
|
|
852
|
+
children: "..."
|
|
853
|
+
}, `ellipsis-${index}`);
|
|
854
|
+
const isActive = page === pageIndex + 1;
|
|
855
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
856
|
+
variant: isActive ? "default" : "outline",
|
|
857
|
+
size: "sm",
|
|
858
|
+
className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
|
|
859
|
+
onClick: () => setPageIndex(page - 1),
|
|
860
|
+
disabled: isActive,
|
|
861
|
+
children: page
|
|
862
|
+
}, page);
|
|
863
|
+
}) : !isClientMode && /* @__PURE__ */ jsxs("span", {
|
|
864
|
+
className: "px-2 text-sm text-muted-foreground",
|
|
357
865
|
children: [
|
|
358
866
|
"Page",
|
|
359
867
|
" ",
|
|
360
|
-
|
|
361
|
-
" ",
|
|
362
|
-
"of",
|
|
363
|
-
" ",
|
|
364
|
-
pagination.pageCount
|
|
868
|
+
pageIndex + 1
|
|
365
869
|
]
|
|
366
870
|
}),
|
|
367
871
|
/* @__PURE__ */ jsx(Button, {
|
|
368
872
|
variant: "outline",
|
|
369
|
-
size: "
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}),
|
|
374
|
-
/* @__PURE__ */ jsx(Button, {
|
|
375
|
-
variant: "outline",
|
|
376
|
-
size: "sm",
|
|
377
|
-
onClick: pagination.nextPage,
|
|
378
|
-
disabled: !pagination.canNextPage,
|
|
873
|
+
size: "icon",
|
|
874
|
+
className: "size-8",
|
|
875
|
+
onClick: nextPage,
|
|
876
|
+
disabled: !canNextPage,
|
|
379
877
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
380
878
|
})
|
|
381
879
|
]
|
|
@@ -421,7 +919,7 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
|
|
|
421
919
|
//#endregion
|
|
422
920
|
//#region src/components/features/data-table/components/search.tsx
|
|
423
921
|
function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
|
|
424
|
-
const { search, setSearch } =
|
|
922
|
+
const { search, setSearch } = useDataTableSearch();
|
|
425
923
|
const [inputValue, setInputValue] = useState(search);
|
|
426
924
|
useEffect(() => {
|
|
427
925
|
setInputValue(search);
|
|
@@ -446,225 +944,32 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
|
|
|
446
944
|
});
|
|
447
945
|
}
|
|
448
946
|
|
|
449
|
-
//#endregion
|
|
450
|
-
//#region src/components/features/data-table/hooks/use-inline-contents.ts
|
|
451
|
-
function useInlineContents() {
|
|
452
|
-
const [inlineContents, setInlineContents] = useState([]);
|
|
453
|
-
return {
|
|
454
|
-
inlineContents,
|
|
455
|
-
registerInlineContent: useCallback((entry) => {
|
|
456
|
-
setInlineContents((prev) => {
|
|
457
|
-
const index = prev.findIndex((e) => e.id === entry.id);
|
|
458
|
-
if (index >= 0) {
|
|
459
|
-
const next = [...prev];
|
|
460
|
-
next[index] = entry;
|
|
461
|
-
return next;
|
|
462
|
-
}
|
|
463
|
-
return [...prev, entry];
|
|
464
|
-
});
|
|
465
|
-
}, []),
|
|
466
|
-
unregisterInlineContent: useCallback((id) => {
|
|
467
|
-
setInlineContents((prev) => prev.filter((e) => e.id !== id));
|
|
468
|
-
}, [])
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
947
|
//#endregion
|
|
473
948
|
//#region src/components/features/data-table/core/client-provider.tsx
|
|
474
|
-
function ClientProvider({
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (Array.isArray(filterValue)) {
|
|
484
|
-
if (Array.isArray(cellValue)) return cellValue.some((v) => filterValue.includes(v));
|
|
485
|
-
return filterValue.includes(cellValue);
|
|
486
|
-
}
|
|
487
|
-
if (typeof filterValue === "string" && typeof cellValue === "string") return cellValue.toLowerCase().includes(filterValue.toLowerCase());
|
|
488
|
-
return cellValue === filterValue;
|
|
489
|
-
}, []);
|
|
490
|
-
const globalFilterFn = useMemo(() => {
|
|
491
|
-
if (searchFn) return (row, _columnId, filterValue) => searchFn(row.original, String(filterValue));
|
|
492
|
-
if (searchableColumns && searchableColumns.length > 0) return (row, _columnId, filterValue) => {
|
|
493
|
-
const query = String(filterValue).toLowerCase();
|
|
494
|
-
return searchableColumns.some((col) => {
|
|
495
|
-
const cellValue = row.getValue(col);
|
|
496
|
-
return cellValue != null && String(cellValue).toLowerCase().includes(query);
|
|
497
|
-
});
|
|
498
|
-
};
|
|
499
|
-
}, [searchFn, searchableColumns]);
|
|
500
|
-
const table = useReactTable({
|
|
501
|
-
data,
|
|
502
|
-
columns,
|
|
503
|
-
state: {
|
|
504
|
-
sorting,
|
|
505
|
-
rowSelection,
|
|
506
|
-
globalFilter: search,
|
|
507
|
-
columnFilters,
|
|
508
|
-
pagination: {
|
|
509
|
-
pageIndex: pagination.pageIndex,
|
|
510
|
-
pageSize: pagination.pageSize
|
|
511
|
-
}
|
|
512
|
-
},
|
|
513
|
-
filterFns: { arrayIncludes: arrayIncludesFilter },
|
|
514
|
-
defaultColumn: { filterFn: arrayIncludesFilter },
|
|
515
|
-
...globalFilterFn ? { globalFilterFn } : {},
|
|
516
|
-
onSortingChange: (updater) => {
|
|
517
|
-
setSorting(typeof updater === "function" ? updater(sorting) : updater);
|
|
518
|
-
},
|
|
519
|
-
onRowSelectionChange: (updater) => {
|
|
520
|
-
setRowSelection(typeof updater === "function" ? updater(rowSelection) : updater);
|
|
521
|
-
},
|
|
522
|
-
onPaginationChange: (updater) => {
|
|
523
|
-
const next = typeof updater === "function" ? updater({
|
|
524
|
-
pageIndex: pagination.pageIndex,
|
|
525
|
-
pageSize: pagination.pageSize
|
|
526
|
-
}) : updater;
|
|
527
|
-
pagination.setPageIndex(next.pageIndex);
|
|
528
|
-
pagination.setPageSize(next.pageSize);
|
|
529
|
-
},
|
|
530
|
-
getCoreRowModel: getCoreRowModel(),
|
|
531
|
-
getSortedRowModel: getSortedRowModel(),
|
|
532
|
-
getFilteredRowModel: getFilteredRowModel(),
|
|
533
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
534
|
-
getRowId,
|
|
535
|
-
enableRowSelection: !!enableRowSelection
|
|
536
|
-
});
|
|
537
|
-
const { inlineContents, registerInlineContent, unregisterInlineContent } = useInlineContents();
|
|
538
|
-
const paginationState = useMemo(() => ({
|
|
539
|
-
canNextPage: table.getCanNextPage(),
|
|
540
|
-
canPrevPage: table.getCanPreviousPage(),
|
|
541
|
-
nextPage: () => table.nextPage(),
|
|
542
|
-
prevPage: () => table.previousPage(),
|
|
543
|
-
pageIndex: pagination.pageIndex,
|
|
544
|
-
pageCount: table.getPageCount(),
|
|
545
|
-
setPageIndex: pagination.setPageIndex,
|
|
546
|
-
pageSize: pagination.pageSize,
|
|
547
|
-
setPageSize: pagination.setPageSize
|
|
548
|
-
}), [table, pagination]);
|
|
549
|
-
return /* @__PURE__ */ jsx(DataTableContext, {
|
|
550
|
-
value: useMemo(() => ({
|
|
551
|
-
table,
|
|
552
|
-
mode: "client",
|
|
553
|
-
sorting,
|
|
554
|
-
filters,
|
|
555
|
-
search,
|
|
556
|
-
rowSelection,
|
|
557
|
-
isLoading: false,
|
|
558
|
-
setSorting,
|
|
559
|
-
setFilter,
|
|
560
|
-
clearFilter,
|
|
561
|
-
clearAllFilters,
|
|
562
|
-
setSearch,
|
|
563
|
-
clearSearch,
|
|
564
|
-
setRowSelection,
|
|
565
|
-
pagination: paginationState,
|
|
566
|
-
inlineContents,
|
|
567
|
-
registerInlineContent,
|
|
568
|
-
unregisterInlineContent
|
|
569
|
-
}), [
|
|
570
|
-
table,
|
|
571
|
-
sorting,
|
|
572
|
-
filters,
|
|
573
|
-
search,
|
|
574
|
-
rowSelection,
|
|
575
|
-
setSorting,
|
|
576
|
-
setFilter,
|
|
577
|
-
clearFilter,
|
|
578
|
-
clearAllFilters,
|
|
579
|
-
setSearch,
|
|
580
|
-
clearSearch,
|
|
581
|
-
setRowSelection,
|
|
582
|
-
paginationState,
|
|
583
|
-
inlineContents,
|
|
584
|
-
registerInlineContent,
|
|
585
|
-
unregisterInlineContent
|
|
586
|
-
]),
|
|
587
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
588
|
-
className,
|
|
589
|
-
children
|
|
949
|
+
function ClientProvider({ store, table, className, children }) {
|
|
950
|
+
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
951
|
+
value: store,
|
|
952
|
+
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
953
|
+
value: table,
|
|
954
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
955
|
+
className,
|
|
956
|
+
children
|
|
957
|
+
})
|
|
590
958
|
})
|
|
591
959
|
});
|
|
592
960
|
}
|
|
593
961
|
|
|
594
962
|
//#endregion
|
|
595
963
|
//#region src/components/features/data-table/core/server-provider.tsx
|
|
596
|
-
function ServerProvider({
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
setSorting(typeof updater === "function" ? updater(sorting) : updater);
|
|
606
|
-
},
|
|
607
|
-
onRowSelectionChange: (updater) => {
|
|
608
|
-
setRowSelection(typeof updater === "function" ? updater(rowSelection) : updater);
|
|
609
|
-
},
|
|
610
|
-
getCoreRowModel: getCoreRowModel(),
|
|
611
|
-
getRowId,
|
|
612
|
-
enableRowSelection: !!enableRowSelection,
|
|
613
|
-
manualPagination: true,
|
|
614
|
-
manualSorting: true,
|
|
615
|
-
manualFiltering: true
|
|
616
|
-
});
|
|
617
|
-
const { inlineContents, registerInlineContent, unregisterInlineContent } = useInlineContents();
|
|
618
|
-
const paginationState = useMemo(() => ({
|
|
619
|
-
canNextPage: pagination.hasNextPage,
|
|
620
|
-
canPrevPage: pagination.hasPrevPage,
|
|
621
|
-
nextPage: pagination.nextPage,
|
|
622
|
-
prevPage: pagination.prevPage,
|
|
623
|
-
pageSize: pagination.limit,
|
|
624
|
-
setPageSize: pagination.setLimit
|
|
625
|
-
}), [pagination]);
|
|
626
|
-
return /* @__PURE__ */ jsx(DataTableContext, {
|
|
627
|
-
value: useMemo(() => ({
|
|
628
|
-
table,
|
|
629
|
-
mode: "server",
|
|
630
|
-
sorting,
|
|
631
|
-
filters,
|
|
632
|
-
search,
|
|
633
|
-
rowSelection,
|
|
634
|
-
isLoading,
|
|
635
|
-
setSorting,
|
|
636
|
-
setFilter,
|
|
637
|
-
clearFilter,
|
|
638
|
-
clearAllFilters,
|
|
639
|
-
setSearch,
|
|
640
|
-
clearSearch,
|
|
641
|
-
setRowSelection,
|
|
642
|
-
pagination: paginationState,
|
|
643
|
-
inlineContents,
|
|
644
|
-
registerInlineContent,
|
|
645
|
-
unregisterInlineContent
|
|
646
|
-
}), [
|
|
647
|
-
table,
|
|
648
|
-
sorting,
|
|
649
|
-
filters,
|
|
650
|
-
search,
|
|
651
|
-
rowSelection,
|
|
652
|
-
isLoading,
|
|
653
|
-
setSorting,
|
|
654
|
-
setFilter,
|
|
655
|
-
clearFilter,
|
|
656
|
-
clearAllFilters,
|
|
657
|
-
setSearch,
|
|
658
|
-
clearSearch,
|
|
659
|
-
setRowSelection,
|
|
660
|
-
paginationState,
|
|
661
|
-
inlineContents,
|
|
662
|
-
registerInlineContent,
|
|
663
|
-
unregisterInlineContent
|
|
664
|
-
]),
|
|
665
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
666
|
-
className,
|
|
667
|
-
children
|
|
964
|
+
function ServerProvider({ store, table, className, children }) {
|
|
965
|
+
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
966
|
+
value: store,
|
|
967
|
+
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
968
|
+
value: table,
|
|
969
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
970
|
+
className,
|
|
971
|
+
children
|
|
972
|
+
})
|
|
668
973
|
})
|
|
669
974
|
});
|
|
670
975
|
}
|
|
@@ -696,8 +1001,16 @@ function Badge({ className, variant, ...props }) {
|
|
|
696
1001
|
//#region src/components/features/data-table/filters/checkbox-filter.tsx
|
|
697
1002
|
const MAX_VISIBLE_BADGES = 2;
|
|
698
1003
|
function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName }) {
|
|
699
|
-
const { filters, setFilter, clearFilter } =
|
|
1004
|
+
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
700
1005
|
const [open, setOpen] = useState(false);
|
|
1006
|
+
useEffect(() => {
|
|
1007
|
+
registerFilter(column, "checkbox");
|
|
1008
|
+
return () => unregisterFilter(column);
|
|
1009
|
+
}, [
|
|
1010
|
+
column,
|
|
1011
|
+
registerFilter,
|
|
1012
|
+
unregisterFilter
|
|
1013
|
+
]);
|
|
701
1014
|
const selectedValues = filters[column] ?? [];
|
|
702
1015
|
const updateValues = (newValues) => {
|
|
703
1016
|
if (newValues.length > 0) setFilter(column, newValues);
|
|
@@ -797,8 +1110,16 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
|
|
|
797
1110
|
//#endregion
|
|
798
1111
|
//#region src/components/features/data-table/filters/date-picker-filter.tsx
|
|
799
1112
|
function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
|
|
800
|
-
const { filters, setFilter, clearFilter } =
|
|
1113
|
+
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
801
1114
|
const rawValue = filters[column];
|
|
1115
|
+
useEffect(() => {
|
|
1116
|
+
registerFilter(column, "date-gte");
|
|
1117
|
+
return () => unregisterFilter(column);
|
|
1118
|
+
}, [
|
|
1119
|
+
column,
|
|
1120
|
+
registerFilter,
|
|
1121
|
+
unregisterFilter
|
|
1122
|
+
]);
|
|
802
1123
|
const dateRange = useMemo(() => {
|
|
803
1124
|
const date = rawValue ? new Date(rawValue) : void 0;
|
|
804
1125
|
return {
|
|
@@ -831,9 +1152,17 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
|
|
|
831
1152
|
//#endregion
|
|
832
1153
|
//#region src/components/features/data-table/filters/select-filter.tsx
|
|
833
1154
|
function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
|
|
834
|
-
const { filters, setFilter, clearFilter } =
|
|
1155
|
+
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
835
1156
|
const [open, setOpen] = useState(false);
|
|
836
1157
|
const value = filters[column];
|
|
1158
|
+
useEffect(() => {
|
|
1159
|
+
registerFilter(column, "select");
|
|
1160
|
+
return () => unregisterFilter(column);
|
|
1161
|
+
}, [
|
|
1162
|
+
column,
|
|
1163
|
+
registerFilter,
|
|
1164
|
+
unregisterFilter
|
|
1165
|
+
]);
|
|
837
1166
|
const selectedOption = options.find((o) => o.value === value);
|
|
838
1167
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
839
1168
|
open,
|
|
@@ -908,255 +1237,241 @@ const DataTable = {
|
|
|
908
1237
|
//#endregion
|
|
909
1238
|
//#region src/components/features/data-table/hooks/use-data-table-client.ts
|
|
910
1239
|
function useDataTableClient(options) {
|
|
911
|
-
const { data, columns, pageSize
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
}));
|
|
924
|
-
}, []);
|
|
925
|
-
const clearFilter = useCallback((key) => {
|
|
926
|
-
setFilters((prev) => {
|
|
927
|
-
const { [key]: _, ...rest } = prev;
|
|
928
|
-
return rest;
|
|
929
|
-
});
|
|
930
|
-
}, []);
|
|
931
|
-
const clearAllFilters = useCallback(() => {
|
|
932
|
-
setFilters({});
|
|
933
|
-
}, []);
|
|
934
|
-
const clearSearch = useCallback(() => {
|
|
935
|
-
setSearch("");
|
|
936
|
-
}, []);
|
|
1240
|
+
const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
|
|
1241
|
+
const store = useMemo(() => createDataTableStore({
|
|
1242
|
+
data,
|
|
1243
|
+
mode: "client",
|
|
1244
|
+
defaultSort,
|
|
1245
|
+
defaultFilters,
|
|
1246
|
+
pageSize,
|
|
1247
|
+
searchableColumns,
|
|
1248
|
+
searchFn,
|
|
1249
|
+
filterFns
|
|
1250
|
+
}), []);
|
|
1251
|
+
const isInitialRender = useRef(true);
|
|
937
1252
|
useEffect(() => {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1253
|
+
if (isInitialRender.current) {
|
|
1254
|
+
isInitialRender.current = false;
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
store.setData(data);
|
|
1258
|
+
}, [data, store]);
|
|
1259
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1260
|
+
const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
1261
|
+
const table = useReactTable({
|
|
1262
|
+
data: filteredData,
|
|
1263
|
+
columns: resolvedColumns,
|
|
1264
|
+
state: {
|
|
1265
|
+
sorting,
|
|
1266
|
+
rowSelection,
|
|
1267
|
+
pagination: {
|
|
1268
|
+
pageIndex,
|
|
1269
|
+
pageSize: storePageSize
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
onSortingChange: (updater) => {
|
|
1273
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1274
|
+
store.setSorting(next);
|
|
1275
|
+
},
|
|
1276
|
+
onRowSelectionChange: (updater) => {
|
|
1277
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1278
|
+
store.setRowSelection(next);
|
|
1279
|
+
},
|
|
1280
|
+
onPaginationChange: (updater) => {
|
|
1281
|
+
const next = typeof updater === "function" ? updater({
|
|
1282
|
+
pageIndex,
|
|
1283
|
+
pageSize: storePageSize
|
|
1284
|
+
}) : updater;
|
|
1285
|
+
store.setPageIndex(next.pageIndex);
|
|
1286
|
+
store.setPageSize(next.pageSize);
|
|
1287
|
+
},
|
|
1288
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1289
|
+
getSortedRowModel: getSortedRowModel(),
|
|
1290
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
1291
|
+
getRowId,
|
|
1292
|
+
enableRowSelection: !!enableRowSelection
|
|
1293
|
+
});
|
|
1294
|
+
const hydratedRef = useRef(false);
|
|
1295
|
+
useEffect(() => {
|
|
1296
|
+
if (stateAdapter && !hydratedRef.current) {
|
|
1297
|
+
hydratedRef.current = true;
|
|
1298
|
+
const persisted = stateAdapter.read();
|
|
1299
|
+
if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
|
|
1300
|
+
if (persisted.filters) {
|
|
1301
|
+
for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
|
|
1302
|
+
}
|
|
1303
|
+
if (persisted.search) store.setSearch(persisted.search);
|
|
1304
|
+
if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
|
|
1305
|
+
if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
|
|
1306
|
+
}
|
|
1307
|
+
}, []);
|
|
1308
|
+
const isFirstWrite = useRef(true);
|
|
946
1309
|
useEffect(() => {
|
|
947
|
-
stateAdapter
|
|
1310
|
+
if (!stateAdapter) return;
|
|
1311
|
+
if (isFirstWrite.current) {
|
|
1312
|
+
isFirstWrite.current = false;
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
stateAdapter.write({
|
|
948
1316
|
sorting,
|
|
949
1317
|
filters,
|
|
950
1318
|
search,
|
|
951
1319
|
pageIndex,
|
|
952
|
-
pageSize
|
|
1320
|
+
pageSize: storePageSize
|
|
953
1321
|
});
|
|
954
1322
|
}, [
|
|
955
|
-
stateAdapter,
|
|
956
1323
|
sorting,
|
|
957
1324
|
filters,
|
|
958
1325
|
search,
|
|
959
1326
|
pageIndex,
|
|
960
|
-
|
|
1327
|
+
storePageSize,
|
|
1328
|
+
stateAdapter
|
|
961
1329
|
]);
|
|
962
|
-
const pagination = useMemo(() => ({
|
|
963
|
-
pageIndex,
|
|
964
|
-
pageSize,
|
|
965
|
-
setPageIndex,
|
|
966
|
-
setPageSize
|
|
967
|
-
}), [pageIndex, pageSize]);
|
|
968
|
-
const selectionEnabled = !!enableRowSelection;
|
|
969
|
-
const selectionOptions = typeof enableRowSelection === "object" ? enableRowSelection : void 0;
|
|
970
1330
|
return {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
selectionEnabled,
|
|
974
|
-
columns,
|
|
975
|
-
selectionOptions
|
|
976
|
-
]),
|
|
977
|
-
sorting,
|
|
978
|
-
setSorting,
|
|
979
|
-
filters,
|
|
980
|
-
setFilter,
|
|
981
|
-
clearFilter,
|
|
982
|
-
clearAllFilters,
|
|
983
|
-
search,
|
|
984
|
-
setSearch,
|
|
985
|
-
clearSearch,
|
|
986
|
-
rowSelection,
|
|
987
|
-
setRowSelection,
|
|
988
|
-
pagination,
|
|
989
|
-
getRowId,
|
|
990
|
-
enableRowSelection: selectionEnabled,
|
|
991
|
-
searchableColumns,
|
|
992
|
-
searchFn
|
|
1331
|
+
store,
|
|
1332
|
+
table
|
|
993
1333
|
};
|
|
994
1334
|
}
|
|
995
1335
|
|
|
996
1336
|
//#endregion
|
|
997
1337
|
//#region src/components/features/data-table/hooks/use-data-table-server.ts
|
|
998
1338
|
function useDataTableServer(options) {
|
|
999
|
-
const { columns, fetchFn, transform, limit
|
|
1000
|
-
const
|
|
1001
|
-
const [data, setData] = useState([]);
|
|
1002
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
1003
|
-
const [sorting, setSorting] = useState(adapterState.sorting ?? defaultSort);
|
|
1004
|
-
const [filters, setFilters] = useState(adapterState.filters ?? defaultFilters);
|
|
1005
|
-
const [search, setSearch] = useState(adapterState.search ?? "");
|
|
1006
|
-
const [rowSelection, setRowSelection] = useState({});
|
|
1007
|
-
const [limit, setLimit] = useState(adapterState.limit ?? initialLimit);
|
|
1008
|
-
const [cursor, setCursor] = useState(void 0);
|
|
1009
|
-
const [nextCursor, setNextCursor] = useState(void 0);
|
|
1010
|
-
const [hasNextPage, setHasNextPage] = useState(false);
|
|
1011
|
-
const [cursorHistory, setCursorHistory] = useState([]);
|
|
1012
|
-
const fetchFnRef = useRef(fetchFn);
|
|
1013
|
-
fetchFnRef.current = fetchFn;
|
|
1339
|
+
const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection = false, defaultSort, defaultFilters, stateAdapter } = options;
|
|
1340
|
+
const fetchRef = useRef(fetchFn);
|
|
1014
1341
|
const transformRef = useRef(transform);
|
|
1015
|
-
transformRef.current = transform;
|
|
1016
|
-
const setFilter = useCallback((key, value) => {
|
|
1017
|
-
setFilters((prev) => ({
|
|
1018
|
-
...prev,
|
|
1019
|
-
[key]: value
|
|
1020
|
-
}));
|
|
1021
|
-
}, []);
|
|
1022
|
-
const clearFilter = useCallback((key) => {
|
|
1023
|
-
setFilters((prev) => {
|
|
1024
|
-
const { [key]: _, ...rest } = prev;
|
|
1025
|
-
return rest;
|
|
1026
|
-
});
|
|
1027
|
-
}, []);
|
|
1028
|
-
const clearAllFilters = useCallback(() => {
|
|
1029
|
-
setFilters({});
|
|
1030
|
-
}, []);
|
|
1031
|
-
const clearSearch = useCallback(() => {
|
|
1032
|
-
setSearch("");
|
|
1033
|
-
}, []);
|
|
1034
|
-
const resetPagination = useCallback(() => {
|
|
1035
|
-
setCursor(void 0);
|
|
1036
|
-
setNextCursor(void 0);
|
|
1037
|
-
setCursorHistory([]);
|
|
1038
|
-
setHasNextPage(false);
|
|
1039
|
-
setRowSelection({});
|
|
1040
|
-
}, []);
|
|
1041
1342
|
useEffect(() => {
|
|
1042
|
-
|
|
1043
|
-
}, [
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1343
|
+
fetchRef.current = fetchFn;
|
|
1344
|
+
}, [fetchFn]);
|
|
1345
|
+
useEffect(() => {
|
|
1346
|
+
transformRef.current = transform;
|
|
1347
|
+
}, [transform]);
|
|
1348
|
+
const cursorMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1349
|
+
const [hasNextPage, setHasNextPage] = useState(false);
|
|
1350
|
+
const store = useMemo(() => createDataTableStore({
|
|
1351
|
+
data: [],
|
|
1352
|
+
mode: "server",
|
|
1353
|
+
defaultSort,
|
|
1354
|
+
defaultFilters,
|
|
1355
|
+
pageSize: limit
|
|
1356
|
+
}), []);
|
|
1357
|
+
const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
1050
1358
|
useEffect(() => {
|
|
1051
1359
|
let cancelled = false;
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
limit,
|
|
1360
|
+
store.setLoading(true);
|
|
1361
|
+
const cursor = cursorMapRef.current.get(pageIndex);
|
|
1362
|
+
fetchRef.current({
|
|
1056
1363
|
sorting,
|
|
1057
1364
|
filters,
|
|
1058
|
-
search
|
|
1365
|
+
search,
|
|
1366
|
+
cursor,
|
|
1367
|
+
limit: pageSize
|
|
1059
1368
|
}).then((response) => {
|
|
1060
1369
|
if (cancelled) return;
|
|
1061
1370
|
const result = transformRef.current(response);
|
|
1062
|
-
|
|
1063
|
-
|
|
1371
|
+
store.setServerData(result.data);
|
|
1372
|
+
store.setError(null);
|
|
1373
|
+
if (result.nextCursor) cursorMapRef.current.set(pageIndex + 1, result.nextCursor);
|
|
1064
1374
|
setHasNextPage(result.hasNextPage);
|
|
1065
|
-
}).catch(() => {
|
|
1375
|
+
}).catch((error) => {
|
|
1066
1376
|
if (cancelled) return;
|
|
1067
|
-
|
|
1068
|
-
|
|
1377
|
+
store.setServerData([]);
|
|
1378
|
+
store.setError(error instanceof Error ? error : new Error(String(error)));
|
|
1069
1379
|
setHasNextPage(false);
|
|
1070
1380
|
}).finally(() => {
|
|
1071
|
-
if (!cancelled)
|
|
1381
|
+
if (!cancelled) store.setLoading(false);
|
|
1072
1382
|
});
|
|
1073
1383
|
return () => {
|
|
1074
1384
|
cancelled = true;
|
|
1075
1385
|
};
|
|
1076
1386
|
}, [
|
|
1077
|
-
cursor,
|
|
1078
|
-
limit,
|
|
1079
1387
|
sorting,
|
|
1080
1388
|
filters,
|
|
1081
|
-
search
|
|
1389
|
+
search,
|
|
1390
|
+
pageSize,
|
|
1391
|
+
pageIndex,
|
|
1392
|
+
store
|
|
1082
1393
|
]);
|
|
1394
|
+
const prevQueryRef = useRef({
|
|
1395
|
+
sorting,
|
|
1396
|
+
filters,
|
|
1397
|
+
search,
|
|
1398
|
+
pageSize
|
|
1399
|
+
});
|
|
1083
1400
|
useEffect(() => {
|
|
1084
|
-
|
|
1401
|
+
const prev = prevQueryRef.current;
|
|
1402
|
+
if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
|
|
1403
|
+
cursorMapRef.current = /* @__PURE__ */ new Map();
|
|
1404
|
+
store.setPageIndex(0);
|
|
1405
|
+
setHasNextPage(false);
|
|
1406
|
+
}
|
|
1407
|
+
prevQueryRef.current = {
|
|
1085
1408
|
sorting,
|
|
1086
1409
|
filters,
|
|
1087
1410
|
search,
|
|
1088
|
-
|
|
1089
|
-
}
|
|
1411
|
+
pageSize
|
|
1412
|
+
};
|
|
1090
1413
|
}, [
|
|
1091
|
-
stateAdapter,
|
|
1092
1414
|
sorting,
|
|
1093
1415
|
filters,
|
|
1094
1416
|
search,
|
|
1095
|
-
|
|
1417
|
+
pageSize,
|
|
1418
|
+
store
|
|
1096
1419
|
]);
|
|
1097
|
-
const
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1420
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1421
|
+
const table = useReactTable({
|
|
1422
|
+
data: store.getSnapshot().data,
|
|
1423
|
+
columns: resolvedColumns,
|
|
1424
|
+
state: {
|
|
1425
|
+
sorting,
|
|
1426
|
+
rowSelection,
|
|
1427
|
+
pagination: {
|
|
1428
|
+
pageIndex,
|
|
1429
|
+
pageSize
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
manualPagination: true,
|
|
1433
|
+
manualSorting: true,
|
|
1434
|
+
manualFiltering: true,
|
|
1435
|
+
pageCount: hasNextPage ? pageIndex + 2 : pageIndex + 1,
|
|
1436
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1437
|
+
getRowId,
|
|
1438
|
+
enableRowSelection: !!enableRowSelection,
|
|
1439
|
+
onSortingChange: (updater) => {
|
|
1440
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1441
|
+
store.setSorting(next);
|
|
1442
|
+
},
|
|
1443
|
+
onRowSelectionChange: (updater) => {
|
|
1444
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1445
|
+
store.setRowSelection(next);
|
|
1446
|
+
},
|
|
1447
|
+
onPaginationChange: (updater) => {
|
|
1448
|
+
const next = typeof updater === "function" ? updater({
|
|
1449
|
+
pageIndex,
|
|
1450
|
+
pageSize
|
|
1451
|
+
}) : updater;
|
|
1452
|
+
if (next.pageIndex !== pageIndex) store.setPageIndex(next.pageIndex);
|
|
1453
|
+
if (next.pageSize !== pageSize) store.setPageSize(next.pageSize);
|
|
1103
1454
|
}
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
const newHistory = [...prev];
|
|
1112
|
-
const previousCursor = newHistory.pop();
|
|
1113
|
-
setCursor(previousCursor === "" ? void 0 : previousCursor);
|
|
1114
|
-
return newHistory;
|
|
1455
|
+
});
|
|
1456
|
+
useEffect(() => {
|
|
1457
|
+
if (stateAdapter) stateAdapter.write({
|
|
1458
|
+
sorting,
|
|
1459
|
+
filters,
|
|
1460
|
+
search,
|
|
1461
|
+
pageSize
|
|
1115
1462
|
});
|
|
1116
|
-
|
|
1117
|
-
}, []);
|
|
1118
|
-
const pagination = useMemo(() => ({
|
|
1119
|
-
cursor,
|
|
1120
|
-
limit,
|
|
1121
|
-
hasNextPage,
|
|
1122
|
-
hasPrevPage,
|
|
1123
|
-
nextPage,
|
|
1124
|
-
prevPage,
|
|
1125
|
-
setLimit
|
|
1126
|
-
}), [
|
|
1127
|
-
cursor,
|
|
1128
|
-
limit,
|
|
1129
|
-
hasNextPage,
|
|
1130
|
-
hasPrevPage,
|
|
1131
|
-
nextPage,
|
|
1132
|
-
prevPage
|
|
1133
|
-
]);
|
|
1134
|
-
const selectionEnabled = !!enableRowSelection;
|
|
1135
|
-
const selectionOptions = typeof enableRowSelection === "object" ? enableRowSelection : void 0;
|
|
1136
|
-
return {
|
|
1137
|
-
data,
|
|
1138
|
-
columns: useMemo(() => selectionEnabled ? withSelectionColumn(columns, selectionOptions) : columns, [
|
|
1139
|
-
selectionEnabled,
|
|
1140
|
-
columns,
|
|
1141
|
-
selectionOptions
|
|
1142
|
-
]),
|
|
1143
|
-
isLoading,
|
|
1463
|
+
}, [
|
|
1144
1464
|
sorting,
|
|
1145
|
-
setSorting,
|
|
1146
1465
|
filters,
|
|
1147
|
-
setFilter,
|
|
1148
|
-
clearFilter,
|
|
1149
|
-
clearAllFilters,
|
|
1150
1466
|
search,
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
enableRowSelection: selectionEnabled
|
|
1467
|
+
pageSize,
|
|
1468
|
+
stateAdapter
|
|
1469
|
+
]);
|
|
1470
|
+
return {
|
|
1471
|
+
store,
|
|
1472
|
+
table
|
|
1158
1473
|
};
|
|
1159
1474
|
}
|
|
1160
1475
|
|
|
1161
1476
|
//#endregion
|
|
1162
|
-
export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, createSelectionColumn, useDataTableClient, useDataTableContext, useDataTableServer, useNuqsAdapter };
|
|
1477
|
+
export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, createDataTableStore, createSelectionColumn, useDataTableClient, useDataTableContext, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableServer, useDataTableSorting, useNuqsAdapter };
|