@dxos/react-ui-table 0.6.14-staging.e15392e → 0.7.1-staging.599df14

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 +1274 -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 +1380 -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 +1399 -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 +13 -15
  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 +14 -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 +177 -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 +87 -0
  160. package/src/components/Toolbar/common.tsx +71 -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,222 @@
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';
29
19
  import { mx } from '@dxos/react-ui-theme';
20
+ import { isNotFalsy } from '@dxos/util';
30
21
 
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';
22
+ import { ColumnActionsMenu } from './ColumnActionsMenu';
23
+ import { ColumnSettings } from './ColumnSettings';
24
+ import { RowActionsMenu } from './RowActionsMenu';
25
+ import { type TableModel } from '../../model';
26
+ import { type GridCell } from '../../util';
27
+ import { TableCellEditor, type TableCellEditorProps } from '../TableCellEditor';
38
28
 
39
- type TableRootProps = { children: ReactNode };
29
+ // NOTE(Zan): These fragments add border to inline-end and block-end of the grid using pseudo-elements.
30
+ // These are offset by 1px to avoid double borders in planks.
31
+ const inlineEndLine =
32
+ '[&>.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';
33
+ const blockEndLine =
34
+ '[&>.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
35
 
41
- const TableRoot = ({ children }: TableRootProps) => {
42
- const contextValue = useTableRootContext();
43
- return <TableRootContext.Provider value={contextValue}>{children}</TableRootContext.Provider>;
44
- };
36
+ const frozen = { frozenRowsStart: 1, frozenColsStart: 1, frozenColsEnd: 1 };
45
37
 
46
- type TableViewportProps = ThemedClassName<ComponentPropsWithoutRef<typeof Primitive.div>> & {
47
- asChild?: boolean;
48
- };
38
+ //
39
+ // Root
40
+ //
49
41
 
50
- const TableViewport = ({ children, classNames, asChild, ...props }: TableViewportProps) => {
51
- const { scrollContextRef } = useContext(TableRootContext);
42
+ export type TableRootProps = PropsWithChildren<{ role?: string }>;
52
43
 
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}>
44
+ const TableRoot = ({ children, role }: TableRootProps) => {
45
+ return (
46
+ <div
47
+ role='none'
48
+ className={mx(
49
+ 'relative border-bs !border-separator',
50
+ role === 'section'
51
+ ? 'attention-surface overflow-hidden [&_.dx-grid]:max-is-max'
52
+ : 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-max [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-max',
53
+ )}
54
+ >
60
55
  {children}
61
56
  </div>
62
57
  );
63
58
  };
64
59
 
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
- });
60
+ //
61
+ // Main
62
+ //
92
63
 
93
- const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
64
+ export type TableController = {
65
+ update?: (cell?: GridCell) => void;
66
+ };
94
67
 
95
- const [grouping, handleGroupingChange] = useState<GroupingState>(props.grouping ?? []);
96
- useEffect(() => handleGroupingChange(props.grouping ?? []), [handleGroupingChange, props.grouping]);
68
+ export type TableMainProps = {
69
+ model?: TableModel;
70
+ ignoreAttention?: boolean;
71
+ };
97
72
 
98
- const columnSorting = useColumnSorting();
73
+ const TableMain = forwardRef<TableController, TableMainProps>(({ model, ignoreAttention }, forwardedRef) => {
74
+ const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
75
+
76
+ const { hasAttention } = useAttention(model?.table ? fullyQualifiedId(model.table) : 'table');
77
+
78
+ /**
79
+ * Provides an external controller that can be called to repaint the table.
80
+ */
81
+ useImperativeHandle<TableController, TableController>(
82
+ forwardedRef,
83
+ () => {
84
+ if (!model || !dxGrid) {
85
+ return {};
86
+ }
87
+
88
+ dxGrid.getCells = model.getCells.bind(model);
89
+ return {
90
+ update: (cell) => {
91
+ if (cell) {
92
+ dxGrid.updateIfWithinBounds(cell);
93
+ } else {
94
+ dxGrid.updateCells(true);
95
+ }
96
+ },
97
+ };
98
+ },
99
+ [model, dxGrid],
100
+ );
99
101
 
100
- const table = useReactTable({
101
- // Data
102
- meta: {},
103
- data,
102
+ const handleFocus: TableCellEditorProps['onFocus'] = (increment, delta) => {
103
+ dxGrid?.refocus(increment, delta);
104
+ };
104
105
 
105
- // Columns
106
- columns,
107
- defaultColumn: {
108
- size: 400, // Required in order remove default width.
109
- maxSize: 1024,
106
+ const handleEnter = useCallback<NonNullable<TableCellEditorProps['onEnter']>>(
107
+ (cell) => {
108
+ // TODO(burdon): Insert row only if bottom row isn't completely blank already.
109
+ if (model && cell.row === model.getRowCount() - 1) {
110
+ model.insertRow(cell.row);
111
+ }
110
112
  },
113
+ [model],
114
+ );
111
115
 
112
- // State
113
- state: {
114
- columnVisibility,
115
- columnSizing,
116
- columnSizingInfo,
117
- sorting: columnSorting.sorting,
118
- rowSelection,
119
- grouping,
116
+ const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
117
+ (event) => {
118
+ const cell = closestCell(event.target);
119
+ if (!model || !cell) {
120
+ return;
121
+ }
122
+
123
+ switch (event.key) {
124
+ case 'Backspace':
125
+ case 'Delete': {
126
+ model.setCellData(cell, undefined);
127
+ break;
128
+ }
129
+ }
120
130
  },
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>
131
+ [model],
168
132
  );
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
-
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
133
 
203
- {grouping?.length !== 0 ? <TableComponent /> : <GroupedTableContent />}
204
- </table>
134
+ const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
135
+ (event) => {
136
+ if (event.axis === 'col') {
137
+ const columnIndex = parseInt(event.index, 10);
138
+ model?.setColumnWidth(columnIndex, event.size);
139
+ }
140
+ },
141
+ [model],
205
142
  );
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
-
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
143
 
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,
144
+ const handleWheel = useCallback(
145
+ (event: WheelEvent) => {
146
+ if (!ignoreAttention && !hasAttention) {
147
+ event.stopPropagation();
148
+ }
149
+ },
150
+ [hasAttention, ignoreAttention],
237
151
  );
238
152
 
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>
153
+ // TODO(burdon): Factor out?
154
+ const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
155
+ async ({ field, props }, _text) => {
156
+ if (model && props.referenceSchema && field.referencePath) {
157
+ const space = getSpace(model.table);
158
+ invariant(space);
159
+ const schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
160
+ if (schema) {
161
+ // TODO(burdon): Cache/filter.
162
+ const { objects } = await space.db.query(Filter.schema(schema)).run();
163
+ return objects
164
+ .map((obj) => {
165
+ const value = getValue(obj, field.referencePath!);
166
+ if (!value || typeof value !== 'string') {
167
+ return undefined;
168
+ }
169
+
170
+ return {
171
+ label: value,
172
+ data: obj,
173
+ };
174
+ })
175
+ .filter(isNotFalsy);
176
+ }
177
+ }
178
+
179
+ return [];
180
+ },
181
+ [model],
257
182
  );
258
- };
259
-
260
- export const MemoizedVirtualizedTableContent = memo(VirtualizedTableContent);
261
183
 
262
- const GroupedTableContent = () => {
263
- const {
264
- table: { getGroupedRowModel, getHeaderGroups, getState },
265
- debug,
266
- } = useTableContext();
184
+ if (!model) {
185
+ return <span role='none' className='attention-surface' />;
186
+ }
267
187
 
268
188
  return (
269
189
  <>
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
- })}
190
+ <Grid.Root id={model.table.id ?? 'table-grid'}>
191
+ <TableCellEditor model={model} onEnter={handleEnter} onFocus={handleFocus} onQuery={handleQuery} />
192
+
193
+ <Grid.Content
194
+ onWheelCapture={handleWheel}
195
+ className={mx('[--dx-grid-base:var(--surface-bg)]', inlineEndLine, blockEndLine)}
196
+ frozen={frozen}
197
+ columns={model.columnMeta.value}
198
+ limitRows={model.getRowCount() ?? 0}
199
+ limitColumns={model.table.view?.fields?.length ?? 0}
200
+ onAxisResize={handleAxisResize}
201
+ onClick={model?.handleGridClick}
202
+ onKeyDown={handleKeyDown}
203
+ overscroll='trap'
204
+ ref={setDxGrid}
205
+ />
206
+ </Grid.Root>
207
+
208
+ <RowActionsMenu model={model} />
209
+ <ColumnActionsMenu model={model} />
210
+ <ColumnSettings model={model} />
290
211
  </>
291
212
  );
292
- };
213
+ });
214
+
215
+ //
216
+ // CellEditor
217
+ //
293
218
 
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
219
  export const Table = {
298
220
  Root: TableRoot,
299
- Viewport: TableViewport,
300
- Main: TablePrimitive,
221
+ Main: TableMain,
301
222
  };
@@ -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
+ };