@dxos/react-ui-table 0.6.14-main.f49f251 → 0.6.14-staging.4b6f9cf

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 +1277 -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 +1383 -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 +1402 -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 +174 -257
  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,301 +1,218 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type Primitive } from '@radix-ui/react-primitive';
6
- import { Slot } from '@radix-ui/react-slot';
7
- import {
8
- type GroupingState,
9
- type Row,
10
- type RowData,
11
- getCoreRowModel,
12
- getExpandedRowModel,
13
- getGroupedRowModel,
14
- getSortedRowModel,
15
- useReactTable,
16
- } from '@tanstack/react-table';
17
5
  import React, {
18
- type ComponentPropsWithoutRef,
19
- type ReactNode,
20
- Fragment,
21
- memo,
6
+ forwardRef,
7
+ type PropsWithChildren,
22
8
  useCallback,
23
- useEffect,
9
+ useImperativeHandle,
24
10
  useState,
25
- useContext,
11
+ type WheelEvent,
26
12
  } from 'react';
27
13
 
28
- import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
14
+ import { getValue } from '@dxos/echo-schema';
15
+ import { invariant } from '@dxos/invariant';
16
+ import { Filter, getSpace, fullyQualifiedId } from '@dxos/react-client/echo';
17
+ import { useAttention } from '@dxos/react-ui-attention';
18
+ import { type DxGridElement, Grid, type GridContentProps, closestCell } from '@dxos/react-ui-grid';
19
+ import { StackItem } from '@dxos/react-ui-stack';
29
20
  import { mx } from '@dxos/react-ui-theme';
21
+ import { isNotFalsy } from '@dxos/util';
30
22
 
31
- import { TableBody } from './TableBody';
32
- import { type TypedTableProvider, TableProvider as UntypedTableProvider, useTableContext } from './TableContext';
33
- import { TableHead } from './TableHead';
34
- import { TableRootContext, useTableRootContext } from './TableRootContext';
35
- import { type TableProps } from './props';
36
- import { useColumnResizing, useColumnSorting, usePinLastRow, useRowSelection } from '../../hooks';
37
- import { groupTh, tableRoot } from '../../theme';
23
+ import { ColumnActionsMenu } from './ColumnActionsMenu';
24
+ import { ColumnSettings } from './ColumnSettings';
25
+ import { RowActionsMenu } from './RowActionsMenu';
26
+ import { type TableModel } from '../../model';
27
+ import { type GridCell } from '../../util';
28
+ import { TableCellEditor, type TableCellEditorProps } from '../TableCellEditor';
38
29
 
39
- type TableRootProps = { children: ReactNode };
30
+ // NOTE(Zan): These fragments add border to inline-end and block-end of the grid using pseudo-elements.
31
+ // These are offset by 1px to avoid double borders in planks.
32
+ const inlineEndLine =
33
+ '[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-separator';
34
+ const blockEndLine =
35
+ '[&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-separator';
40
36
 
41
- const TableRoot = ({ children }: TableRootProps) => {
42
- const contextValue = useTableRootContext();
43
- return <TableRootContext.Provider value={contextValue}>{children}</TableRootContext.Provider>;
44
- };
37
+ const frozen = { frozenRowsStart: 1, frozenColsStart: 1, frozenColsEnd: 1 };
45
38
 
46
- type TableViewportProps = ThemedClassName<ComponentPropsWithoutRef<typeof Primitive.div>> & {
47
- asChild?: boolean;
48
- };
39
+ //
40
+ // Root
41
+ //
49
42
 
50
- const TableViewport = ({ children, classNames, asChild, ...props }: TableViewportProps) => {
51
- const { scrollContextRef } = useContext(TableRootContext);
43
+ export type TableRootProps = PropsWithChildren<{ role?: string }>;
52
44
 
53
- const classes = mx(classNames, 'overflow-auto');
54
- return asChild ? (
55
- <Slot ref={scrollContextRef} className={classes} {...props}>
56
- {children}
57
- </Slot>
58
- ) : (
59
- <div role='none' className={classes} ref={scrollContextRef} {...props}>
45
+ const TableRoot = ({ children }: TableRootProps) => {
46
+ return (
47
+ <StackItem.Content toolbar contentSize='intrinsic' classNames='relative'>
60
48
  {children}
61
- </div>
49
+ </StackItem.Content>
62
50
  );
63
51
  };
64
52
 
65
- export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>) => {
66
- const {
67
- role,
68
- header = true,
69
- rowsSelectable,
70
- debug,
71
- pinLastRow,
72
- columnVisibility,
73
- onColumnResize,
74
- onDataSelectionChange,
75
- } = props;
76
-
77
- const columns = useDefaultValue(props.columns, () => []);
78
- const incomingData = useDefaultValue(props.data, () => []);
79
-
80
- const [data, setData] = useState([...incomingData]);
81
-
82
- // Reactivity workaround: https://github.com/dxos/dxos/issues/6376.
83
- const forceUpdate = useCallback(() => setData([...incomingData]), [incomingData]);
84
- useEffect(() => forceUpdate(), [JSON.stringify(incomingData), forceUpdate]);
85
-
86
- const TableProvider = UntypedTableProvider as TypedTableProvider<TData>;
87
-
88
- const { columnSizing, setColumnSizing, columnSizingInfo, setColumnSizingInfo } = useColumnResizing({
89
- columns,
90
- onColumnResize,
91
- });
53
+ //
54
+ // Main
55
+ //
92
56
 
93
- const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
57
+ export type TableController = {
58
+ update?: (cell?: GridCell) => void;
59
+ };
94
60
 
95
- const [grouping, handleGroupingChange] = useState<GroupingState>(props.grouping ?? []);
96
- useEffect(() => handleGroupingChange(props.grouping ?? []), [handleGroupingChange, props.grouping]);
61
+ export type TableMainProps = {
62
+ model?: TableModel;
63
+ };
97
64
 
98
- const columnSorting = useColumnSorting();
65
+ const TableMain = forwardRef<TableController, TableMainProps>(({ model }, forwardedRef) => {
66
+ const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
67
+
68
+ const { hasAttention } = useAttention(model?.table ? fullyQualifiedId(model.table) : 'table');
69
+
70
+ /**
71
+ * Provides an external controller that can be called to repaint the table.
72
+ */
73
+ useImperativeHandle<TableController, TableController>(
74
+ forwardedRef,
75
+ () => {
76
+ if (!model || !dxGrid) {
77
+ return {};
78
+ }
79
+
80
+ dxGrid.getCells = model.getCells.bind(model);
81
+ return {
82
+ update: (cell) => {
83
+ if (cell) {
84
+ dxGrid.updateIfWithinBounds(cell);
85
+ } else {
86
+ dxGrid.updateCells(true);
87
+ }
88
+ },
89
+ };
90
+ },
91
+ [model, dxGrid],
92
+ );
99
93
 
100
- const table = useReactTable({
101
- // Data
102
- meta: {},
103
- data,
94
+ const handleFocus: TableCellEditorProps['onFocus'] = (increment, delta) => {
95
+ dxGrid?.refocus(increment, delta);
96
+ };
104
97
 
105
- // Columns
106
- columns,
107
- defaultColumn: {
108
- size: 400, // Required in order remove default width.
109
- maxSize: 1024,
98
+ const handleEnter = useCallback<NonNullable<TableCellEditorProps['onEnter']>>(
99
+ (cell) => {
100
+ // TODO(burdon): Insert row only if bottom row isn't completely blank already.
101
+ if (model && cell.row === model.getRowCount() - 1) {
102
+ model.insertRow(cell.row);
103
+ }
110
104
  },
105
+ [model],
106
+ );
111
107
 
112
- // State
113
- state: {
114
- columnVisibility,
115
- columnSizing,
116
- columnSizingInfo,
117
- sorting: columnSorting.sorting,
118
- rowSelection,
119
- grouping,
108
+ const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
109
+ (event) => {
110
+ const cell = closestCell(event.target);
111
+ if (!model || !cell) {
112
+ return;
113
+ }
114
+
115
+ switch (event.key) {
116
+ case 'Backspace':
117
+ case 'Delete': {
118
+ model.setCellData(cell, undefined);
119
+ break;
120
+ }
121
+ }
120
122
  },
121
-
122
- // Resize columns
123
- columnResizeMode: 'onChange',
124
- enableColumnResizing: true,
125
- onColumnSizingChange: setColumnSizing,
126
- onColumnSizingInfoChange: setColumnSizingInfo,
127
-
128
- // Rows
129
- getCoreRowModel: getCoreRowModel(),
130
- getSortedRowModel: getSortedRowModel(),
131
- getExpandedRowModel: getExpandedRowModel(),
132
-
133
- // Grouping
134
- getGroupedRowModel: grouping.length > 1 ? getGroupedRowModel() : undefined,
135
- onGroupingChange: handleGroupingChange,
136
-
137
- enableMultiRowSelection: rowsSelectable === 'multi' ? true : undefined,
138
- enableRowSelection: rowsSelectable === true ? true : undefined,
139
-
140
- onRowSelectionChange: handleRowSelectionChange,
141
-
142
- enableSorting: true,
143
- enableColumnPinning: true,
144
-
145
- // Debug
146
- debugTable: debug,
147
- });
148
-
149
- useEffect(() => {
150
- onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
151
- }, [onDataSelectionChange, rowSelection, table]);
152
-
153
- usePinLastRow(pinLastRow, table, data);
154
-
155
- // Create additional expansion column if all columns have fixed width.
156
- const expand = false; // columns.map((column) => column.size).filter(Boolean).length === columns?.length;
157
-
158
- return (
159
- <TableProvider
160
- {...{ ...props, ...columnSorting }}
161
- table={table}
162
- header={header}
163
- expand={expand}
164
- isGrid={role === 'grid' || role === 'treegrid'}
165
- >
166
- <TableImpl<TData> debug={false} {...props} />
167
- </TableProvider>
123
+ [model],
168
124
  );
169
- };
170
-
171
- // TODO(zan): Smush this into the Table component.
172
- /**
173
- * Pure implementation of table outside of context set-up.
174
- */
175
- const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
176
- const { debug, classNames, role, grouping, fullWidth } = props;
177
- const { table } = useTableContext<TData>();
178
-
179
- if (debug) {
180
- return (
181
- <pre className='font-mono text-xs text-neutral-500 m-1 p-2 ring'>
182
- <code>{JSON.stringify(table.getState(), undefined, 2)}</code>
183
- </pre>
184
- );
185
- }
186
-
187
- const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
188
-
189
- const TableComponent = isResizingColumn ? MemoizedVirtualizedTableContent : VirtualizedTableContent;
190
125
 
191
- return (
192
- <table
193
- role={role}
194
- className={tableRoot(props, classNames)}
195
- style={{
196
- // borderCollapse: 'separate',
197
- borderSpacing: 0,
198
- ...(!fullWidth && { width: table.getTotalSize() }),
199
- }}
200
- >
201
- <TableHead />
202
-
203
- {grouping?.length !== 0 ? <TableComponent /> : <GroupedTableContent />}
204
- </table>
126
+ const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
127
+ (event) => {
128
+ if (event.axis === 'col') {
129
+ const columnIndex = parseInt(event.index, 10);
130
+ model?.setColumnWidth(columnIndex, event.size);
131
+ }
132
+ },
133
+ [model],
205
134
  );
206
- };
207
-
208
- const VirtualizedTableContent = () => {
209
- const { table } = useTableContext();
210
- const { virtualizer, dispatch } = useContext(TableRootContext);
211
-
212
- const centerRows = table.getCenterRows();
213
- let pinnedRows = [] as Row<unknown>[];
214
-
215
- useEffect(() => {
216
- dispatch({ type: 'updateTableCount', count: centerRows.length });
217
- }, [centerRows.length]);
218
135
 
219
- try {
220
- // TODO(zan): Work out how to sync the row pinning with rendering
221
- // This is a hack because the first call to this function after a row is deleted will throw an error
222
- // because the row index is no longer in the table
223
- // The pin last row hook updates the pinned row key after the row is deleted
224
- pinnedRows = table.getBottomRows();
225
- } catch (_) {} // Ignore error
226
-
227
- const { getTotalSize, getVirtualItems } = virtualizer;
228
-
229
- const virtualRows = getVirtualItems();
230
- const totalSize = getTotalSize();
231
-
232
- const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
233
- const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0;
234
-
235
- const rowsToRender = [...virtualRows.map((virtualRow) => centerRows[virtualRow.index]), ...pinnedRows].filter(
236
- (r) => !!r,
136
+ const handleWheel = useCallback(
137
+ (event: WheelEvent) => {
138
+ if (!hasAttention) {
139
+ event.stopPropagation();
140
+ }
141
+ },
142
+ [hasAttention],
237
143
  );
238
144
 
239
- return (
240
- <Fragment>
241
- {paddingTop > 0 && (
242
- <tbody role='none'>
243
- <tr role='none'>
244
- <td style={{ height: `${paddingTop}px` }} role='none' />
245
- </tr>
246
- </tbody>
247
- )}
248
- <TableBody rows={rowsToRender} />
249
- {paddingBottom > 0 && (
250
- <tbody role='none'>
251
- <tr role='none'>
252
- <td style={{ height: `${paddingBottom}px` }} role='none' />
253
- </tr>
254
- </tbody>
255
- )}
256
- </Fragment>
145
+ // TODO(burdon): Factor out?
146
+ const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
147
+ async ({ field, props }, _text) => {
148
+ if (model && props.referenceSchema && field.referencePath) {
149
+ const space = getSpace(model.table);
150
+ invariant(space);
151
+ const schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
152
+ if (schema) {
153
+ // TODO(burdon): Cache/filter.
154
+ const { objects } = await space.db.query(Filter.schema(schema)).run();
155
+ return objects
156
+ .map((obj) => {
157
+ const value = getValue(obj, field.referencePath!);
158
+ if (!value || typeof value !== 'string') {
159
+ return undefined;
160
+ }
161
+
162
+ return {
163
+ label: value,
164
+ data: obj,
165
+ };
166
+ })
167
+ .filter(isNotFalsy);
168
+ }
169
+ }
170
+
171
+ return [];
172
+ },
173
+ [model],
257
174
  );
258
- };
259
-
260
- export const MemoizedVirtualizedTableContent = memo(VirtualizedTableContent);
261
175
 
262
- const GroupedTableContent = () => {
263
- const {
264
- table: { getGroupedRowModel, getHeaderGroups, getState },
265
- debug,
266
- } = useTableContext();
176
+ if (!model) {
177
+ return <span role='none' className='attention-surface' />;
178
+ }
267
179
 
268
180
  return (
269
181
  <>
270
- {getGroupedRowModel().rows.map((row, i) => {
271
- return (
272
- <Fragment key={i}>
273
- {/* TODO(burdon): Customize group header renderer. */}
274
- <thead>
275
- <tr>
276
- {debug && <th />}
277
- <th
278
- // TODO(burdon): Calculate row span.
279
- colSpan={getHeaderGroups()[0].headers.length}
280
- className={groupTh({})}
281
- >
282
- {getState().grouping[0]}[{String(row.getGroupingValue(getState().grouping[0]))}]
283
- </th>
284
- </tr>
285
- </thead>
286
- <TableBody rows={row.subRows} />
287
- </Fragment>
288
- );
289
- })}
182
+ <Grid.Root id={model.table.id ?? 'table-grid'}>
183
+ <TableCellEditor model={model} onEnter={handleEnter} onFocus={handleFocus} onQuery={handleQuery} />
184
+
185
+ <Grid.Content
186
+ onWheelCapture={handleWheel}
187
+ className={mx(
188
+ '[--dx-grid-base:var(--surface-bg)] [&_.dx-grid]:bs-min [&_.dx-grid]:shrink [&_.dx-grid]:max-is-max',
189
+ inlineEndLine,
190
+ blockEndLine,
191
+ )}
192
+ frozen={frozen}
193
+ columns={model.columnMeta.value}
194
+ limitRows={model.getRowCount() ?? 0}
195
+ limitColumns={model.table.view?.fields?.length ?? 0}
196
+ onAxisResize={handleAxisResize}
197
+ onClick={model?.handleGridClick}
198
+ onKeyDown={handleKeyDown}
199
+ overscroll='trap'
200
+ ref={setDxGrid}
201
+ />
202
+ </Grid.Root>
203
+
204
+ <RowActionsMenu model={model} />
205
+ <ColumnActionsMenu model={model} />
206
+ <ColumnSettings model={model} />
290
207
  </>
291
208
  );
292
- };
209
+ });
210
+
211
+ //
212
+ // CellEditor
213
+ //
293
214
 
294
- // TODO(burdon): Floating "add row" jumps by 1 pixel on scroll.
295
- // TODO(burdon): Blue focus highlight is clipped by 1px on the left.
296
- // TODO(burdon): Focused row and focus ring passes over the sticky header.
297
215
  export const Table = {
298
216
  Root: TableRoot,
299
- Viewport: TableViewport,
300
- Main: TablePrimitive,
217
+ Main: TableMain,
301
218
  };
@@ -1,8 +1,5 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './props';
6
5
  export * from './Table';
7
- export * from './TableContext';
8
- export * from './TableRootContext';
@@ -0,0 +1,114 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type Meta, type StoryObj } from '@storybook/react';
8
+ import React, { useEffect, useMemo, useState } from 'react';
9
+
10
+ import { type MutableSchema } from '@dxos/echo-schema';
11
+ import { invariant } from '@dxos/invariant';
12
+ import { faker } from '@dxos/random';
13
+ import { Filter, useSpaces, useQuery, create } from '@dxos/react-client/echo';
14
+ import { withClientProvider } from '@dxos/react-client/testing';
15
+ import { defaultSizeRow, Grid, type GridEditing } from '@dxos/react-ui-grid';
16
+ import { ViewProjection, ViewType } from '@dxos/schema';
17
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
18
+
19
+ import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
20
+ import { useTableModel } from '../../hooks';
21
+ import translations from '../../translations';
22
+ import { TableType } from '../../types';
23
+ import { initializeTable } from '../../util';
24
+
25
+ type StoryProps = {
26
+ editing: GridEditing;
27
+ };
28
+
29
+ const DefaultStory = ({ editing }: StoryProps) => {
30
+ const spaces = useSpaces();
31
+ const space = spaces[spaces.length - 1];
32
+ const tables = useQuery(space, Filter.schema(TableType));
33
+ const [table, setTable] = useState<TableType>();
34
+ const [schema, setSchema] = useState<MutableSchema>();
35
+ useEffect(() => {
36
+ if (tables.length && !table) {
37
+ const table = tables[0];
38
+ invariant(table.view);
39
+ setTable(table);
40
+ setSchema(space.db.schemaRegistry.getSchema(table.view.query.type));
41
+ }
42
+ }, [tables]);
43
+
44
+ const projection = useMemo(() => {
45
+ if (schema && table?.view) {
46
+ return new ViewProjection(schema, table.view);
47
+ }
48
+ }, [schema, table?.view]);
49
+
50
+ const model = useTableModel({ table, projection });
51
+
52
+ const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
53
+ const { objects } = await space.db.query(schema).run();
54
+ return objects.map((obj) => {
55
+ const label = obj[field.referencePath ?? 'id'];
56
+ return {
57
+ label,
58
+ data: obj,
59
+ };
60
+ });
61
+ };
62
+
63
+ if (!model || !schema || !table) {
64
+ return <div />;
65
+ }
66
+
67
+ return (
68
+ <div className='flex w-[300px] border border-separator' style={{ height: defaultSizeRow }}>
69
+ <Grid.Root id='test' editing={editing}>
70
+ <TableCellEditor model={model} onQuery={handleQuery} />
71
+ </Grid.Root>
72
+ </div>
73
+ );
74
+ };
75
+
76
+ const meta: Meta<StoryProps> = {
77
+ title: 'plugins/plugin-table/TableCellEditor',
78
+ component: DefaultStory,
79
+ render: DefaultStory,
80
+ parameters: { translations, layout: 'centered' },
81
+ decorators: [
82
+ withClientProvider({
83
+ types: [TableType, ViewType],
84
+ createIdentity: true,
85
+ createSpace: true,
86
+ onSpaceCreated: ({ space }) => {
87
+ const table = space.db.add(create(TableType, {}));
88
+ const schema = initializeTable({ space, table });
89
+ Array.from({ length: 10 }).forEach(() => {
90
+ space.db.add(
91
+ create(schema, {
92
+ name: faker.person.fullName(),
93
+ }),
94
+ );
95
+ });
96
+ },
97
+ }),
98
+ withTheme,
99
+ withLayout({ tooltips: true }),
100
+ ],
101
+ };
102
+
103
+ export default meta;
104
+
105
+ type Story = StoryObj<StoryProps>;
106
+
107
+ export const Default: Story = {
108
+ args: {
109
+ editing: {
110
+ index: '0,3',
111
+ initialContent: 'Test',
112
+ },
113
+ },
114
+ };