@tint-ui/data-table 0.3.5

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 (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/adapters/boolean.d.ts +10 -0
  4. package/adapters/boolean.js +38 -0
  5. package/adapters/index.d.ts +6 -0
  6. package/adapters/index.js +18 -0
  7. package/adapters/number-format.d.ts +1 -0
  8. package/adapters/number-format.js +42 -0
  9. package/adapters/number.d.ts +12 -0
  10. package/adapters/number.js +51 -0
  11. package/adapters/text.d.ts +4 -0
  12. package/adapters/text.js +9 -0
  13. package/cell-adapter-type.d.ts +43 -0
  14. package/cell-adapter-type.js +306 -0
  15. package/cjs/adapters/boolean.js +43 -0
  16. package/cjs/adapters/index.js +21 -0
  17. package/cjs/adapters/number-format.js +46 -0
  18. package/cjs/adapters/number.js +56 -0
  19. package/cjs/adapters/text.js +13 -0
  20. package/cjs/cell-adapter-type.js +312 -0
  21. package/cjs/classes.js +27 -0
  22. package/cjs/context.js +14 -0
  23. package/cjs/data-table-content.js +105 -0
  24. package/cjs/data-table-pagination.js +38 -0
  25. package/cjs/data-table-text-filter.js +83 -0
  26. package/cjs/data-table-toolbar.js +103 -0
  27. package/cjs/data-table-views-options.js +137 -0
  28. package/cjs/data-table.js +63 -0
  29. package/cjs/filter-adapter-type.js +162 -0
  30. package/cjs/filter-adapters/index.js +10 -0
  31. package/cjs/filter-adapters/option.js +152 -0
  32. package/cjs/filter-adapters/use-option-filter.js +195 -0
  33. package/cjs/filter-classes.js +26 -0
  34. package/cjs/filter-fn.js +84 -0
  35. package/cjs/index.js +99 -0
  36. package/cjs/package.json +3 -0
  37. package/cjs/pagination-arrow.js +93 -0
  38. package/cjs/pagination-classes.js +20 -0
  39. package/cjs/pagination-number.js +66 -0
  40. package/cjs/pagination-size-options.js +48 -0
  41. package/cjs/row-button-menu.js +49 -0
  42. package/cjs/row-popover-menu.js +52 -0
  43. package/cjs/toolbar-classes.js +24 -0
  44. package/cjs/types.js +3 -0
  45. package/cjs/use-data-table.js +768 -0
  46. package/cjs/use-lexicon.js +155 -0
  47. package/cjs/use-row-menu.js +60 -0
  48. package/cjs/use-visibility-column.js +105 -0
  49. package/cjs/use-visibility-filter.js +149 -0
  50. package/cjs/utils.js +136 -0
  51. package/classes.d.ts +34 -0
  52. package/classes.js +23 -0
  53. package/context.d.ts +5 -0
  54. package/context.js +9 -0
  55. package/data-table-content.d.ts +9 -0
  56. package/data-table-content.js +98 -0
  57. package/data-table-pagination.d.ts +5 -0
  58. package/data-table-pagination.js +31 -0
  59. package/data-table-text-filter.d.ts +7 -0
  60. package/data-table-text-filter.js +76 -0
  61. package/data-table-toolbar.d.ts +5 -0
  62. package/data-table-toolbar.js +95 -0
  63. package/data-table-views-options.d.ts +2 -0
  64. package/data-table-views-options.js +133 -0
  65. package/data-table.d.ts +18 -0
  66. package/data-table.js +56 -0
  67. package/filter-adapter-type.d.ts +9 -0
  68. package/filter-adapter-type.js +155 -0
  69. package/filter-adapters/index.d.ts +5 -0
  70. package/filter-adapters/index.js +7 -0
  71. package/filter-adapters/option.d.ts +3 -0
  72. package/filter-adapters/option.js +147 -0
  73. package/filter-adapters/use-option-filter.d.ts +27 -0
  74. package/filter-adapters/use-option-filter.js +192 -0
  75. package/filter-classes.d.ts +32 -0
  76. package/filter-classes.js +22 -0
  77. package/filter-fn.d.ts +7 -0
  78. package/filter-fn.js +76 -0
  79. package/index.d.ts +16 -0
  80. package/index.js +16 -0
  81. package/package.json +97 -0
  82. package/pagination-arrow.d.ts +5 -0
  83. package/pagination-arrow.js +86 -0
  84. package/pagination-classes.d.ts +20 -0
  85. package/pagination-classes.js +16 -0
  86. package/pagination-number.d.ts +5 -0
  87. package/pagination-number.js +59 -0
  88. package/pagination-size-options.d.ts +3 -0
  89. package/pagination-size-options.js +44 -0
  90. package/row-button-menu.d.ts +5 -0
  91. package/row-button-menu.js +45 -0
  92. package/row-popover-menu.d.ts +5 -0
  93. package/row-popover-menu.js +48 -0
  94. package/styles-filter.css +1 -0
  95. package/styles-filter.module.css +64 -0
  96. package/styles-filter.module.scss +65 -0
  97. package/styles-pagination.css +1 -0
  98. package/styles-pagination.module.css +28 -0
  99. package/styles-pagination.module.scss +31 -0
  100. package/styles-toolbar.css +1 -0
  101. package/styles-toolbar.module.css +70 -0
  102. package/styles-toolbar.module.scss +67 -0
  103. package/styles.css +1 -0
  104. package/styles.json +8 -0
  105. package/styles.module.css +39 -0
  106. package/styles.module.scss +38 -0
  107. package/toolbar-classes.d.ts +26 -0
  108. package/toolbar-classes.js +20 -0
  109. package/types.d.ts +226 -0
  110. package/types.js +2 -0
  111. package/use-data-table.d.ts +3 -0
  112. package/use-data-table.js +768 -0
  113. package/use-lexicon.d.ts +12 -0
  114. package/use-lexicon.js +151 -0
  115. package/use-row-menu.d.ts +7 -0
  116. package/use-row-menu.js +58 -0
  117. package/use-visibility-column.d.ts +7 -0
  118. package/use-visibility-column.js +101 -0
  119. package/use-visibility-filter.d.ts +7 -0
  120. package/use-visibility-filter.js +145 -0
  121. package/utils.d.ts +14 -0
  122. package/utils.js +128 -0
@@ -0,0 +1,768 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useReactTable, getCoreRowModel, getPaginationRowModel, getFilteredRowModel, getSortedRowModel } from "@tanstack/react-table";
5
+ import { errorMessage } from "@tint-ui/tools/error-message";
6
+ import { useVisibilityColumn } from "./use-visibility-column.js";
7
+ import { renderDataTableCell } from "./cell-adapter-type.js";
8
+ import { useLexicon } from "./use-lexicon.js";
9
+ import { useVisibilityFilter } from "./use-visibility-filter.js";
10
+ import { filterBoolean, filterNumber, filterNumberMultiple, filterString, filterStringMultiple } from "./filter-fn.js";
11
+ import { rowButtonMenu } from "./row-button-menu.js";
12
+ import { rowPopoverMenu } from "./row-popover-menu.js";
13
+ import { diffFilterId, getNavbarConfig, getToolbarConfig } from "./utils.js";
14
+ const createHash = function (name, keyName, manual, cells) {
15
+ let hash = `${name}[${keyName},${manual ? "1" : "0"}]://`;
16
+ for (const cell of cells) {
17
+ hash += ":" + (cell.type || "text") + "/" + String(cell.name);
18
+ }
19
+ return hash;
20
+ };
21
+ const displayType = function (cell) {
22
+ const {
23
+ type = "text",
24
+ name,
25
+ config = {},
26
+ defaultSortDir = "asc",
27
+ label,
28
+ hidden = false,
29
+ required = false,
30
+ searchable = type === "text",
31
+ sortable = false,
32
+ invisible = false
33
+ } = cell;
34
+ let {
35
+ filter: inputFilter = null
36
+ } = cell;
37
+ const displayCell = {
38
+ type,
39
+ name,
40
+ config,
41
+ defaultSortDir,
42
+ label,
43
+ hidden,
44
+ invisible,
45
+ required,
46
+ searchable,
47
+ sortable,
48
+ filter: inputFilter != null
49
+ };
50
+ let filter = null;
51
+ let filterFn = undefined;
52
+ if (inputFilter != null) {
53
+ if (Array.isArray(inputFilter) || typeof inputFilter === "function") {
54
+ inputFilter = {
55
+ type: "option",
56
+ config: {
57
+ options: inputFilter
58
+ }
59
+ };
60
+ }
61
+ const {
62
+ type: adapterType = "option",
63
+ multiple = false,
64
+ config = {}
65
+ } = inputFilter;
66
+ let {
67
+ valueType
68
+ } = inputFilter;
69
+ if (!valueType) {
70
+ if (type === "boolean" || type === "number") {
71
+ valueType = type;
72
+ } else {
73
+ valueType = "string";
74
+ }
75
+ }
76
+ filter = {
77
+ name,
78
+ type: adapterType,
79
+ valueType: valueType,
80
+ multiple: multiple && type !== "boolean",
81
+ label: inputFilter.label || label,
82
+ hidden: typeof inputFilter.hidden === "boolean" ? inputFilter.hidden : hidden,
83
+ required: typeof inputFilter.required === "boolean" ? inputFilter.required : required,
84
+ config
85
+ };
86
+ switch (valueType) {
87
+ case "boolean":
88
+ filterFn = filterBoolean;
89
+ break;
90
+ case "number":
91
+ filterFn = multiple ? filterNumberMultiple : filterNumber;
92
+ break;
93
+ case "string":
94
+ filterFn = multiple ? filterStringMultiple : filterString;
95
+ break;
96
+ }
97
+ }
98
+ return {
99
+ cell: displayCell,
100
+ filter,
101
+ filterFn
102
+ };
103
+ };
104
+ const initialDataState = function (data, initialData) {
105
+ if (Array.isArray(initialData)) {
106
+ return initialData;
107
+ }
108
+ if (typeof data === "function") {
109
+ return [];
110
+ }
111
+ return Array.isArray(data) ? data : [];
112
+ };
113
+ const createFilterState = function (props, opts) {
114
+ const {
115
+ offset = 0,
116
+ filter,
117
+ sortBy,
118
+ sortDir,
119
+ filterText = ""
120
+ } = props;
121
+ const {
122
+ data,
123
+ manual,
124
+ pageSizeOptions,
125
+ hash
126
+ } = opts;
127
+ let {
128
+ total
129
+ } = props;
130
+ if (!manual) {
131
+ total = data.length;
132
+ }
133
+ let {
134
+ limit
135
+ } = props;
136
+ if (limit == null) {
137
+ limit = pageSizeOptions[0];
138
+ }
139
+ let pageIndex = 0;
140
+ if (offset > 0 && offset >= limit) {
141
+ pageIndex = (offset - offset % limit) / limit;
142
+ }
143
+ const columnFilters = [];
144
+ const pagination = {
145
+ pageIndex,
146
+ pageSize: limit
147
+ };
148
+ if (filter != null) {
149
+ applyFilter(columnFilters, filter);
150
+ }
151
+ const sorting = [];
152
+ if (sortBy != null) {
153
+ sorting.push({
154
+ id: sortBy,
155
+ desc: sortDir == null ? false : sortDir === "desc"
156
+ });
157
+ }
158
+ return {
159
+ hash,
160
+ error: null,
161
+ loading: false,
162
+ loadingTarget: null,
163
+ totalCount: total == null ? -1 : total,
164
+ pageCount: getPageCount(total, limit),
165
+ sorting,
166
+ globalFilter: filterText,
167
+ columnFilters,
168
+ pagination,
169
+ selected: [] // todo
170
+ };
171
+ };
172
+ const applyFilter = function (columnFilters, filter) {
173
+ for (const key in filter) {
174
+ const value = filter[key];
175
+ if (value != null) columnFilters.push({
176
+ id: key,
177
+ value
178
+ });
179
+ }
180
+ };
181
+ const defaultPageSize = 25;
182
+ const getPageCount = (total, limit) => {
183
+ return total == null ? -1 : total > limit ? Math.ceil(total / limit) : 1;
184
+ };
185
+ const useDataTable = function (props) {
186
+ const {
187
+ table,
188
+ header,
189
+ cacheable = true,
190
+ onRowClick,
191
+ toolbar: toolbarProp,
192
+ navbar: navbarProp
193
+ } = props;
194
+ const {
195
+ name,
196
+ keyName = "id",
197
+ cells: tableCells,
198
+ rowMenu: rowMenuProp = null,
199
+ storage = false,
200
+ ssr = true,
201
+ requiredFilterText = false
202
+ } = table;
203
+
204
+ // manual (dynamic) table or not
205
+ const refDataFn = React.useRef(null);
206
+ refDataFn.current = typeof props.data === "function" ? props.data : null;
207
+ const manual = refDataFn.current != null;
208
+ const hash = createHash(name, keyName, manual, tableCells);
209
+ const refHash = React.useRef(hash);
210
+ const [data, setData] = React.useState(() => initialDataState(props.data, props.initialData));
211
+ const toolbar = getToolbarConfig(toolbarProp);
212
+ const navbar = getNavbarConfig(navbarProp, props.limit || defaultPageSize);
213
+ const [state, setState] = React.useState(() => createFilterState(props, {
214
+ data,
215
+ pageSizeOptions: navbar.pageSizeOptions,
216
+ manual,
217
+ hash
218
+ }));
219
+
220
+ // read page size from state
221
+ navbar.pageSize = state.pagination.pageSize;
222
+ const {
223
+ onMount,
224
+ onReload,
225
+ onFilterReset,
226
+ onPageSizeChange,
227
+ onColumnFiltersChange,
228
+ onSortingChange,
229
+ onGlobalFilterChange,
230
+ onPaginationChange
231
+ } = React.useMemo(() => {
232
+ let mount = false;
233
+ let lazyId = null;
234
+ let waitTime = 0;
235
+ const clearTimeout = () => {
236
+ if (lazyId != null) {
237
+ window.clearTimeout(lazyId);
238
+ lazyId = null;
239
+ }
240
+ };
241
+ const compareHash = hash => {
242
+ return refHash.current === hash;
243
+ };
244
+ const query = (newState, loadingTarget = null) => {
245
+ const fn = refDataFn.current;
246
+ if (!fn || !mount) {
247
+ return false;
248
+ }
249
+ waitTime = 0;
250
+ const queryHash = refHash.current;
251
+ const asyncFn = async data => fn(data);
252
+ const {
253
+ pagination: {
254
+ pageIndex,
255
+ pageSize
256
+ },
257
+ sorting,
258
+ columnFilters,
259
+ globalFilter
260
+ } = newState;
261
+ const sort = sorting[0];
262
+ const data = {
263
+ filter: {},
264
+ filterText: globalFilter,
265
+ limit: pageSize,
266
+ offset: pageIndex > 0 ? pageIndex * pageSize : 0,
267
+ sortBy: sort == null ? null : sort.id,
268
+ sortDir: sort != null && sort.desc ? "desc" : "asc"
269
+ };
270
+ for (const filter of columnFilters) {
271
+ data.filter[filter.id] = filter.value;
272
+ }
273
+
274
+ // check loading status
275
+ setState(prev => {
276
+ if (prev.loading) {
277
+ return prev;
278
+ }
279
+ return {
280
+ ...prev,
281
+ loading: true,
282
+ loadingTarget
283
+ };
284
+ });
285
+ asyncFn(data).then(result => {
286
+ if (!mount || !compareHash(queryHash)) {
287
+ return;
288
+ }
289
+ const {
290
+ data,
291
+ total,
292
+ filter,
293
+ sortBy,
294
+ sortDir,
295
+ filterText,
296
+ limit,
297
+ offset = 0
298
+ } = result;
299
+ setData(data);
300
+ setState(prev => {
301
+ const columnFilters = [];
302
+ if (filter != null) {
303
+ applyFilter(columnFilters, filter);
304
+ }
305
+ const pageSize = limit;
306
+ const pageIndex = offset >= pageSize ? (offset - offset % pageSize) / pageSize : 0;
307
+ return {
308
+ hash: prev.hash,
309
+ selected: [],
310
+ // todo find selected in new data
311
+ pagination: {
312
+ pageSize,
313
+ pageIndex
314
+ },
315
+ columnFilters,
316
+ globalFilter: typeof filterText === "string" ? filterText : "",
317
+ sorting: sortBy == null ? [] : [{
318
+ id: sortBy,
319
+ desc: sortDir == null ? false : sortDir === "desc"
320
+ }],
321
+ totalCount: total,
322
+ pageCount: getPageCount(total, pageSize),
323
+ loading: false,
324
+ loadingTarget: null,
325
+ error: null
326
+ };
327
+ });
328
+ }).catch(err => {
329
+ if (!mount || !compareHash(queryHash)) {
330
+ return;
331
+ }
332
+ setState(prev => ({
333
+ ...prev,
334
+ loading: false,
335
+ loadingTarget: null,
336
+ error: errorMessage(err).message
337
+ }));
338
+ });
339
+ return true;
340
+ };
341
+ const force = (newState, lazy = false, loadingTarget = null) => {
342
+ const fn = refDataFn.current;
343
+ if (!fn) {
344
+ return false;
345
+ }
346
+ const queryHash = refHash.current;
347
+
348
+ // lazy, wait
349
+ if (lazy) {
350
+ const now = Date.now();
351
+ if (waitTime === 0 || now - waitTime < 2500) {
352
+ clearTimeout();
353
+ if (waitTime === 0) {
354
+ waitTime = now;
355
+ }
356
+ lazyId = window.setTimeout(() => {
357
+ if (compareHash(queryHash)) {
358
+ force(newState, false, loadingTarget);
359
+ }
360
+ }, 200);
361
+ return false;
362
+ }
363
+ }
364
+ return query(newState, loadingTarget);
365
+ };
366
+ return {
367
+ onMount() {
368
+ mount = true;
369
+ return () => {
370
+ mount = false;
371
+ };
372
+ },
373
+ onReload(reloadState) {
374
+ query(reloadState, "reload");
375
+ },
376
+ onFilterReset() {
377
+ if (!mount) {
378
+ return;
379
+ }
380
+ setState(prev => {
381
+ if (prev.loading) {
382
+ return prev;
383
+ }
384
+ const state = {
385
+ ...prev,
386
+ columnFilters: [],
387
+ globalFilter: ""
388
+ };
389
+ if (force(state)) {
390
+ state.loading = true;
391
+ state.loadingTarget = "reset";
392
+ }
393
+ return state;
394
+ });
395
+ },
396
+ onPageSizeChange(pageSize) {
397
+ setState(prev => {
398
+ if (prev.loading || prev.pagination.pageSize === pageSize) {
399
+ return prev;
400
+ }
401
+ const state = {
402
+ ...prev,
403
+ pagination: {
404
+ pageSize,
405
+ pageIndex: 0
406
+ }
407
+ };
408
+ if (force(state)) {
409
+ state.loading = true;
410
+ state.loadingTarget = "page-size";
411
+ }
412
+ return state;
413
+ });
414
+ },
415
+ onGlobalFilterChange(value) {
416
+ setState(prev => {
417
+ if (prev.loading) {
418
+ return prev;
419
+ }
420
+ const {
421
+ globalFilter,
422
+ ...rest
423
+ } = prev;
424
+ if (typeof value === "function") {
425
+ value = value(globalFilter);
426
+ }
427
+ const state = {
428
+ ...rest,
429
+ globalFilter: value == null ? "" : String(value)
430
+ };
431
+ if (force(state, true, "filter-text")) {
432
+ state.loading = true;
433
+ state.loadingTarget = "filter-text";
434
+ }
435
+ return state;
436
+ });
437
+ },
438
+ onColumnFiltersChange(value) {
439
+ setState(prev => {
440
+ if (prev.loading) {
441
+ return prev;
442
+ }
443
+ const {
444
+ columnFilters,
445
+ ...rest
446
+ } = prev;
447
+ if (typeof value === "function") {
448
+ value = value(columnFilters);
449
+ }
450
+ value = value.filter(({
451
+ value
452
+ }) => Array.isArray(value) ? value.length > 0 : value != null && value !== "");
453
+ const state = {
454
+ ...rest,
455
+ columnFilters: value
456
+ };
457
+ if (force(state)) {
458
+ state.loading = true;
459
+ state.loadingTarget = "filter:" + diffFilterId(columnFilters, state.columnFilters);
460
+ }
461
+ return state;
462
+ });
463
+ },
464
+ onSortingChange(value) {
465
+ setState(prev => {
466
+ if (prev.loading) {
467
+ return prev;
468
+ }
469
+ const {
470
+ sorting,
471
+ ...rest
472
+ } = prev;
473
+ const state = {
474
+ ...rest,
475
+ error: null,
476
+ sorting: typeof value === "function" ? value(sorting) : value,
477
+ loading: false
478
+ };
479
+ if (force(state)) {
480
+ state.loading = true;
481
+ const first = sorting[0];
482
+ if (first) {
483
+ state.loadingTarget = `sorting:${first.id}`;
484
+ }
485
+ }
486
+ return state;
487
+ });
488
+ },
489
+ onPaginationChange(value) {
490
+ setState(prev => {
491
+ if (prev.loading) {
492
+ return prev;
493
+ }
494
+ const {
495
+ pagination,
496
+ ...rest
497
+ } = prev;
498
+ const state = {
499
+ ...rest,
500
+ pagination: typeof value === "function" ? value(pagination) : value,
501
+ error: null,
502
+ loading: false
503
+ };
504
+ if (force(state)) {
505
+ state.loading = true;
506
+ state.loadingTarget = "pagination:" + (state.pagination.pageIndex + 1);
507
+ }
508
+ return state;
509
+ });
510
+ }
511
+ };
512
+ }, [hash]);
513
+ React.useEffect(onMount, [onMount]);
514
+ const currentData = !cacheable && !manual && Array.isArray(props.data) ? props.data : null;
515
+ React.useEffect(() => {
516
+ if (currentData != null && data != currentData) {
517
+ setData(currentData);
518
+ }
519
+ }, [currentData, data]);
520
+ React.useEffect(() => {
521
+ let dataState = data;
522
+ let filterState = state;
523
+ if (refHash.current !== hash) {
524
+ refHash.current = hash;
525
+
526
+ // update data state
527
+ dataState = initialDataState(props.data, props.initialData);
528
+ setData(dataState);
529
+
530
+ // update filter state
531
+ filterState = createFilterState(props, {
532
+ data: dataState,
533
+ pageSizeOptions: navbar.pageSizeOptions,
534
+ manual,
535
+ hash
536
+ });
537
+ setState(filterState);
538
+ }
539
+
540
+ // load table if empty
541
+ if (manual && dataState.length === 0) {
542
+ onReload(filterState);
543
+ }
544
+ }, [hash]);
545
+ const {
546
+ cells,
547
+ filters,
548
+ columns,
549
+ dynamic,
550
+ filterText,
551
+ invisible
552
+ } = React.useMemo(() => {
553
+ const cells = [];
554
+ const columns = [];
555
+ const filters = [];
556
+ const invisible = {};
557
+ const dynamic = {
558
+ column: false,
559
+ sort: false,
560
+ filter: false,
561
+ filterText: false
562
+ };
563
+ let filterText = false;
564
+ for (const cell of tableCells) {
565
+ const {
566
+ cell: requiredCell,
567
+ filter,
568
+ filterFn
569
+ } = displayType(cell);
570
+ const {
571
+ name,
572
+ sortable,
573
+ defaultSortDir,
574
+ searchable,
575
+ required
576
+ } = requiredCell;
577
+ if (cell.invisible) {
578
+ invisible[cell.name] = true;
579
+ }
580
+ cells.push(requiredCell);
581
+ if (filter != null) {
582
+ filters.push(filter);
583
+ dynamic.filter = true;
584
+ }
585
+ if (sortable) {
586
+ dynamic.sort = true;
587
+ }
588
+ if (searchable) {
589
+ filterText = true;
590
+ if (!requiredFilterText) {
591
+ dynamic.filterText = true;
592
+ }
593
+ }
594
+ if (!required) {
595
+ dynamic.column = true;
596
+ }
597
+ columns.push({
598
+ accessorKey: name,
599
+ cell: info => renderDataTableCell(info.getValue(), requiredCell, info.row.original),
600
+ header: () => cell.label,
601
+ footer: () => cell.label,
602
+ filterFn,
603
+ enableHiding: !required && !cell.invisible,
604
+ enableSorting: sortable,
605
+ sortDescFirst: defaultSortDir === "desc",
606
+ enableColumnFilter: filter != null,
607
+ enableGlobalFilter: searchable,
608
+ meta: {
609
+ _cell: requiredCell
610
+ }
611
+ });
612
+ }
613
+ let menu = [];
614
+ let mode = null;
615
+ let alignStart = false;
616
+ if (rowMenuProp != null) {
617
+ if (Array.isArray(rowMenuProp)) {
618
+ menu = rowMenuProp;
619
+ } else {
620
+ menu = rowMenuProp.menu;
621
+ if (rowMenuProp.mode) {
622
+ mode = rowMenuProp.mode;
623
+ }
624
+ if (rowMenuProp.align === "start") {
625
+ alignStart = true;
626
+ }
627
+ }
628
+ if (!mode) {
629
+ mode = menu.length > 4 ? "popover" : "button";
630
+ }
631
+ } else {
632
+ mode = "button";
633
+ }
634
+ if (menu.length > 0) {
635
+ const column = {
636
+ accessorKey: "__#menu",
637
+ cell: mode === "button" ? info => rowButtonMenu(info, menu) : info => rowPopoverMenu(info, menu),
638
+ header: "",
639
+ footer: "",
640
+ enableHiding: false,
641
+ enableSorting: false,
642
+ enableColumnFilter: false,
643
+ enableGlobalFilter: false
644
+ };
645
+ alignStart ? columns.unshift(column) : columns.push(column);
646
+ }
647
+
648
+ // todo selectable mode...
649
+
650
+ return {
651
+ columns,
652
+ filters,
653
+ cells,
654
+ dynamic,
655
+ filterText,
656
+ invisible
657
+ };
658
+ }, [hash]);
659
+ const [columnVisibility, onColumnVisibilityChange] = useVisibilityColumn(name, cells, {
660
+ storage,
661
+ ssr
662
+ });
663
+ const filterVisible = useVisibilityFilter(name, filters, filterText, {
664
+ storage,
665
+ ssr,
666
+ requiredFilterText
667
+ });
668
+ const top = header === "top" || header === "both";
669
+ let bottom = header === "bottom" || header === "both";
670
+ if (data.length < 7 && top && bottom) {
671
+ bottom = false;
672
+ }
673
+ const {
674
+ loading
675
+ } = state;
676
+ const getRowClickHandler = React.useCallback(data => {
677
+ if (!onRowClick || loading) {
678
+ return undefined;
679
+ }
680
+ return event => {
681
+ const target = event.target;
682
+ if ("closest" in target && !target.closest("a[href],button,input,select,textarea,[role=button]")) {
683
+ onRowClick(data);
684
+ }
685
+ };
686
+ }, [onRowClick, loading]);
687
+ const {
688
+ pagination
689
+ } = state;
690
+ const tableCtx = useReactTable({
691
+ data,
692
+ columns,
693
+ pageCount: manual ? state.pageCount : undefined,
694
+ state: {
695
+ columnVisibility,
696
+ sorting: state.sorting,
697
+ globalFilter: state.globalFilter,
698
+ columnFilters: state.columnFilters,
699
+ pagination
700
+ },
701
+ getCoreRowModel: getCoreRowModel(),
702
+ getPaginationRowModel: getPaginationRowModel(),
703
+ getFilteredRowModel: getFilteredRowModel(),
704
+ getSortedRowModel: getSortedRowModel(),
705
+ globalFilterFn: "auto",
706
+ onColumnFiltersChange,
707
+ onGlobalFilterChange,
708
+ onColumnVisibilityChange,
709
+ onSortingChange,
710
+ onPaginationChange,
711
+ manualFiltering: manual,
712
+ manualSorting: manual,
713
+ manualPagination: manual
714
+ });
715
+
716
+ // lexicon page config
717
+ const limit = pagination.pageSize;
718
+ const offset = pagination.pageIndex * limit;
719
+ let totalCount = state.totalCount;
720
+ let size = data.length;
721
+ let pageCount = state.pageCount;
722
+ if (!manual) {
723
+ totalCount = data.length;
724
+ pageCount = tableCtx.getPageCount();
725
+ const end = offset + limit;
726
+ size = end > totalCount ? limit - end + totalCount : limit;
727
+ }
728
+ const lexicon = useLexicon(props.lexicon || {}, {
729
+ limit,
730
+ offset,
731
+ pageNumber: pagination.pageIndex + 1,
732
+ pageCount,
733
+ total: totalCount,
734
+ size,
735
+ selected: state.selected.length
736
+ });
737
+ console.log("state.loadingTarget", state.loadingTarget);
738
+ return {
739
+ hash,
740
+ data,
741
+ cells,
742
+ filters,
743
+ dynamic,
744
+ totalCount,
745
+ lexicon,
746
+ getRowClickHandler,
747
+ filterVisible,
748
+ manual,
749
+ loading,
750
+ loadingTarget: state.loadingTarget,
751
+ header: {
752
+ top,
753
+ bottom
754
+ },
755
+ toolbar: {
756
+ ...toolbar,
757
+ onFilterReset
758
+ },
759
+ navbar: {
760
+ ...navbar,
761
+ onPageSizeChange
762
+ },
763
+ table: tableCtx,
764
+ error: state.error,
765
+ invisible
766
+ };
767
+ };
768
+ export { useDataTable };