@dxos/react-ui-table 0.4.10-main.fda6f37 → 0.4.10-next.169e4e3
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/lib/browser/index.mjs +219 -139
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +17 -4
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +29 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableRootContext.d.ts +17 -0
- package/dist/types/src/components/Table/TableRootContext.d.ts.map +1 -0
- package/dist/types/src/components/Table/index.d.ts +1 -0
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- package/dist/types/src/components/Table/props.d.ts +2 -3
- package/dist/types/src/components/Table/props.d.ts.map +1 -1
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useColumnSizing.d.ts +12 -0
- package/dist/types/src/hooks/useColumnSizing.d.ts.map +1 -0
- package/dist/types/src/hooks/usePinLastRow.d.ts +1 -1
- package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useRowSelection.d.ts +7 -0
- package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -0
- package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -1
- package/package.json +19 -16
- package/src/components/ColumnMenu/ColumnMenu.tsx +34 -32
- package/src/components/Table/Table.stories.tsx +204 -89
- package/src/components/Table/Table.tsx +70 -101
- package/src/components/Table/TableBody.tsx +6 -1
- package/src/components/Table/TableRootContext.ts +39 -0
- package/src/components/Table/index.ts +1 -0
- package/src/components/Table/props.ts +17 -24
- package/src/helpers.tsx +9 -7
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useColumnSizing.ts +45 -0
- package/src/hooks/usePinLastRow.ts +10 -10
- package/src/hooks/useRowSelection.ts +39 -0
- package/src/schema/SchemaToTable.stories.tsx +15 -16
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
6
|
|
|
7
7
|
import { Plugs, PlugsConnected } from '@phosphor-icons/react';
|
|
8
|
-
import React, { useCallback, useEffect, useMemo,
|
|
8
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import { create } from '@dxos/echo-schema
|
|
10
|
+
import { create } from '@dxos/echo-schema';
|
|
11
|
+
import { registerSignalRuntime } from '@dxos/echo-signals/react';
|
|
11
12
|
import { PublicKey } from '@dxos/keys';
|
|
12
13
|
import { faker } from '@dxos/random';
|
|
13
|
-
import {
|
|
14
|
+
import { Button, DensityProvider } from '@dxos/react-ui';
|
|
14
15
|
import { withTheme } from '@dxos/storybook-utils';
|
|
15
16
|
import { range } from '@dxos/util';
|
|
16
17
|
|
|
@@ -115,9 +116,19 @@ const makeColumns = (onUpdate?: ValueUpdater<Item, any>): TableColumnDef<Item, a
|
|
|
115
116
|
// Tests
|
|
116
117
|
//
|
|
117
118
|
|
|
119
|
+
const MinimalTable = (props: any) => {
|
|
120
|
+
return (
|
|
121
|
+
<Table.Root>
|
|
122
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
123
|
+
<Table.Table<any> {...props} />
|
|
124
|
+
</Table.Viewport>
|
|
125
|
+
</Table.Root>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
118
129
|
export default {
|
|
119
130
|
title: 'react-ui-table/Table',
|
|
120
|
-
component:
|
|
131
|
+
component: MinimalTable,
|
|
121
132
|
args: {
|
|
122
133
|
header: true,
|
|
123
134
|
keyAccessor: (item: Item) => item.publicKey.toHex(),
|
|
@@ -186,7 +197,6 @@ export const Empty = {
|
|
|
186
197
|
|
|
187
198
|
export const Dynamic = {
|
|
188
199
|
render: () => {
|
|
189
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
190
200
|
const [items, setItems] = useState<Item[]>(createItems(50));
|
|
191
201
|
|
|
192
202
|
useEffect(() => {
|
|
@@ -204,26 +214,27 @@ export const Dynamic = {
|
|
|
204
214
|
const columns = useMemo(() => makeColumns(), []);
|
|
205
215
|
|
|
206
216
|
return (
|
|
207
|
-
<
|
|
208
|
-
<Table
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
<Table.Root>
|
|
218
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
219
|
+
<Table.Table<Item>
|
|
220
|
+
role='grid'
|
|
221
|
+
rowsSelectable='multi'
|
|
222
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
223
|
+
columns={columns}
|
|
224
|
+
data={items}
|
|
225
|
+
fullWidth
|
|
226
|
+
stickyHeader
|
|
227
|
+
border
|
|
228
|
+
pinLastRow
|
|
229
|
+
/>
|
|
230
|
+
</Table.Viewport>
|
|
231
|
+
</Table.Root>
|
|
220
232
|
);
|
|
221
233
|
},
|
|
222
234
|
};
|
|
223
235
|
|
|
224
236
|
export const Editable = {
|
|
225
237
|
render: () => {
|
|
226
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
227
238
|
const [items, setItems] = useState<Item[]>(createItems(200));
|
|
228
239
|
|
|
229
240
|
const onUpdate: ValueUpdater<Item, any> = useCallback(
|
|
@@ -234,26 +245,27 @@ export const Editable = {
|
|
|
234
245
|
const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
|
|
235
246
|
|
|
236
247
|
return (
|
|
237
|
-
<
|
|
238
|
-
<Table
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
<Table.Root>
|
|
249
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
250
|
+
<Table.Table<Item>
|
|
251
|
+
role='grid'
|
|
252
|
+
rowsSelectable='multi'
|
|
253
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
254
|
+
columns={columns}
|
|
255
|
+
data={items}
|
|
256
|
+
fullWidth
|
|
257
|
+
stickyHeader
|
|
258
|
+
border
|
|
259
|
+
pinLastRow
|
|
260
|
+
/>
|
|
261
|
+
</Table.Viewport>
|
|
262
|
+
</Table.Root>
|
|
250
263
|
);
|
|
251
264
|
},
|
|
252
265
|
};
|
|
253
266
|
|
|
254
267
|
export const PinnedLastRow = {
|
|
255
268
|
render: () => {
|
|
256
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
257
269
|
const [items, setItems] = useState<Item[]>(createItems(200));
|
|
258
270
|
|
|
259
271
|
const onUpdate: ValueUpdater<Item, any> = useCallback(
|
|
@@ -264,27 +276,27 @@ export const PinnedLastRow = {
|
|
|
264
276
|
const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
|
|
265
277
|
|
|
266
278
|
return (
|
|
267
|
-
<
|
|
268
|
-
<Table
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
<Table.Root>
|
|
280
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
281
|
+
<Table.Table<Item>
|
|
282
|
+
role='grid'
|
|
283
|
+
rowsSelectable='multi'
|
|
284
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
285
|
+
columns={columns}
|
|
286
|
+
data={items}
|
|
287
|
+
fullWidth
|
|
288
|
+
stickyHeader
|
|
289
|
+
border
|
|
290
|
+
pinLastRow
|
|
291
|
+
/>
|
|
292
|
+
</Table.Viewport>
|
|
293
|
+
</Table.Root>
|
|
281
294
|
);
|
|
282
295
|
},
|
|
283
296
|
};
|
|
284
297
|
|
|
285
298
|
export const InsertDelete = {
|
|
286
299
|
render: () => {
|
|
287
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
288
300
|
const [items, setItems] = useState<Item[]>(createItems(10));
|
|
289
301
|
|
|
290
302
|
const onUpdate: ValueUpdater<Item, any> = useCallback(
|
|
@@ -318,20 +330,21 @@ export const InsertDelete = {
|
|
|
318
330
|
<Button onClick={onDeleteFirst}>Delete first</Button>
|
|
319
331
|
<Button onClick={onDeleteLast}>Delete last</Button>
|
|
320
332
|
</div>
|
|
321
|
-
<
|
|
322
|
-
<Table
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
<Table.Root>
|
|
334
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
335
|
+
<Table.Table<Item>
|
|
336
|
+
role='grid'
|
|
337
|
+
rowsSelectable='multi'
|
|
338
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
339
|
+
columns={columns}
|
|
340
|
+
data={items}
|
|
341
|
+
fullWidth
|
|
342
|
+
stickyHeader
|
|
343
|
+
border
|
|
344
|
+
pinLastRow
|
|
345
|
+
/>
|
|
346
|
+
</Table.Viewport>
|
|
347
|
+
</Table.Root>
|
|
335
348
|
</div>
|
|
336
349
|
);
|
|
337
350
|
},
|
|
@@ -339,7 +352,6 @@ export const InsertDelete = {
|
|
|
339
352
|
|
|
340
353
|
export const Resizable = {
|
|
341
354
|
render: () => {
|
|
342
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
343
355
|
const [items, setItems] = useState<Item[]>(createItems(10));
|
|
344
356
|
|
|
345
357
|
const onUpdate: ValueUpdater<Item, any> = useCallback(
|
|
@@ -348,25 +360,24 @@ export const Resizable = {
|
|
|
348
360
|
);
|
|
349
361
|
|
|
350
362
|
return (
|
|
351
|
-
<
|
|
352
|
-
<Table
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
</
|
|
363
|
+
<Table.Root>
|
|
364
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
365
|
+
<Table.Table<Item>
|
|
366
|
+
rowsSelectable='multi'
|
|
367
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
368
|
+
columns={makeColumns(onUpdate)}
|
|
369
|
+
data={items}
|
|
370
|
+
fullWidth
|
|
371
|
+
stickyHeader
|
|
372
|
+
/>
|
|
373
|
+
</Table.Viewport>
|
|
374
|
+
</Table.Root>
|
|
363
375
|
);
|
|
364
376
|
},
|
|
365
377
|
};
|
|
366
378
|
|
|
367
379
|
export const TenThousandRows = {
|
|
368
380
|
render: () => {
|
|
369
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
370
381
|
const [items, setItems] = useState<Item[]>(createItems(10000));
|
|
371
382
|
|
|
372
383
|
const onUpdate: ValueUpdater<Item, any> = useCallback(
|
|
@@ -377,19 +388,123 @@ export const TenThousandRows = {
|
|
|
377
388
|
const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
|
|
378
389
|
|
|
379
390
|
return (
|
|
380
|
-
<
|
|
381
|
-
<Table
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
391
|
+
<Table.Root>
|
|
392
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
393
|
+
<Table.Table<Item>
|
|
394
|
+
role='grid'
|
|
395
|
+
rowsSelectable='multi'
|
|
396
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
397
|
+
columns={columns}
|
|
398
|
+
data={items}
|
|
399
|
+
fullWidth
|
|
400
|
+
stickyHeader
|
|
401
|
+
border
|
|
402
|
+
/>{' '}
|
|
403
|
+
</Table.Viewport>
|
|
404
|
+
</Table.Root>
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
registerSignalRuntime();
|
|
410
|
+
const state = create({ items: createItems(10) });
|
|
411
|
+
|
|
412
|
+
export const RealTimeUpdates = {
|
|
413
|
+
args: {
|
|
414
|
+
periodicMutations: true,
|
|
415
|
+
mutationInterval: 1000,
|
|
416
|
+
periodicDeletions: false,
|
|
417
|
+
deletionInterval: 5000,
|
|
418
|
+
periodicInsertions: false,
|
|
419
|
+
insertionInterval: 3000,
|
|
420
|
+
},
|
|
421
|
+
argTypes: {
|
|
422
|
+
mutationInterval: { control: 'number' },
|
|
423
|
+
periodicDeletions: { control: 'boolean' },
|
|
424
|
+
deletionInterval: { control: 'number' },
|
|
425
|
+
periodicInsertions: { control: 'boolean' },
|
|
426
|
+
insertionInterval: { control: 'number' },
|
|
427
|
+
},
|
|
428
|
+
render: ({
|
|
429
|
+
periodicMutations,
|
|
430
|
+
mutationInterval,
|
|
431
|
+
periodicDeletions,
|
|
432
|
+
deletionInterval,
|
|
433
|
+
periodicInsertions,
|
|
434
|
+
insertionInterval,
|
|
435
|
+
}: any) => {
|
|
436
|
+
useEffect(() => {
|
|
437
|
+
if (!periodicMutations) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const interval = setInterval(() => {
|
|
442
|
+
if (state.items.length === 0) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const randomIndex = Math.floor(Math.random() * state.items.length);
|
|
447
|
+
console.log('Mutating row', randomIndex);
|
|
448
|
+
state.items[randomIndex].name = faker.commerce.productName();
|
|
449
|
+
state.items[randomIndex].count = Math.floor(Math.random() * 1000);
|
|
450
|
+
state.items[randomIndex].started = new Date();
|
|
451
|
+
}, mutationInterval);
|
|
452
|
+
|
|
453
|
+
return () => clearInterval(interval);
|
|
454
|
+
}, [periodicMutations, mutationInterval, state.items]);
|
|
455
|
+
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
if (!periodicInsertions) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const interval = setInterval(() => {
|
|
462
|
+
console.log('Inserting...');
|
|
463
|
+
state.items.push(createItems(1)[0]);
|
|
464
|
+
}, insertionInterval);
|
|
465
|
+
|
|
466
|
+
return () => clearInterval(interval);
|
|
467
|
+
}, [periodicInsertions, insertionInterval, state.items]);
|
|
468
|
+
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
if (!periodicDeletions) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const interval = setInterval(() => {
|
|
475
|
+
if (state.items.length === 0) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
console.log('Deleting');
|
|
480
|
+
|
|
481
|
+
// Randomly delete a row from state.items
|
|
482
|
+
const randomIndex = Math.floor(Math.random() * state.items.length);
|
|
483
|
+
state.items.splice(randomIndex, 1);
|
|
484
|
+
}, deletionInterval);
|
|
485
|
+
|
|
486
|
+
return () => clearInterval(interval);
|
|
487
|
+
}, [periodicDeletions, deletionInterval, state.items]);
|
|
488
|
+
|
|
489
|
+
const onUpdate = useCallback((...args: any[]) => {}, []);
|
|
490
|
+
const columns = useMemo(() => makeColumns(onUpdate), []);
|
|
491
|
+
|
|
492
|
+
return (
|
|
493
|
+
<Table.Root>
|
|
494
|
+
<Table.Viewport classNames='fixed inset-0 overflow-auto'>
|
|
495
|
+
<Table.Table<Item>
|
|
496
|
+
role='grid'
|
|
497
|
+
rowsSelectable='multi'
|
|
498
|
+
keyAccessor={(row) => row.publicKey.toHex()}
|
|
499
|
+
columns={columns}
|
|
500
|
+
data={state.items}
|
|
501
|
+
fullWidth
|
|
502
|
+
stickyHeader
|
|
503
|
+
border
|
|
504
|
+
pinLastRow
|
|
505
|
+
/>
|
|
506
|
+
</Table.Viewport>
|
|
507
|
+
</Table.Root>
|
|
393
508
|
);
|
|
394
509
|
},
|
|
395
510
|
};
|
|
@@ -1,36 +1,60 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import {
|
|
4
|
+
import { type Primitive } from '@radix-ui/react-primitive';
|
|
5
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
5
6
|
import {
|
|
6
7
|
getCoreRowModel,
|
|
7
8
|
getSortedRowModel,
|
|
8
9
|
useReactTable,
|
|
9
|
-
type ColumnSizingInfoState,
|
|
10
10
|
type GroupingState,
|
|
11
11
|
type RowData,
|
|
12
|
-
type ColumnSizingState,
|
|
13
|
-
type OnChangeFn,
|
|
14
|
-
type RowSelectionState,
|
|
15
12
|
getGroupedRowModel,
|
|
16
13
|
getExpandedRowModel,
|
|
17
14
|
type Row,
|
|
18
15
|
} from '@tanstack/react-table';
|
|
19
|
-
import {
|
|
20
|
-
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
|
16
|
+
import React, { type ComponentPropsWithoutRef, Fragment, useCallback, useEffect, useState, useContext } from 'react';
|
|
21
17
|
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
18
|
+
import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
|
|
19
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
24
20
|
|
|
25
21
|
import { TableBody } from './TableBody';
|
|
26
22
|
import { type TypedTableProvider, TableProvider as UntypedTableProvider, useTableContext } from './TableContext';
|
|
27
23
|
import { TableFooter } from './TableFooter';
|
|
28
24
|
import { TableHead } from './TableHead';
|
|
25
|
+
import { TableRootContext, useTableRootContext } from './TableRootContext';
|
|
29
26
|
import { type TableProps } from './props';
|
|
30
|
-
import { usePinLastRow } from '../../hooks';
|
|
27
|
+
import { useColumnResizing, usePinLastRow, useRowSelection } from '../../hooks';
|
|
31
28
|
import { groupTh, tableRoot } from '../../theme';
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
type TableRootProps = { children: React.ReactNode };
|
|
31
|
+
|
|
32
|
+
const TableRoot = ({ children }: TableRootProps) => {
|
|
33
|
+
const contextValue = useTableRootContext();
|
|
34
|
+
return <TableRootContext.Provider value={contextValue}>{children}</TableRootContext.Provider>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type TableViewportProps = ThemedClassName<ComponentPropsWithoutRef<typeof Primitive.div>> & {
|
|
38
|
+
asChild?: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const TableViewport = ({ children, classNames, asChild, ...props }: TableViewportProps) => {
|
|
42
|
+
const { scrollContextRef } = useContext(TableRootContext);
|
|
43
|
+
|
|
44
|
+
const classes = mx(classNames);
|
|
45
|
+
|
|
46
|
+
return asChild ? (
|
|
47
|
+
<Slot ref={scrollContextRef} {...props}>
|
|
48
|
+
{children}
|
|
49
|
+
</Slot>
|
|
50
|
+
) : (
|
|
51
|
+
<div role='none' className={classes} ref={scrollContextRef} {...props}>
|
|
52
|
+
{children}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>) => {
|
|
34
58
|
const {
|
|
35
59
|
role,
|
|
36
60
|
onColumnResize,
|
|
@@ -39,62 +63,26 @@ export const Table = <TData extends RowData>(props: TableProps<TData>) => {
|
|
|
39
63
|
rowsSelectable,
|
|
40
64
|
debug,
|
|
41
65
|
onDataSelectionChange,
|
|
42
|
-
getScrollElement,
|
|
43
66
|
pinLastRow,
|
|
44
67
|
} = props;
|
|
45
68
|
|
|
46
69
|
const columns = useDefaultValue(props.columns, []);
|
|
47
|
-
const
|
|
70
|
+
const incomingData = useDefaultValue(props.data, []);
|
|
48
71
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const [columnsInitialised, setColumnsInitialised] = useState(false);
|
|
52
|
-
const [columnSizing, setColumnSizing] = useState<ColumnSizingState>({});
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (columnsInitialised) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
setColumnSizing(
|
|
60
|
-
columns
|
|
61
|
-
.filter((column) => !!column.size && (column as any).prop !== undefined)
|
|
62
|
-
.reduce<ColumnSizingState>((state, column) => {
|
|
63
|
-
state[(column as any).prop] = column.size!;
|
|
64
|
-
return state;
|
|
65
|
-
}, {}),
|
|
66
|
-
);
|
|
72
|
+
const [data, setData] = useState([...incomingData]);
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
// Reactivity workaround: https://github.com/dxos/dxos/issues/6376.
|
|
75
|
+
const forceUpdate = useCallback(() => setData([...incomingData]), [incomingData]);
|
|
76
|
+
useEffect(() => forceUpdate(), [JSON.stringify(incomingData), forceUpdate]);
|
|
70
77
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
// Notify on column resize.
|
|
74
|
-
const notifyColumnResize = useCallback(() => onColumnResize?.(columnSizing), [onColumnResize, columnSizing]);
|
|
75
|
-
useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === 'string', false, notifyColumnResize);
|
|
78
|
+
const TableProvider = UntypedTableProvider as TypedTableProvider<TData>;
|
|
76
79
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
defaultProp: props.defaultRowSelection,
|
|
80
|
+
const { columnSizing, setColumnSizing, columnSizingInfo, setColumnSizingInfo } = useColumnResizing({
|
|
81
|
+
columns,
|
|
82
|
+
onColumnResize,
|
|
81
83
|
});
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
const handleRowSelectionChange = useCallback<OnChangeFn<RowSelectionState>>(
|
|
85
|
-
(updaterOrValue) => {
|
|
86
|
-
const nextRowSelection = typeof updaterOrValue === 'function' ? updaterOrValue(rowSelection) : updaterOrValue;
|
|
87
|
-
if (rowsSelectable === 'multi') {
|
|
88
|
-
setRowSelection(nextRowSelection);
|
|
89
|
-
} else if (rowsSelectable) {
|
|
90
|
-
const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
|
|
91
|
-
setRowSelection(nextRowSelectionKey ? { [nextRowSelectionKey]: true } : {});
|
|
92
|
-
} else {
|
|
93
|
-
setRowSelection({});
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
[rowsSelectable, setRowSelection, rowSelection],
|
|
97
|
-
);
|
|
85
|
+
const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
|
|
98
86
|
|
|
99
87
|
const [grouping, handleGroupingChange] = useState<GroupingState>(props.grouping ?? []);
|
|
100
88
|
useEffect(() => handleGroupingChange(props.grouping ?? []), [handleGroupingChange, props.grouping]);
|
|
@@ -151,15 +139,11 @@ export const Table = <TData extends RowData>(props: TableProps<TData>) => {
|
|
|
151
139
|
onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
|
|
152
140
|
}, [onDataSelectionChange, rowSelection, table]);
|
|
153
141
|
|
|
154
|
-
usePinLastRow(pinLastRow, table, data
|
|
142
|
+
usePinLastRow(pinLastRow, table, data);
|
|
155
143
|
|
|
156
144
|
// Create additional expansion column if all columns have fixed width.
|
|
157
145
|
const expand = false; // columns.map((column) => column.size).filter(Boolean).length === columns?.length;
|
|
158
146
|
|
|
159
|
-
if (!getScrollElement) {
|
|
160
|
-
log.warn('Table: getScrollElement is not set. This is required for virtualized tables.');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
147
|
return (
|
|
164
148
|
<TableProvider
|
|
165
149
|
{...props}
|
|
@@ -168,16 +152,17 @@ export const Table = <TData extends RowData>(props: TableProps<TData>) => {
|
|
|
168
152
|
expand={expand}
|
|
169
153
|
isGrid={role === 'grid' || role === 'treegrid'}
|
|
170
154
|
>
|
|
171
|
-
<TableImpl<TData> debug={false}
|
|
155
|
+
<TableImpl<TData> debug={false} {...props} />
|
|
172
156
|
</TableProvider>
|
|
173
157
|
);
|
|
174
158
|
};
|
|
175
159
|
|
|
160
|
+
// TODO(Zan): Smush this into the Table component.
|
|
176
161
|
/**
|
|
177
162
|
* Pure implementation of table outside of context set-up.
|
|
178
163
|
*/
|
|
179
164
|
const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
|
|
180
|
-
const { debug, classNames,
|
|
165
|
+
const { debug, classNames, role, footer, grouping, fullWidth } = props;
|
|
181
166
|
const { table } = useTableContext<TData>();
|
|
182
167
|
|
|
183
168
|
if (debug) {
|
|
@@ -188,9 +173,10 @@ const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
|
|
|
188
173
|
);
|
|
189
174
|
}
|
|
190
175
|
|
|
191
|
-
const virtualisable = getScrollElement !== undefined;
|
|
192
176
|
const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
|
|
193
177
|
|
|
178
|
+
const TableComponent = isResizingColumn ? MemoizedVirtualisedTableContent : VirtualizedTableContent;
|
|
179
|
+
|
|
194
180
|
return (
|
|
195
181
|
<table
|
|
196
182
|
role={role}
|
|
@@ -199,44 +185,24 @@ const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
|
|
|
199
185
|
>
|
|
200
186
|
<TableHead />
|
|
201
187
|
|
|
202
|
-
{grouping?.length !== 0 ?
|
|
203
|
-
virtualisable ? (
|
|
204
|
-
isResizingColumn ? (
|
|
205
|
-
<MemoizedVirtualisedTableContent getScrollElement={getScrollElement} />
|
|
206
|
-
) : (
|
|
207
|
-
<VirtualizedTableContent getScrollElement={getScrollElement} />
|
|
208
|
-
)
|
|
209
|
-
) : (
|
|
210
|
-
<UnvirtualizedTableContent />
|
|
211
|
-
)
|
|
212
|
-
) : (
|
|
213
|
-
<GroupedTableContent />
|
|
214
|
-
)}
|
|
188
|
+
{grouping?.length !== 0 ? <TableComponent /> : <GroupedTableContent />}
|
|
215
189
|
|
|
216
190
|
{footer && <TableFooter />}
|
|
217
191
|
</table>
|
|
218
192
|
);
|
|
219
193
|
};
|
|
220
194
|
|
|
221
|
-
const
|
|
222
|
-
const { table } = useTableContext();
|
|
223
|
-
|
|
224
|
-
const centerRows = table.getCenterRows();
|
|
225
|
-
const pinnedRows = table.getBottomRows();
|
|
226
|
-
|
|
227
|
-
const rows = [...centerRows, ...pinnedRows];
|
|
228
|
-
|
|
229
|
-
return <TableBody rows={rows} />;
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const VirtualizedTableContent = ({
|
|
233
|
-
getScrollElement,
|
|
234
|
-
}: Pick<VirtualizerOptions<Element, Element>, 'getScrollElement'>) => {
|
|
195
|
+
const VirtualizedTableContent = () => {
|
|
235
196
|
const { table } = useTableContext();
|
|
197
|
+
const { virtualizer, dispatch } = useContext(TableRootContext);
|
|
236
198
|
|
|
237
199
|
const centerRows = table.getCenterRows();
|
|
238
200
|
let pinnedRows = [] as Row<unknown>[];
|
|
239
201
|
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
dispatch({ type: 'updateTableCount', count: centerRows.length });
|
|
204
|
+
}, [centerRows.length]);
|
|
205
|
+
|
|
240
206
|
try {
|
|
241
207
|
// TODO(zan): Work out how to sync the row pinning with rendering
|
|
242
208
|
// This is a hack because the first call to this function after a row is deleted will throw an error
|
|
@@ -245,12 +211,7 @@ const VirtualizedTableContent = ({
|
|
|
245
211
|
pinnedRows = table.getBottomRows();
|
|
246
212
|
} catch (_) {} // Ignore error
|
|
247
213
|
|
|
248
|
-
const { getTotalSize, getVirtualItems } =
|
|
249
|
-
getScrollElement,
|
|
250
|
-
count: centerRows.length,
|
|
251
|
-
overscan: 8,
|
|
252
|
-
estimateSize: () => 40,
|
|
253
|
-
});
|
|
214
|
+
const { getTotalSize, getVirtualItems } = virtualizer;
|
|
254
215
|
|
|
255
216
|
const virtualRows = getVirtualItems();
|
|
256
217
|
const totalSize = getTotalSize();
|
|
@@ -258,10 +219,12 @@ const VirtualizedTableContent = ({
|
|
|
258
219
|
const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
|
|
259
220
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0;
|
|
260
221
|
|
|
261
|
-
const rowsToRender = [...virtualRows.map((virtualRow) => centerRows[virtualRow.index]), ...pinnedRows]
|
|
222
|
+
const rowsToRender = [...virtualRows.map((virtualRow) => centerRows[virtualRow.index]), ...pinnedRows].filter(
|
|
223
|
+
(r) => !!r,
|
|
224
|
+
);
|
|
262
225
|
|
|
263
226
|
return (
|
|
264
|
-
|
|
227
|
+
<Fragment>
|
|
265
228
|
{paddingTop > 0 && (
|
|
266
229
|
<tbody role='none'>
|
|
267
230
|
<tr role='none'>
|
|
@@ -277,7 +240,7 @@ const VirtualizedTableContent = ({
|
|
|
277
240
|
</tr>
|
|
278
241
|
</tbody>
|
|
279
242
|
)}
|
|
280
|
-
|
|
243
|
+
</Fragment>
|
|
281
244
|
);
|
|
282
245
|
};
|
|
283
246
|
|
|
@@ -314,3 +277,9 @@ const GroupedTableContent = () => {
|
|
|
314
277
|
</>
|
|
315
278
|
);
|
|
316
279
|
};
|
|
280
|
+
|
|
281
|
+
export const Table = {
|
|
282
|
+
Root: TableRoot,
|
|
283
|
+
Table: TablePrimitive,
|
|
284
|
+
Viewport: TableViewport,
|
|
285
|
+
};
|