@dxos/react-ui-table 0.6.14-staging.e15392e → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/dist/lib/browser/_deprecated/index.mjs +1483 -0
  2. package/dist/lib/browser/_deprecated/index.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-A5W4XWPD.mjs +18 -0
  4. package/dist/lib/browser/chunk-A5W4XWPD.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +1275 -1356
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/types/index.mjs +7 -0
  9. package/dist/lib/browser/types/index.mjs.map +7 -0
  10. package/dist/lib/node/_deprecated/index.cjs +1511 -0
  11. package/dist/lib/node/_deprecated/index.cjs.map +7 -0
  12. package/dist/lib/node/chunk-32IGMRME.cjs +40 -0
  13. package/dist/lib/node/chunk-32IGMRME.cjs.map +7 -0
  14. package/dist/lib/node/index.cjs +1381 -0
  15. package/dist/lib/node/index.cjs.map +7 -0
  16. package/dist/lib/node/meta.json +1 -0
  17. package/dist/lib/node/types/index.cjs +29 -0
  18. package/dist/lib/node/types/index.cjs.map +7 -0
  19. package/dist/lib/node-esm/_deprecated/index.mjs +1485 -0
  20. package/dist/lib/node-esm/_deprecated/index.mjs.map +7 -0
  21. package/dist/lib/node-esm/chunk-DWQ4KGLD.mjs +20 -0
  22. package/dist/lib/node-esm/chunk-DWQ4KGLD.mjs.map +7 -0
  23. package/dist/lib/node-esm/index.mjs +1400 -0
  24. package/dist/lib/node-esm/index.mjs.map +7 -0
  25. package/dist/lib/node-esm/meta.json +1 -0
  26. package/dist/lib/node-esm/types/index.mjs +8 -0
  27. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  28. package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +1 -0
  29. package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +1 -0
  30. package/dist/types/src/_deprecated/components/Cell/index.d.ts.map +1 -0
  31. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +1 -0
  32. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -0
  33. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +1 -0
  34. package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.stories.d.ts +4 -4
  35. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -0
  36. package/dist/types/src/_deprecated/components/ColumnMenu/index.d.ts.map +1 -0
  37. package/dist/types/src/_deprecated/components/Table/Table.d.ts +20 -0
  38. package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +1 -0
  39. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts +116 -0
  40. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +1 -0
  41. package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +1 -0
  42. package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +1 -0
  43. package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +1 -0
  44. package/dist/types/src/_deprecated/components/Table/TableRootContext.d.ts.map +1 -0
  45. package/dist/types/src/_deprecated/components/Table/index.d.ts +5 -0
  46. package/dist/types/src/_deprecated/components/Table/index.d.ts.map +1 -0
  47. package/dist/types/src/_deprecated/components/Table/props.d.ts.map +1 -0
  48. package/dist/types/src/_deprecated/components/index.d.ts +4 -0
  49. package/dist/types/src/_deprecated/components/index.d.ts.map +1 -0
  50. package/dist/types/src/_deprecated/helpers.d.ts.map +1 -0
  51. package/dist/types/src/_deprecated/hooks/index.d.ts +5 -0
  52. package/dist/types/src/_deprecated/hooks/index.d.ts.map +1 -0
  53. package/dist/types/src/_deprecated/hooks/useColumnSizing.d.ts.map +1 -0
  54. package/dist/types/src/{hooks → _deprecated/hooks}/useColumnSorting.d.ts +1 -1
  55. package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts.map +1 -0
  56. package/dist/types/src/_deprecated/hooks/usePinLastRow.d.ts.map +1 -0
  57. package/dist/types/src/_deprecated/hooks/useRowSelection.d.ts.map +1 -0
  58. package/dist/types/src/_deprecated/index.d.ts +8 -0
  59. package/dist/types/src/_deprecated/index.d.ts.map +1 -0
  60. package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +1 -0
  61. package/dist/types/src/{schema → _deprecated/schema}/column-utils.d.ts +3 -0
  62. package/dist/types/src/_deprecated/schema/column-utils.d.ts.map +1 -0
  63. package/dist/types/src/_deprecated/schema/index.d.ts.map +1 -0
  64. package/dist/types/src/{schema → _deprecated/schema}/types.d.ts +3 -2
  65. package/dist/types/src/_deprecated/schema/types.d.ts.map +1 -0
  66. package/dist/types/src/_deprecated/theme.d.ts.map +1 -0
  67. package/dist/types/src/_deprecated/translations.d.ts +21 -0
  68. package/dist/types/src/_deprecated/translations.d.ts.map +1 -0
  69. package/dist/types/src/_deprecated/types.d.ts.map +1 -0
  70. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +8 -0
  71. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -0
  72. package/dist/types/src/components/Table/ColumnSettings.d.ts +8 -0
  73. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -0
  74. package/dist/types/src/components/Table/RowActionsMenu.d.ts +8 -0
  75. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -0
  76. package/dist/types/src/components/Table/Table.d.ts +11 -14
  77. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  78. package/dist/types/src/components/Table/Table.stories.d.ts +10 -113
  79. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  80. package/dist/types/src/components/Table/index.d.ts +0 -3
  81. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  82. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +17 -0
  83. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -0
  84. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +11 -0
  85. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -0
  86. package/dist/types/src/components/TableCellEditor/extension.d.ts +12 -0
  87. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -0
  88. package/dist/types/src/components/TableCellEditor/index.d.ts +2 -0
  89. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -0
  90. package/dist/types/src/components/Toolbar/Toolbar.d.ts +21 -0
  91. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  92. package/dist/types/src/components/Toolbar/common.d.ts +20 -0
  93. package/dist/types/src/components/Toolbar/common.d.ts.map +1 -0
  94. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  95. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  96. package/dist/types/src/components/index.d.ts +1 -2
  97. package/dist/types/src/components/index.d.ts.map +1 -1
  98. package/dist/types/src/components/testing.d.ts +97 -0
  99. package/dist/types/src/components/testing.d.ts.map +1 -0
  100. package/dist/types/src/hooks/index.d.ts +1 -4
  101. package/dist/types/src/hooks/index.d.ts.map +1 -1
  102. package/dist/types/src/hooks/useTableModel.d.ts +15 -0
  103. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -0
  104. package/dist/types/src/index.d.ts +3 -4
  105. package/dist/types/src/index.d.ts.map +1 -1
  106. package/dist/types/src/model/index.d.ts +3 -0
  107. package/dist/types/src/model/index.d.ts.map +1 -0
  108. package/dist/types/src/model/modal-controller.d.ts +29 -0
  109. package/dist/types/src/model/modal-controller.d.ts.map +1 -0
  110. package/dist/types/src/model/selection-model.d.ts +22 -0
  111. package/dist/types/src/model/selection-model.d.ts.map +1 -0
  112. package/dist/types/src/model/table-model.d.ts +93 -0
  113. package/dist/types/src/model/table-model.d.ts.map +1 -0
  114. package/dist/types/src/model/table-model.test.d.ts +2 -0
  115. package/dist/types/src/model/table-model.test.d.ts.map +1 -0
  116. package/dist/types/src/translations.d.ts +23 -16
  117. package/dist/types/src/translations.d.ts.map +1 -1
  118. package/dist/types/src/types/index.d.ts +2 -0
  119. package/dist/types/src/types/index.d.ts.map +1 -0
  120. package/dist/types/src/types/table.d.ts +106 -0
  121. package/dist/types/src/types/table.d.ts.map +1 -0
  122. package/dist/types/src/util/grid.d.ts +14 -0
  123. package/dist/types/src/util/grid.d.ts.map +1 -0
  124. package/dist/types/src/util/index.d.ts +5 -0
  125. package/dist/types/src/util/index.d.ts.map +1 -0
  126. package/dist/types/src/util/signals.d.ts +6 -0
  127. package/dist/types/src/util/signals.d.ts.map +1 -0
  128. package/dist/types/src/util/table-buttons.d.ts +24 -0
  129. package/dist/types/src/util/table-buttons.d.ts.map +1 -0
  130. package/dist/types/src/util/table-controls.d.ts +21 -0
  131. package/dist/types/src/util/table-controls.d.ts.map +1 -0
  132. package/dist/types/src/util/table.d.ts +8 -0
  133. package/dist/types/src/util/table.d.ts.map +1 -0
  134. package/package.json +56 -22
  135. package/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.stories.tsx +4 -4
  136. package/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.stories.tsx +9 -9
  137. package/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.tsx +3 -3
  138. package/src/_deprecated/components/Table/Table.stories.tsx +508 -0
  139. package/src/_deprecated/components/Table/Table.tsx +301 -0
  140. package/src/{components → _deprecated/components}/Table/TableHead.tsx +3 -1
  141. package/src/_deprecated/components/Table/index.ts +8 -0
  142. package/src/_deprecated/components/index.ts +7 -0
  143. package/src/{helpers.tsx → _deprecated/helpers.tsx} +4 -3
  144. package/src/_deprecated/hooks/index.ts +8 -0
  145. package/src/_deprecated/index.ts +12 -0
  146. package/src/_deprecated/schema/column-utils.ts +54 -0
  147. package/src/{schema → _deprecated/schema}/types.ts +3 -2
  148. package/src/_deprecated/translations.ts +26 -0
  149. package/src/components/Table/ColumnActionsMenu.tsx +60 -0
  150. package/src/components/Table/ColumnSettings.tsx +76 -0
  151. package/src/components/Table/RowActionsMenu.tsx +36 -0
  152. package/src/components/Table/Table.stories.tsx +197 -467
  153. package/src/components/Table/Table.tsx +172 -256
  154. package/src/components/Table/index.ts +1 -4
  155. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +114 -0
  156. package/src/components/TableCellEditor/TableCellEditor.tsx +155 -0
  157. package/src/components/TableCellEditor/extension.ts +62 -0
  158. package/src/components/TableCellEditor/index.ts +5 -0
  159. package/src/components/Toolbar/Toolbar.tsx +86 -0
  160. package/src/components/Toolbar/common.tsx +72 -0
  161. package/src/components/Toolbar/index.ts +5 -0
  162. package/src/components/index.ts +2 -3
  163. package/src/components/testing.ts +114 -0
  164. package/src/hooks/index.ts +1 -4
  165. package/src/hooks/useTableModel.ts +52 -0
  166. package/src/index.ts +5 -6
  167. package/src/model/index.ts +6 -0
  168. package/src/model/modal-controller.ts +83 -0
  169. package/src/model/selection-model.ts +99 -0
  170. package/src/model/table-model.test.ts +182 -0
  171. package/src/model/table-model.ts +565 -0
  172. package/src/translations.ts +20 -13
  173. package/src/types/index.ts +5 -0
  174. package/src/types/table.ts +13 -0
  175. package/src/util/grid.ts +23 -0
  176. package/src/util/index.ts +8 -0
  177. package/src/util/signals.ts +9 -0
  178. package/src/util/table-buttons.ts +48 -0
  179. package/src/util/table-controls.ts +27 -0
  180. package/src/util/table.ts +77 -0
  181. package/dist/types/src/components/Cell/Cell.d.ts.map +0 -1
  182. package/dist/types/src/components/Cell/CellCombobox.d.ts.map +0 -1
  183. package/dist/types/src/components/Cell/index.d.ts.map +0 -1
  184. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +0 -1
  185. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +0 -1
  186. package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts.map +0 -1
  187. package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts.map +0 -1
  188. package/dist/types/src/components/ColumnMenu/index.d.ts.map +0 -1
  189. package/dist/types/src/components/Table/TableBody.d.ts.map +0 -1
  190. package/dist/types/src/components/Table/TableContext.d.ts.map +0 -1
  191. package/dist/types/src/components/Table/TableHead.d.ts.map +0 -1
  192. package/dist/types/src/components/Table/TableRootContext.d.ts.map +0 -1
  193. package/dist/types/src/components/Table/props.d.ts.map +0 -1
  194. package/dist/types/src/helpers.d.ts.map +0 -1
  195. package/dist/types/src/hooks/useColumnSizing.d.ts.map +0 -1
  196. package/dist/types/src/hooks/useColumnSorting.d.ts.map +0 -1
  197. package/dist/types/src/hooks/usePinLastRow.d.ts.map +0 -1
  198. package/dist/types/src/hooks/useRowSelection.d.ts.map +0 -1
  199. package/dist/types/src/schema/Schema.stories.d.ts.map +0 -1
  200. package/dist/types/src/schema/column-utils.d.ts.map +0 -1
  201. package/dist/types/src/schema/index.d.ts.map +0 -1
  202. package/dist/types/src/schema/types.d.ts.map +0 -1
  203. package/dist/types/src/theme.d.ts.map +0 -1
  204. package/dist/types/src/types.d.ts.map +0 -1
  205. package/src/schema/column-utils.ts +0 -48
  206. /package/dist/types/src/{components → _deprecated/components}/Cell/Cell.d.ts +0 -0
  207. /package/dist/types/src/{components → _deprecated/components}/Cell/CellCombobox.d.ts +0 -0
  208. /package/dist/types/src/{components → _deprecated/components}/Cell/index.d.ts +0 -0
  209. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.d.ts +0 -0
  210. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.stories.d.ts +0 -0
  211. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.d.ts +0 -0
  212. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/index.d.ts +0 -0
  213. /package/dist/types/src/{components → _deprecated/components}/Table/TableBody.d.ts +0 -0
  214. /package/dist/types/src/{components → _deprecated/components}/Table/TableContext.d.ts +0 -0
  215. /package/dist/types/src/{components → _deprecated/components}/Table/TableHead.d.ts +0 -0
  216. /package/dist/types/src/{components → _deprecated/components}/Table/TableRootContext.d.ts +0 -0
  217. /package/dist/types/src/{components → _deprecated/components}/Table/props.d.ts +0 -0
  218. /package/dist/types/src/{helpers.d.ts → _deprecated/helpers.d.ts} +0 -0
  219. /package/dist/types/src/{hooks → _deprecated/hooks}/useColumnSizing.d.ts +0 -0
  220. /package/dist/types/src/{hooks → _deprecated/hooks}/usePinLastRow.d.ts +0 -0
  221. /package/dist/types/src/{hooks → _deprecated/hooks}/useRowSelection.d.ts +0 -0
  222. /package/dist/types/src/{schema → _deprecated/schema}/Schema.stories.d.ts +0 -0
  223. /package/dist/types/src/{schema → _deprecated/schema}/index.d.ts +0 -0
  224. /package/dist/types/src/{theme.d.ts → _deprecated/theme.d.ts} +0 -0
  225. /package/dist/types/src/{types.d.ts → _deprecated/types.d.ts} +0 -0
  226. /package/src/{components → _deprecated/components}/Cell/Cell.tsx +0 -0
  227. /package/src/{components → _deprecated/components}/Cell/CellCombobox.tsx +0 -0
  228. /package/src/{components → _deprecated/components}/Cell/index.ts +0 -0
  229. /package/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.tsx +0 -0
  230. /package/src/{components → _deprecated/components}/ColumnMenu/index.ts +0 -0
  231. /package/src/{components → _deprecated/components}/Table/TableBody.tsx +0 -0
  232. /package/src/{components → _deprecated/components}/Table/TableContext.tsx +0 -0
  233. /package/src/{components → _deprecated/components}/Table/TableRootContext.ts +0 -0
  234. /package/src/{components → _deprecated/components}/Table/props.ts +0 -0
  235. /package/src/{hooks → _deprecated/hooks}/useColumnSizing.ts +0 -0
  236. /package/src/{hooks → _deprecated/hooks}/useColumnSorting.ts +0 -0
  237. /package/src/{hooks → _deprecated/hooks}/usePinLastRow.ts +0 -0
  238. /package/src/{hooks → _deprecated/hooks}/useRowSelection.ts +0 -0
  239. /package/src/{schema → _deprecated/schema}/Schema.stories.tsx +0 -0
  240. /package/src/{schema → _deprecated/schema}/index.ts +0 -0
  241. /package/src/{theme.ts → _deprecated/theme.ts} +0 -0
  242. /package/src/{types.ts → _deprecated/types.ts} +0 -0
@@ -1,508 +1,238 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { Plugs, PlugsConnected } from '@phosphor-icons/react';
8
- import { type StoryObj } from '@storybook/react';
9
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
9
 
11
- import { create } from '@dxos/echo-schema';
12
- import { PublicKey } from '@dxos/keys';
13
- import { log } from '@dxos/log';
10
+ import { type MutableSchema } from '@dxos/echo-schema';
11
+ import { invariant } from '@dxos/invariant';
12
+ import { useGlobalFilteredObjects } from '@dxos/plugin-search';
14
13
  import { faker } from '@dxos/random';
15
- import { Button } from '@dxos/react-ui';
16
- import { withSignals, withTheme } from '@dxos/storybook-utils';
17
- import { range } from '@dxos/util';
18
-
19
- import { Table } from './Table';
20
- import { createColumnBuilder, type SearchListQueryModel, type ValueUpdater } from '../../helpers';
21
- import { type TableColumnDef } from '../../types';
22
-
23
- // TODO(burdon): Header menu builder.
24
- // TODO(burdon): Expand width if not all columns have explicit size.
25
-
26
- type Item = {
27
- publicKey: PublicKey;
28
- name: string;
29
- company?: string;
30
- count?: number;
31
- started?: Date;
32
- complete?: boolean;
33
- };
34
-
35
- faker.seed(911);
36
-
37
- const createItems = (count: number) =>
38
- range(count).map(
39
- () =>
40
- create<Item>({
41
- publicKey: PublicKey.random(),
42
- name: faker.commerce.productName(),
43
- count: faker.datatype.boolean({ probability: 0.9 }) ? faker.number.int({ min: 0, max: 10_000 }) : undefined,
44
- started: faker.date.recent(),
45
- complete: faker.datatype.boolean() ? true : faker.datatype.boolean() ? false : undefined,
46
- }) as Item,
47
- );
48
-
49
- const updateItems = <TValue = any,>(items: Item[], key: PublicKey, id: string, value: TValue) => {
50
- return items.map((item) => (item.publicKey.equals(key) ? { ...item, [id]: value } : item));
51
- };
52
-
53
- const tableStorySelectItems: Record<string, Item> = range(128).reduce((acc: Record<string, Item>, _i) => {
54
- const id = PublicKey.random();
55
- acc[id.toHex()] = {
56
- publicKey: id,
57
- name: faker.company.name(),
58
- };
59
- return acc;
60
- }, {});
61
-
62
- const timeout = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
63
-
64
- const tableStorySelectModel: SearchListQueryModel<Item> = {
65
- getId: (object) => object?.publicKey?.toHex() ?? 'never',
66
- getText: (object) => object?.name ?? 'never',
67
- query: async (search: string) => {
68
- await timeout(400);
69
- return Object.values(tableStorySelectItems).filter((item) => {
70
- return item.name.toLowerCase().includes(search.toLowerCase());
71
- });
72
- },
73
- };
74
-
75
- const { helper, builder } = createColumnBuilder<Item>();
76
-
77
- const makeColumns = (onUpdate?: ValueUpdater<Item, any>): TableColumnDef<Item, any>[] => [
78
- helper.display(builder.selectRow()),
79
- helper.accessor((item) => item.publicKey, { id: 'key', ...builder.key({ tooltip: true }) }),
80
- helper.accessor(
81
- 'name',
82
- builder.string({
83
- label: 'Name',
84
- onUpdate,
85
- meta: { expand: true },
86
- footer: (props) => props.table.getRowModel().rows.length,
87
- }),
88
- ),
89
- helper.accessor('started', builder.date({ label: 'Started', relative: true, meta: { resizable: true } })),
90
- helper.accessor(
91
- 'count',
92
- builder.number({
93
- label: 'Count',
94
- meta: { resizable: true },
95
- onUpdate,
96
- getGroupingValue: (row) => (row.count ? (row.count < 2000 ? 'A' : row.count < 5000 ? 'B' : 'C') : 'D'),
97
- }),
98
- ),
99
- helper.accessor('company', builder.combobox({ label: 'Company', model: tableStorySelectModel, onUpdate })),
100
- helper.accessor(
101
- 'complete',
102
- builder.switch({ getGroupingValue: (row) => row.complete === true, label: '', onUpdate }),
103
- ),
104
- helper.accessor('complete', builder.icon({ id: 'done', label: '' })),
105
- helper.accessor(
106
- 'complete',
107
- builder.icon({
108
- id: 'connected',
109
- label: '',
110
- on: { Icon: PlugsConnected, className: 'text-blue-500' },
111
- off: { Icon: Plugs, className: 'text-blue-200' },
112
- }),
113
- ),
114
- ];
14
+ import { Filter, useSpaces, useQuery, create } from '@dxos/react-client/echo';
15
+ import { withClientProvider } from '@dxos/react-client/testing';
16
+ import { useDefaultValue } from '@dxos/react-ui';
17
+ import { ViewEditor } from '@dxos/react-ui-data';
18
+ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
+ import { ViewProjection, ViewType } from '@dxos/schema';
20
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
21
+
22
+ import { Table, type TableController } from './Table';
23
+ import { useTableModel, type UseTableModelParams } from '../../hooks';
24
+ import translations from '../../translations';
25
+ import { TableType } from '../../types';
26
+ import { initializeTable } from '../../util';
27
+ import { Toolbar } from '../Toolbar';
28
+ import { createItems, createTable, type SimulatorProps, useSimulator } from '../testing';
115
29
 
116
30
  //
117
- // Tests
31
+ // Story components.
118
32
  //
119
33
 
120
- const MinimalTable = (props: any) => {
121
- return (
122
- <Table.Root>
123
- <Table.Viewport classNames='fixed inset-0'>
124
- <Table.Main<any> {...props} />
125
- </Table.Viewport>
126
- </Table.Root>
127
- );
128
- };
129
-
130
- export default {
131
- title: 'ui/react-ui-table/Table',
132
- component: MinimalTable,
133
- args: {
134
- header: true,
135
- keyAccessor: (item: Item) => item.publicKey.toHex(),
136
- },
137
- argTypes: {
138
- header: { control: 'boolean' },
139
- footer: { control: 'boolean' },
140
- border: { control: 'boolean' },
141
- fullWidth: { control: 'boolean' },
142
- grouping: {
143
- control: 'select',
144
- options: ['none', 'complete', 'count'],
145
- mapping: {
146
- none: undefined,
147
- complete: ['complete'],
148
- count: ['count'],
149
- },
150
- },
151
- columnVisibility: {
152
- control: 'select',
153
- options: ['all', 'limited'],
154
- mapping: {
155
- all: undefined,
156
- limited: { key: false, started: false },
157
- },
158
- },
159
- rowsSelectable: { control: 'select', options: [false, true, 'multi'] },
160
- },
161
- decorators: [withTheme],
162
- };
163
-
164
- export const Default = {
165
- args: {
166
- columns: makeColumns(),
167
- data: createItems(20),
168
- },
169
- };
170
-
171
- export const Scrolling = {
172
- args: {
173
- columns: makeColumns(),
174
- data: createItems(200),
175
- },
176
- };
177
-
178
- export const Visibility = {
179
- args: {
180
- columns: makeColumns(),
181
- data: createItems(10),
182
- columnVisibility: { key: false, started: false },
183
- },
184
- };
185
-
186
- export const Empty = {
187
- args: {
188
- columns: makeColumns(),
189
- },
190
- };
191
-
192
- export const Dynamic = {
193
- render: () => {
194
- const [items, setItems] = useState<Item[]>(createItems(50));
195
-
196
- useEffect(() => {
197
- const interval = setInterval(() => {
198
- setItems((items) => {
199
- if (items.length >= 200) {
200
- clearInterval(interval);
201
- }
202
- return [...items, ...createItems(1)];
203
- });
204
- }, 500);
205
- return () => clearInterval(interval);
206
- }, []);
207
-
208
- const columns = useMemo(() => makeColumns(), []);
209
-
210
- return (
211
- <Table.Root>
212
- <Table.Viewport classNames='fixed inset-0'>
213
- <Table.Main<Item>
214
- role='grid'
215
- rowsSelectable='multi'
216
- keyAccessor={(row) => row.publicKey.toHex()}
217
- columns={columns}
218
- data={items}
219
- fullWidth
220
- stickyHeader
221
- border
222
- pinLastRow
223
- />
224
- </Table.Viewport>
225
- </Table.Root>
226
- );
227
- },
228
- };
229
-
230
- export const Editable = {
231
- render: () => {
232
- const [items, setItems] = useState<Item[]>(createItems(200));
233
-
234
- const onUpdate: ValueUpdater<Item, any> = useCallback(
235
- (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
236
- [setItems],
237
- );
238
-
239
- const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
240
-
241
- return (
242
- <Table.Root>
243
- <Table.Viewport classNames='fixed inset-0'>
244
- <Table.Main<Item>
245
- role='grid'
246
- rowsSelectable='multi'
247
- keyAccessor={(row) => row.publicKey.toHex()}
248
- columns={columns}
249
- data={items}
250
- fullWidth
251
- stickyHeader
252
- border
253
- pinLastRow
254
- />
255
- </Table.Viewport>
256
- </Table.Root>
257
- );
258
- },
259
- };
260
-
261
- export const PinnedLastRow = {
262
- render: () => {
263
- const [items, setItems] = useState<Item[]>(createItems(200));
264
-
265
- const onUpdate: ValueUpdater<Item, any> = useCallback(
266
- (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
267
- [setItems],
268
- );
269
-
270
- const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
271
-
272
- return (
273
- <Table.Root>
274
- <Table.Viewport classNames='fixed inset-0'>
275
- <Table.Main<Item>
276
- role='grid'
277
- rowsSelectable='multi'
278
- keyAccessor={(row) => row.publicKey.toHex()}
279
- columns={columns}
280
- data={items}
281
- fullWidth
282
- stickyHeader
283
- border
284
- pinLastRow
285
- />
286
- </Table.Viewport>
287
- </Table.Root>
288
- );
289
- },
290
- };
34
+ const DefaultStory = () => {
35
+ const spaces = useSpaces();
36
+ const space = spaces[spaces.length - 1];
37
+ const tables = useQuery(space, Filter.schema(TableType));
38
+ const [table, setTable] = useState<TableType>();
39
+ const [schema, setSchema] = useState<MutableSchema>();
40
+ useEffect(() => {
41
+ if (tables.length && !table) {
42
+ const table = tables[0];
43
+ invariant(table.view);
44
+ setTable(table);
45
+ setSchema(space.db.schemaRegistry.getSchema(table.view.query.type));
46
+ }
47
+ }, [tables]);
291
48
 
292
- export const InsertDelete = {
293
- render: () => {
294
- const [items, setItems] = useState<Item[]>(createItems(10));
49
+ const projection = useMemo(() => {
50
+ if (schema && table?.view) {
51
+ return new ViewProjection(schema, table.view);
52
+ }
53
+ }, [schema, table?.view]);
295
54
 
296
- const onUpdate: ValueUpdater<Item, any> = useCallback(
297
- (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
298
- [setItems],
299
- );
55
+ const objects = useQuery(space, schema ? Filter.schema(schema) : () => false, undefined, [schema]);
56
+ const filteredObjects = useGlobalFilteredObjects(objects);
300
57
 
301
- const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
58
+ const handleInsertRow = useCallback(() => {
59
+ if (space && schema) {
60
+ space.db.add(create(schema, {}));
61
+ }
62
+ }, [space, schema]);
302
63
 
303
- const onInsertFirst = () => {
304
- setItems((items) => [...createItems(1), ...items]);
305
- };
64
+ const handleDeleteRows = useCallback(
65
+ (_: number, objects: any[]) => {
66
+ for (const object of objects) {
67
+ space.db.remove(object);
68
+ }
69
+ },
70
+ [space],
71
+ );
306
72
 
307
- const onInsertLast = () => {
308
- setItems((items) => [...items, ...createItems(1)]);
309
- };
73
+ const handleDeleteColumn = useCallback(
74
+ (fieldId: string) => {
75
+ if (projection) {
76
+ projection.deleteFieldProjection(fieldId);
77
+ }
78
+ },
79
+ [table, projection],
80
+ );
310
81
 
311
- const onDeleteFirst = () => {
312
- setItems(([_first, ...rest]) => rest);
313
- };
82
+ const handleAction = useCallback(
83
+ (action: { type: string }) => {
84
+ switch (action.type) {
85
+ case 'on-thread-create': {
86
+ console.log('Thread creation triggered');
87
+ break;
88
+ }
89
+ case 'add-row': {
90
+ handleInsertRow();
91
+ }
92
+ }
93
+ },
94
+ [table, spaces],
95
+ );
314
96
 
315
- const onDeleteLast = () => {
316
- setItems((items) => [...items.slice(0, items.length - 1)]);
317
- };
97
+ const tableRef = useRef<TableController>(null);
98
+ const model = useTableModel({
99
+ table,
100
+ projection,
101
+ objects: filteredObjects,
102
+ onInsertRow: handleInsertRow,
103
+ onDeleteRows: handleDeleteRows,
104
+ onDeleteColumn: handleDeleteColumn,
105
+ onCellUpdate: (cell) => tableRef.current?.update?.(cell),
106
+ onRowOrderChanged: () => tableRef.current?.update?.(),
107
+ });
108
+
109
+ if (!schema || !table) {
110
+ return <div />;
111
+ }
318
112
 
319
- return (
320
- <div className='space-y-4'>
321
- <div className='flex flex-row gap-2'>
322
- <Button onClick={onInsertFirst}>Insert first</Button>
323
- <Button onClick={onInsertLast}>Insert last</Button>
324
- <Button onClick={onDeleteFirst}>Delete first</Button>
325
- <Button onClick={onDeleteLast}>Delete last</Button>
326
- </div>
113
+ return (
114
+ <div className='grow grid grid-cols-[1fr_350px]'>
115
+ <div className='flex flex-col h-full overflow-hidden'>
116
+ <Toolbar.Root classNames='border-b border-separator' onAction={handleAction}>
117
+ <Toolbar.Editing />
118
+ <Toolbar.Separator />
119
+ <Toolbar.Actions />
120
+ </Toolbar.Root>
327
121
  <Table.Root>
328
- <Table.Viewport>
329
- <Table.Main<Item>
330
- role='grid'
331
- rowsSelectable='multi'
332
- keyAccessor={(row) => row.publicKey.toHex()}
333
- columns={columns}
334
- data={items}
335
- fullWidth
336
- stickyHeader
337
- border
338
- pinLastRow
339
- />
340
- </Table.Viewport>
122
+ <Table.Main ref={tableRef} model={model} />
341
123
  </Table.Root>
342
124
  </div>
343
- );
344
- },
345
- };
346
-
347
- export const Resizable = {
348
- render: () => {
349
- const [items, setItems] = useState<Item[]>(createItems(10));
350
-
351
- const onUpdate: ValueUpdater<Item, any> = useCallback(
352
- (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
353
- [setItems],
354
- );
355
-
356
- return (
357
- <Table.Root>
358
- <Table.Viewport classNames='fixed inset-0'>
359
- <Table.Main<Item>
360
- rowsSelectable='multi'
361
- keyAccessor={(row) => row.publicKey.toHex()}
362
- columns={makeColumns(onUpdate)}
363
- data={items}
364
- fullWidth
365
- stickyHeader
125
+ <div className='flex flex-col h-full border-l border-separator overflow-y-auto'>
126
+ {table.view && (
127
+ <ViewEditor
128
+ registry={space?.db.schemaRegistry}
129
+ schema={schema}
130
+ view={table.view}
131
+ onDelete={handleDeleteColumn}
366
132
  />
367
- </Table.Viewport>
368
- </Table.Root>
369
- );
370
- },
371
- };
372
-
373
- export const TenThousandRows = {
374
- render: () => {
375
- const [items, setItems] = useState<Item[]>(createItems(10000));
133
+ )}
376
134
 
377
- const onUpdate: ValueUpdater<Item, any> = useCallback(
378
- (item, prop, value) => setItems((items) => updateItems(items, item.publicKey, prop, value)),
379
- [setItems],
380
- );
381
-
382
- const columns = useMemo(() => makeColumns(onUpdate), [onUpdate]);
383
-
384
- return (
385
- <Table.Root>
386
- <Table.Viewport classNames='fixed inset-0'>
387
- <Table.Main<Item>
388
- role='grid'
389
- rowsSelectable='multi'
390
- keyAccessor={(row) => row.publicKey.toHex()}
391
- columns={columns}
392
- data={items}
393
- fullWidth
394
- stickyHeader
395
- border
396
- />{' '}
397
- </Table.Viewport>
398
- </Table.Root>
399
- );
400
- },
135
+ <SyntaxHighlighter language='json' className='w-full text-xs'>
136
+ {JSON.stringify({ view: table.view, schema }, null, 2)}
137
+ </SyntaxHighlighter>
138
+ </div>
139
+ </div>
140
+ );
401
141
  };
402
142
 
403
- // TODO(burdon): Warning: Encountered two children with the same key.
404
-
405
143
  type StoryProps = {
406
- periodicMutations: boolean;
407
- mutationInterval: number;
408
- periodicDeletions: boolean;
409
- deletionInterval: number;
410
- periodicInsertions: boolean;
411
- insertionInterval: number;
412
- };
413
-
414
- const DefaultStory = ({
415
- periodicMutations,
416
- mutationInterval,
417
- periodicDeletions,
418
- deletionInterval,
419
- periodicInsertions,
420
- insertionInterval,
421
- }: StoryProps) => {
422
- const [state] = useState(() => create({ items: createItems(10) }));
423
-
424
- useEffect(() => {
425
- if (!periodicMutations) {
426
- return;
427
- }
428
-
429
- const i = setInterval(() => {
430
- if (state.items.length === 0) {
431
- return;
144
+ rows?: number;
145
+ } & Pick<SimulatorProps, 'insertInterval' | 'updateInterval'>;
146
+
147
+ const TablePerformanceStory = (props: StoryProps) => {
148
+ const getDefaultRows = useCallback(() => 10, []);
149
+ const rows = useDefaultValue(props.rows, getDefaultRows);
150
+ const table = useMemo(() => createTable(), []);
151
+ const items = useMemo(() => createItems(rows), [rows]);
152
+ const itemsRef = useRef(items);
153
+ const simulatorProps = useMemo(() => ({ table, items, ...props }), [table, items, props]);
154
+ useSimulator(simulatorProps);
155
+
156
+ const handleDeleteRows = useCallback<NonNullable<UseTableModelParams<any>['onDeleteRows']>>((row) => {
157
+ itemsRef.current.splice(row, 1);
158
+ }, []);
159
+
160
+ const handleDeleteColumn = useCallback<NonNullable<UseTableModelParams<any>['onDeleteColumn']>>(
161
+ (fieldId) => {
162
+ if (table && table.view) {
163
+ const fieldPosition = table.view.fields.findIndex((field) => field.id === fieldId);
164
+ table.view.fields.splice(fieldPosition, 1);
432
165
  }
166
+ },
167
+ [table],
168
+ );
433
169
 
434
- const idx = Math.floor(Math.random() * state.items.length);
435
- log('mutating', { idx });
436
- state.items[idx].name = faker.commerce.productName();
437
- state.items[idx].count = Math.floor(Math.random() * 1000);
438
- state.items[idx].started = new Date();
439
- }, mutationInterval);
440
-
441
- return () => clearInterval(i);
442
- }, [periodicMutations, mutationInterval, state.items]);
443
-
444
- useEffect(() => {
445
- if (!periodicInsertions) {
446
- return;
447
- }
448
-
449
- const i = setInterval(() => {
450
- log('inserting...');
451
- state.items.push(createItems(1)[0]);
452
- }, insertionInterval);
170
+ const tableRef = useRef<TableController>(null);
171
+ const model = useTableModel({
172
+ table,
173
+ objects: items as any[],
174
+ onDeleteRows: handleDeleteRows,
175
+ onDeleteColumn: handleDeleteColumn,
176
+ onCellUpdate: (cell) => tableRef.current?.update?.(cell),
177
+ onRowOrderChanged: () => tableRef.current?.update?.(),
178
+ });
453
179
 
454
- return () => clearInterval(i);
455
- }, [periodicInsertions, insertionInterval, state.items]);
180
+ return (
181
+ <Table.Root>
182
+ <Table.Main ref={tableRef} model={model} />
183
+ </Table.Root>
184
+ );
185
+ };
456
186
 
457
- useEffect(() => {
458
- if (!periodicDeletions) {
459
- return;
460
- }
187
+ //
188
+ // Story definitions.
189
+ //
461
190
 
462
- const i = setInterval(() => {
463
- if (state.items.length === 0) {
464
- return;
465
- }
191
+ const meta: Meta<StoryProps> = {
192
+ title: 'plugins/plugin-table/Table',
193
+ component: Table.Main as any,
194
+ render: DefaultStory,
195
+ parameters: { translations },
196
+ decorators: [
197
+ withClientProvider({
198
+ types: [TableType, ViewType],
199
+ createIdentity: true,
200
+ createSpace: true,
201
+ onSpaceCreated: async ({ space }) => {
202
+ const table = space.db.add(create(TableType, {}));
203
+ const schema = initializeTable({ space, table });
204
+ Array.from({ length: 30 }).map(() => {
205
+ return space.db.add(
206
+ create(schema, {
207
+ name: faker.person.fullName(),
208
+ }),
209
+ );
210
+ });
211
+ },
212
+ }),
213
+ withTheme,
214
+ withLayout({ fullscreen: true, tooltips: true }),
215
+ ],
216
+ };
466
217
 
467
- log('deleting...');
468
- const randomIndex = Math.floor(Math.random() * state.items.length);
469
- state.items.splice(randomIndex, 1);
470
- }, deletionInterval);
218
+ export default meta;
471
219
 
472
- return () => clearInterval(i);
473
- }, [periodicDeletions, deletionInterval, state.items]);
220
+ type Story = StoryObj<StoryProps>;
474
221
 
475
- const onUpdate = useCallback((...args: any[]) => {}, []);
476
- const columns = useMemo(() => makeColumns(onUpdate), []);
222
+ export const Default = {};
477
223
 
478
- return (
479
- <Table.Root>
480
- <Table.Viewport classNames='fixed inset-0'>
481
- <Table.Main<Item>
482
- role='grid'
483
- rowsSelectable='multi'
484
- keyAccessor={(row) => row.publicKey.toHex()}
485
- columns={columns}
486
- data={state.items}
487
- fullWidth
488
- stickyHeader
489
- border
490
- pinLastRow
491
- />
492
- </Table.Viewport>
493
- </Table.Root>
494
- );
224
+ export const Mutations: Story = {
225
+ render: TablePerformanceStory,
226
+ args: {
227
+ rows: 1000,
228
+ updateInterval: 1,
229
+ },
495
230
  };
496
231
 
497
- export const RealTimeUpdates: StoryObj<StoryProps> = {
498
- render: DefaultStory,
499
- decorators: [withSignals],
232
+ export const RapidInsertions: Story = {
233
+ render: TablePerformanceStory,
500
234
  args: {
501
- periodicMutations: true,
502
- mutationInterval: 1000,
503
- periodicDeletions: false,
504
- deletionInterval: 5000,
505
- periodicInsertions: false,
506
- insertionInterval: 3000,
235
+ rows: 0,
236
+ insertInterval: 100,
507
237
  },
508
238
  };