@dxos/react-ui-table 0.4.9 → 0.4.10-main.05b9ab6

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 (87) hide show
  1. package/dist/lib/browser/index.mjs +786 -367
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts +19 -0
  5. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -0
  6. package/dist/types/src/components/{ColumnMenu.stories.d.ts → ColumnMenu/ColumnMenu.stories.d.ts} +10 -2
  7. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -0
  8. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts +11 -0
  9. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +1 -0
  10. package/dist/types/src/components/ColumnMenu/index.d.ts +3 -0
  11. package/dist/types/src/components/ColumnMenu/index.d.ts.map +1 -0
  12. package/dist/types/src/components/Table/Table.d.ts +17 -4
  13. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  14. package/dist/types/src/components/Table/Table.stories.d.ts +35 -1
  15. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
  17. package/dist/types/src/components/Table/TableRootContext.d.ts +17 -0
  18. package/dist/types/src/components/Table/TableRootContext.d.ts.map +1 -0
  19. package/dist/types/src/components/Table/index.d.ts +1 -0
  20. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  21. package/dist/types/src/components/Table/props.d.ts +2 -3
  22. package/dist/types/src/components/Table/props.d.ts.map +1 -1
  23. package/dist/types/src/components/index.d.ts +1 -1
  24. package/dist/types/src/components/index.d.ts.map +1 -1
  25. package/dist/types/src/helpers.d.ts +4 -0
  26. package/dist/types/src/helpers.d.ts.map +1 -1
  27. package/dist/types/src/hooks/index.d.ts +5 -0
  28. package/dist/types/src/hooks/index.d.ts.map +1 -0
  29. package/dist/types/src/hooks/useColumnSizing.d.ts +12 -0
  30. package/dist/types/src/hooks/useColumnSizing.d.ts.map +1 -0
  31. package/dist/types/src/hooks/useColumnSorting.d.ts +9 -0
  32. package/dist/types/src/hooks/useColumnSorting.d.ts.map +1 -0
  33. package/dist/types/src/hooks/usePinLastRow.d.ts +3 -0
  34. package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -0
  35. package/dist/types/src/hooks/useRowSelection.d.ts +7 -0
  36. package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -0
  37. package/dist/types/src/index.d.ts +2 -1
  38. package/dist/types/src/index.d.ts.map +1 -1
  39. package/dist/types/src/schema/SchemaToTable.stories.d.ts +11 -0
  40. package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -0
  41. package/dist/types/src/schema/index.d.ts +3 -0
  42. package/dist/types/src/schema/index.d.ts.map +1 -0
  43. package/dist/types/src/schema/schema.d.ts +5 -0
  44. package/dist/types/src/schema/schema.d.ts.map +1 -0
  45. package/dist/types/src/schema/schema.test.d.ts +2 -0
  46. package/dist/types/src/schema/schema.test.d.ts.map +1 -0
  47. package/dist/types/src/schema/schemaToColumns.d.ts +4 -0
  48. package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -0
  49. package/dist/types/src/{schema.d.ts → schema/types.d.ts} +1 -1
  50. package/dist/types/src/schema/types.d.ts.map +1 -0
  51. package/dist/types/src/theme.d.ts +1 -0
  52. package/dist/types/src/theme.d.ts.map +1 -1
  53. package/package.json +22 -16
  54. package/src/components/ColumnMenu/ColumnMenu.stories.tsx +127 -0
  55. package/src/components/ColumnMenu/ColumnMenu.tsx +168 -0
  56. package/src/components/ColumnMenu/ColumnSettingsForm.tsx +214 -0
  57. package/src/components/ColumnMenu/index.ts +6 -0
  58. package/src/components/Table/Table.stories.tsx +260 -59
  59. package/src/components/Table/Table.tsx +82 -107
  60. package/src/components/Table/TableBody.tsx +12 -2
  61. package/src/components/Table/TableRootContext.ts +39 -0
  62. package/src/components/Table/index.ts +1 -0
  63. package/src/components/Table/props.ts +17 -24
  64. package/src/components/index.ts +1 -1
  65. package/src/helpers.tsx +32 -8
  66. package/src/hooks/index.ts +8 -0
  67. package/src/hooks/useColumnSizing.ts +45 -0
  68. package/src/hooks/useColumnSorting.ts +54 -0
  69. package/src/hooks/usePinLastRow.ts +64 -0
  70. package/src/hooks/useRowSelection.ts +39 -0
  71. package/src/index.ts +3 -1
  72. package/src/schema/SchemaToTable.stories.tsx +71 -0
  73. package/src/schema/index.ts +6 -0
  74. package/src/schema/schema.test.ts +148 -0
  75. package/src/schema/schema.ts +92 -0
  76. package/src/schema/schemaToColumns.ts +48 -0
  77. package/src/theme.ts +3 -2
  78. package/dist/types/src/components/ColumnMenu.d.ts +0 -13
  79. package/dist/types/src/components/ColumnMenu.d.ts.map +0 -1
  80. package/dist/types/src/components/ColumnMenu.stories.d.ts.map +0 -1
  81. package/dist/types/src/components/SortButton.d.ts +0 -8
  82. package/dist/types/src/components/SortButton.d.ts.map +0 -1
  83. package/dist/types/src/schema.d.ts.map +0 -1
  84. package/src/components/ColumnMenu.stories.tsx +0 -46
  85. package/src/components/ColumnMenu.tsx +0 -248
  86. package/src/components/SortButton.tsx +0 -34
  87. /package/src/{schema.ts → schema/types.ts} +0 -0
@@ -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, useRef, useState } from 'react';
8
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
9
 
10
- import * as E from '@dxos/echo-schema/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 { AnchoredOverflow, DensityProvider } from '@dxos/react-ui';
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
 
@@ -35,7 +36,7 @@ faker.seed(911);
35
36
  const createItems = (count: number) =>
36
37
  range(count).map(
37
38
  () =>
38
- E.object<Item>({
39
+ create<Item>({
39
40
  publicKey: PublicKey.random(),
40
41
  name: faker.commerce.productName(),
41
42
  count: faker.datatype.boolean({ probability: 0.9 }) ? faker.number.int({ min: 0, max: 10_000 }) : undefined,
@@ -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: Table,
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
- <AnchoredOverflow.Root classNames='max-bs-[80dvh]' ref={containerRef}>
208
- <Table<Item>
209
- rowsSelectable='multi'
210
- keyAccessor={(row) => row.publicKey.toHex()}
211
- columns={columns}
212
- data={items}
213
- fullWidth
214
- footer
215
- stickyHeader
216
- getScrollElement={() => containerRef.current}
217
- />
218
- <AnchoredOverflow.Anchor />
219
- </AnchoredOverflow.Root>
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,18 +245,106 @@ export const Editable = {
234
245
  const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
235
246
 
236
247
  return (
237
- <div ref={containerRef} className='fixed inset-0 overflow-auto'>
238
- <Table<Item>
239
- role='grid'
240
- rowsSelectable='multi'
241
- keyAccessor={(row) => row.publicKey.toHex()}
242
- columns={columns}
243
- data={items}
244
- fullWidth
245
- stickyHeader
246
- border
247
- getScrollElement={() => containerRef.current}
248
- />
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>
263
+ );
264
+ },
265
+ };
266
+
267
+ export const PinnedLastRow = {
268
+ render: () => {
269
+ const [items, setItems] = useState<Item[]>(createItems(200));
270
+
271
+ const onUpdate: ValueUpdater<Item, any> = useCallback(
272
+ (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
273
+ [setItems],
274
+ );
275
+
276
+ const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
277
+
278
+ return (
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>
294
+ );
295
+ },
296
+ };
297
+
298
+ export const InsertDelete = {
299
+ render: () => {
300
+ const [items, setItems] = useState<Item[]>(createItems(10));
301
+
302
+ const onUpdate: ValueUpdater<Item, any> = useCallback(
303
+ (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
304
+ [setItems],
305
+ );
306
+
307
+ const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
308
+
309
+ const onInsertFirst = () => {
310
+ setItems((items) => [...createItems(1), ...items]);
311
+ };
312
+
313
+ const onInsertLast = () => {
314
+ setItems((items) => [...items, ...createItems(1)]);
315
+ };
316
+
317
+ const onDeleteFirst = () => {
318
+ setItems(([_first, ...rest]) => rest);
319
+ };
320
+
321
+ const onDeleteLast = () => {
322
+ setItems((items) => [...items.slice(0, items.length - 1)]);
323
+ };
324
+
325
+ return (
326
+ <div>
327
+ <div className='flex flex-row gap-2'>
328
+ <Button onClick={onInsertFirst}>Insert first</Button>
329
+ <Button onClick={onInsertLast}>Insert last</Button>
330
+ <Button onClick={onDeleteFirst}>Delete first</Button>
331
+ <Button onClick={onDeleteLast}>Delete last</Button>
332
+ </div>
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>
249
348
  </div>
250
349
  );
251
350
  },
@@ -253,7 +352,6 @@ export const Editable = {
253
352
 
254
353
  export const Resizable = {
255
354
  render: () => {
256
- const containerRef = useRef<HTMLDivElement | null>(null);
257
355
  const [items, setItems] = useState<Item[]>(createItems(10));
258
356
 
259
357
  const onUpdate: ValueUpdater<Item, any> = useCallback(
@@ -262,25 +360,24 @@ export const Resizable = {
262
360
  );
263
361
 
264
362
  return (
265
- <div ref={containerRef} className='fixed inset-0 overflow-auto'>
266
- <Table<Item>
267
- rowsSelectable='multi'
268
- keyAccessor={(row) => row.publicKey.toHex()}
269
- columns={makeColumns(onUpdate)}
270
- data={items}
271
- fullWidth
272
- stickyHeader
273
- getScrollElement={() => containerRef.current}
274
- // onColumnResize={handleColumnResize}
275
- />
276
- </div>
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>
277
375
  );
278
376
  },
279
377
  };
280
378
 
281
379
  export const TenThousandRows = {
282
380
  render: () => {
283
- const containerRef = useRef<HTMLDivElement | null>(null);
284
381
  const [items, setItems] = useState<Item[]>(createItems(10000));
285
382
 
286
383
  const onUpdate: ValueUpdater<Item, any> = useCallback(
@@ -291,19 +388,123 @@ export const TenThousandRows = {
291
388
  const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
292
389
 
293
390
  return (
294
- <div ref={containerRef} className='fixed inset-0 overflow-auto'>
295
- <Table<Item>
296
- role='grid'
297
- rowsSelectable='multi'
298
- keyAccessor={(row) => row.publicKey.toHex()}
299
- columns={columns}
300
- data={items}
301
- fullWidth
302
- stickyHeader
303
- border
304
- getScrollElement={() => containerRef.current}
305
- />
306
- </div>
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>
307
508
  );
308
509
  },
309
510
  };