@dxos/react-ui-table 0.8.1-main.ba2dec9 → 0.8.1-staging.5be625a

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 (203) hide show
  1. package/dist/lib/browser/{chunk-3FR6NHPD.mjs → chunk-5MKYB7NF.mjs} +5 -7
  2. package/dist/lib/browser/chunk-5MKYB7NF.mjs.map +7 -0
  3. package/dist/lib/browser/{_deprecated → deprecated}/index.mjs +26 -26
  4. package/dist/lib/{node-esm/_deprecated → browser/deprecated}/index.mjs.map +4 -4
  5. package/dist/lib/browser/index.mjs +394 -192
  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 +1 -1
  9. package/dist/lib/node/{chunk-NB3SFHGA.cjs → chunk-NRFKBRPV.cjs} +7 -9
  10. package/dist/lib/node/chunk-NRFKBRPV.cjs.map +7 -0
  11. package/dist/lib/node/{_deprecated → deprecated}/index.cjs +5 -5
  12. package/dist/lib/node/{_deprecated → deprecated}/index.cjs.map +4 -4
  13. package/dist/lib/node/index.cjs +386 -188
  14. package/dist/lib/node/index.cjs.map +4 -4
  15. package/dist/lib/node/meta.json +1 -1
  16. package/dist/lib/node/types/index.cjs +3 -3
  17. package/dist/lib/node/types/index.cjs.map +1 -1
  18. package/dist/lib/node-esm/{chunk-RLVVOZW5.mjs → chunk-SU4D55KI.mjs} +5 -7
  19. package/dist/lib/node-esm/chunk-SU4D55KI.mjs.map +7 -0
  20. package/dist/lib/node-esm/{_deprecated → deprecated}/index.mjs +26 -26
  21. package/dist/lib/node-esm/deprecated/index.mjs.map +7 -0
  22. package/dist/lib/node-esm/index.mjs +394 -192
  23. package/dist/lib/node-esm/index.mjs.map +4 -4
  24. package/dist/lib/node-esm/meta.json +1 -1
  25. package/dist/lib/node-esm/types/index.mjs +1 -1
  26. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/DynamicTable.d.ts +19 -0
  29. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -0
  30. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/Table.d.ts +1 -0
  32. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/Table.stories.d.ts +3 -1
  34. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/index.d.ts +1 -0
  36. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/testing.d.ts +7 -7
  39. package/dist/types/src/components/testing.d.ts.map +1 -1
  40. package/dist/types/src/deprecated/components/Cell/Cell.d.ts.map +1 -0
  41. package/dist/types/src/deprecated/components/Cell/CellCombobox.d.ts.map +1 -0
  42. package/dist/types/src/deprecated/components/Cell/index.d.ts.map +1 -0
  43. package/dist/types/src/deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +1 -0
  44. package/dist/types/src/deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -0
  45. package/dist/types/src/deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +1 -0
  46. package/dist/types/src/deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -0
  47. package/dist/types/src/deprecated/components/ColumnMenu/index.d.ts.map +1 -0
  48. package/dist/types/src/deprecated/components/Table/Table.d.ts.map +1 -0
  49. package/dist/types/src/deprecated/components/Table/Table.stories.d.ts.map +1 -0
  50. package/dist/types/src/deprecated/components/Table/TableBody.d.ts.map +1 -0
  51. package/dist/types/src/deprecated/components/Table/TableContext.d.ts.map +1 -0
  52. package/dist/types/src/deprecated/components/Table/TableHead.d.ts.map +1 -0
  53. package/dist/types/src/deprecated/components/Table/TableRootContext.d.ts.map +1 -0
  54. package/dist/types/src/deprecated/components/Table/index.d.ts.map +1 -0
  55. package/dist/types/src/deprecated/components/Table/props.d.ts.map +1 -0
  56. package/dist/types/src/deprecated/components/index.d.ts.map +1 -0
  57. package/dist/types/src/deprecated/helpers.d.ts.map +1 -0
  58. package/dist/types/src/deprecated/hooks/index.d.ts.map +1 -0
  59. package/dist/types/src/deprecated/hooks/useColumnSizing.d.ts.map +1 -0
  60. package/dist/types/src/deprecated/hooks/useColumnSorting.d.ts.map +1 -0
  61. package/dist/types/src/deprecated/hooks/usePinLastRow.d.ts.map +1 -0
  62. package/dist/types/src/deprecated/hooks/useRowSelection.d.ts.map +1 -0
  63. package/dist/types/src/deprecated/index.d.ts.map +1 -0
  64. package/dist/types/src/deprecated/schema/Schema.stories.d.ts.map +1 -0
  65. package/dist/types/src/deprecated/schema/column-utils.d.ts.map +1 -0
  66. package/dist/types/src/deprecated/schema/index.d.ts.map +1 -0
  67. package/dist/types/src/deprecated/schema/types.d.ts.map +1 -0
  68. package/dist/types/src/deprecated/theme.d.ts.map +1 -0
  69. package/dist/types/src/deprecated/translations.d.ts.map +1 -0
  70. package/dist/types/src/deprecated/types.d.ts.map +1 -0
  71. package/dist/types/src/hooks/useTableModel.d.ts +6 -3
  72. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  73. package/dist/types/src/model/table-model.d.ts +34 -7
  74. package/dist/types/src/model/table-model.d.ts.map +1 -1
  75. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  76. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  77. package/dist/types/src/types/table.d.ts +7 -33
  78. package/dist/types/src/types/table.d.ts.map +1 -1
  79. package/dist/types/src/util/dynamic-table.d.ts +40 -0
  80. package/dist/types/src/util/dynamic-table.d.ts.map +1 -0
  81. package/dist/types/src/util/index.d.ts +1 -0
  82. package/dist/types/src/util/index.d.ts.map +1 -1
  83. package/dist/types/src/util/table.d.ts +5 -3
  84. package/dist/types/src/util/table.d.ts.map +1 -1
  85. package/package.json +35 -35
  86. package/src/components/Table/ColumnActionsMenu.tsx +6 -4
  87. package/src/components/Table/ColumnSettings.tsx +5 -6
  88. package/src/components/Table/CreateRefPanel.tsx +3 -3
  89. package/src/components/Table/DynamicTable.tsx +86 -0
  90. package/src/components/Table/RefPanel.tsx +3 -3
  91. package/src/components/Table/RowActionsMenu.tsx +18 -3
  92. package/src/components/Table/Table.stories.tsx +71 -20
  93. package/src/components/Table/Table.tsx +30 -10
  94. package/src/components/Table/index.ts +1 -0
  95. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +9 -4
  96. package/src/components/testing.ts +1 -0
  97. package/src/{_deprecated → deprecated}/components/Cell/Cell.tsx +2 -2
  98. package/src/{_deprecated → deprecated}/components/Table/TableBody.tsx +2 -2
  99. package/src/{_deprecated → deprecated}/helpers.tsx +2 -2
  100. package/src/hooks/useTableModel.ts +28 -7
  101. package/src/model/table-model.test.ts +1 -1
  102. package/src/model/table-model.ts +97 -21
  103. package/src/model/table-presentation.test.ts +1 -1
  104. package/src/model/table-presentation.ts +30 -12
  105. package/src/model/table-sorting.ts +1 -0
  106. package/src/types/table.ts +5 -7
  107. package/src/util/dynamic-table.ts +71 -0
  108. package/src/util/index.ts +1 -0
  109. package/src/util/table.ts +67 -65
  110. package/dist/lib/browser/_deprecated/index.mjs.map +0 -7
  111. package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +0 -7
  112. package/dist/lib/node/chunk-NB3SFHGA.cjs.map +0 -7
  113. package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +0 -7
  114. package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +0 -1
  115. package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +0 -1
  116. package/dist/types/src/_deprecated/components/Cell/index.d.ts.map +0 -1
  117. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +0 -1
  118. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +0 -1
  119. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +0 -1
  120. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +0 -1
  121. package/dist/types/src/_deprecated/components/ColumnMenu/index.d.ts.map +0 -1
  122. package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +0 -1
  123. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +0 -1
  124. package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +0 -1
  125. package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +0 -1
  126. package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +0 -1
  127. package/dist/types/src/_deprecated/components/Table/TableRootContext.d.ts.map +0 -1
  128. package/dist/types/src/_deprecated/components/Table/index.d.ts.map +0 -1
  129. package/dist/types/src/_deprecated/components/Table/props.d.ts.map +0 -1
  130. package/dist/types/src/_deprecated/components/index.d.ts.map +0 -1
  131. package/dist/types/src/_deprecated/helpers.d.ts.map +0 -1
  132. package/dist/types/src/_deprecated/hooks/index.d.ts.map +0 -1
  133. package/dist/types/src/_deprecated/hooks/useColumnSizing.d.ts.map +0 -1
  134. package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts.map +0 -1
  135. package/dist/types/src/_deprecated/hooks/usePinLastRow.d.ts.map +0 -1
  136. package/dist/types/src/_deprecated/hooks/useRowSelection.d.ts.map +0 -1
  137. package/dist/types/src/_deprecated/index.d.ts.map +0 -1
  138. package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +0 -1
  139. package/dist/types/src/_deprecated/schema/column-utils.d.ts.map +0 -1
  140. package/dist/types/src/_deprecated/schema/index.d.ts.map +0 -1
  141. package/dist/types/src/_deprecated/schema/types.d.ts.map +0 -1
  142. package/dist/types/src/_deprecated/theme.d.ts.map +0 -1
  143. package/dist/types/src/_deprecated/translations.d.ts.map +0 -1
  144. package/dist/types/src/_deprecated/types.d.ts.map +0 -1
  145. /package/dist/types/src/{_deprecated → deprecated}/components/Cell/Cell.d.ts +0 -0
  146. /package/dist/types/src/{_deprecated → deprecated}/components/Cell/CellCombobox.d.ts +0 -0
  147. /package/dist/types/src/{_deprecated → deprecated}/components/Cell/index.d.ts +0 -0
  148. /package/dist/types/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnMenu.d.ts +0 -0
  149. /package/dist/types/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnMenu.stories.d.ts +0 -0
  150. /package/dist/types/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnSettings.d.ts +0 -0
  151. /package/dist/types/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnSettings.stories.d.ts +0 -0
  152. /package/dist/types/src/{_deprecated → deprecated}/components/ColumnMenu/index.d.ts +0 -0
  153. /package/dist/types/src/{_deprecated → deprecated}/components/Table/Table.d.ts +0 -0
  154. /package/dist/types/src/{_deprecated → deprecated}/components/Table/Table.stories.d.ts +0 -0
  155. /package/dist/types/src/{_deprecated → deprecated}/components/Table/TableBody.d.ts +0 -0
  156. /package/dist/types/src/{_deprecated → deprecated}/components/Table/TableContext.d.ts +0 -0
  157. /package/dist/types/src/{_deprecated → deprecated}/components/Table/TableHead.d.ts +0 -0
  158. /package/dist/types/src/{_deprecated → deprecated}/components/Table/TableRootContext.d.ts +0 -0
  159. /package/dist/types/src/{_deprecated → deprecated}/components/Table/index.d.ts +0 -0
  160. /package/dist/types/src/{_deprecated → deprecated}/components/Table/props.d.ts +0 -0
  161. /package/dist/types/src/{_deprecated → deprecated}/components/index.d.ts +0 -0
  162. /package/dist/types/src/{_deprecated → deprecated}/helpers.d.ts +0 -0
  163. /package/dist/types/src/{_deprecated → deprecated}/hooks/index.d.ts +0 -0
  164. /package/dist/types/src/{_deprecated → deprecated}/hooks/useColumnSizing.d.ts +0 -0
  165. /package/dist/types/src/{_deprecated → deprecated}/hooks/useColumnSorting.d.ts +0 -0
  166. /package/dist/types/src/{_deprecated → deprecated}/hooks/usePinLastRow.d.ts +0 -0
  167. /package/dist/types/src/{_deprecated → deprecated}/hooks/useRowSelection.d.ts +0 -0
  168. /package/dist/types/src/{_deprecated → deprecated}/index.d.ts +0 -0
  169. /package/dist/types/src/{_deprecated → deprecated}/schema/Schema.stories.d.ts +0 -0
  170. /package/dist/types/src/{_deprecated → deprecated}/schema/column-utils.d.ts +0 -0
  171. /package/dist/types/src/{_deprecated → deprecated}/schema/index.d.ts +0 -0
  172. /package/dist/types/src/{_deprecated → deprecated}/schema/types.d.ts +0 -0
  173. /package/dist/types/src/{_deprecated → deprecated}/theme.d.ts +0 -0
  174. /package/dist/types/src/{_deprecated → deprecated}/translations.d.ts +0 -0
  175. /package/dist/types/src/{_deprecated → deprecated}/types.d.ts +0 -0
  176. /package/src/{_deprecated → deprecated}/components/Cell/CellCombobox.tsx +0 -0
  177. /package/src/{_deprecated → deprecated}/components/Cell/index.ts +0 -0
  178. /package/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnMenu.stories.tsx +0 -0
  179. /package/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnMenu.tsx +0 -0
  180. /package/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnSettings.stories.tsx +0 -0
  181. /package/src/{_deprecated → deprecated}/components/ColumnMenu/ColumnSettings.tsx +0 -0
  182. /package/src/{_deprecated → deprecated}/components/ColumnMenu/index.ts +0 -0
  183. /package/src/{_deprecated → deprecated}/components/Table/Table.stories.tsx +0 -0
  184. /package/src/{_deprecated → deprecated}/components/Table/Table.tsx +0 -0
  185. /package/src/{_deprecated → deprecated}/components/Table/TableContext.tsx +0 -0
  186. /package/src/{_deprecated → deprecated}/components/Table/TableHead.tsx +0 -0
  187. /package/src/{_deprecated → deprecated}/components/Table/TableRootContext.ts +0 -0
  188. /package/src/{_deprecated → deprecated}/components/Table/index.ts +0 -0
  189. /package/src/{_deprecated → deprecated}/components/Table/props.ts +0 -0
  190. /package/src/{_deprecated → deprecated}/components/index.ts +0 -0
  191. /package/src/{_deprecated → deprecated}/hooks/index.ts +0 -0
  192. /package/src/{_deprecated → deprecated}/hooks/useColumnSizing.ts +0 -0
  193. /package/src/{_deprecated → deprecated}/hooks/useColumnSorting.ts +0 -0
  194. /package/src/{_deprecated → deprecated}/hooks/usePinLastRow.ts +0 -0
  195. /package/src/{_deprecated → deprecated}/hooks/useRowSelection.ts +0 -0
  196. /package/src/{_deprecated → deprecated}/index.ts +0 -0
  197. /package/src/{_deprecated → deprecated}/schema/Schema.stories.tsx +0 -0
  198. /package/src/{_deprecated → deprecated}/schema/column-utils.ts +0 -0
  199. /package/src/{_deprecated → deprecated}/schema/index.ts +0 -0
  200. /package/src/{_deprecated → deprecated}/schema/types.ts +0 -0
  201. /package/src/{_deprecated → deprecated}/theme.ts +0 -0
  202. /package/src/{_deprecated → deprecated}/translations.ts +0 -0
  203. /package/src/{_deprecated → deprecated}/types.ts +0 -0
@@ -4,11 +4,11 @@
4
4
 
5
5
  import { computed, effect, signal, type ReadonlySignal } from '@preact/signals-core';
6
6
 
7
+ import { type Space } from '@dxos/client/echo';
7
8
  import { Resource } from '@dxos/context';
8
9
  import { type FieldSortType, FormatEnum, getValue, setValue, type JsonProp } from '@dxos/echo-schema';
9
10
  import { invariant } from '@dxos/invariant';
10
11
  import { isReactiveObject, makeRef } from '@dxos/live-object';
11
- import { PublicKey } from '@dxos/react-client';
12
12
  import { formatForEditing, parseValue } from '@dxos/react-ui-form';
13
13
  import {
14
14
  type DxGridAxisMeta,
@@ -16,18 +16,38 @@ import {
16
16
  type DxGridPlanePosition,
17
17
  type DxGridPosition,
18
18
  } from '@dxos/react-ui-grid';
19
- import { type ViewProjection } from '@dxos/schema';
19
+ import { type ViewType, type ViewProjection } from '@dxos/schema';
20
20
 
21
21
  import { SelectionModel } from './selection-model';
22
22
  import { TableSorting } from './table-sorting';
23
- import { type TableType } from '../types';
24
23
  import { touch } from '../util';
25
24
 
26
25
  export type BaseTableRow = Record<JsonProp, any> & { id: string };
27
26
 
27
+ export type TableRowAction = {
28
+ id: string;
29
+ translationKey: string;
30
+ };
31
+
32
+ // TODO(ZaymonFC): There should be a separate concept for immutable schemata.
33
+ export type TableFeatures = {
34
+ selection: boolean;
35
+ dataEditable: boolean;
36
+ schemaEditable: boolean;
37
+ };
38
+
39
+ const defaultFeatures: TableFeatures = {
40
+ selection: true,
41
+ dataEditable: false,
42
+ schemaEditable: false,
43
+ };
44
+
28
45
  export type TableModelProps<T extends BaseTableRow = { id: string }> = {
29
- table: TableType;
46
+ id?: string;
47
+ space?: Space;
48
+ view?: ViewType;
30
49
  projection: ViewProjection;
50
+ features?: Partial<TableFeatures>;
31
51
  sorting?: FieldSortType[];
32
52
  pinnedRows?: { top: number[]; bottom: number[] };
33
53
  onInsertRow?: (index?: number) => void;
@@ -35,12 +55,14 @@ export type TableModelProps<T extends BaseTableRow = { id: string }> = {
35
55
  onDeleteColumn?: (fieldId: string) => void;
36
56
  onCellUpdate?: (cell: DxGridPosition) => void;
37
57
  onRowOrderChanged?: () => void;
58
+ rowActions?: TableRowAction[];
59
+ onRowAction?: (actionId: string, data: T) => void;
38
60
  };
39
61
 
40
62
  export class TableModel<T extends BaseTableRow = { id: string }> extends Resource {
41
- public readonly id = `table-model-${PublicKey.random().truncate()}`;
42
-
43
- private readonly _table: TableType;
63
+ private readonly _id: string | undefined;
64
+ private readonly _space: Space | undefined;
65
+ private readonly _view: ViewType | undefined;
44
66
  private readonly _projection: ViewProjection;
45
67
 
46
68
  private readonly _visibleRange = signal<DxGridPlaneRange>({
@@ -53,6 +75,9 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
53
75
  private readonly _onDeleteColumn?: TableModelProps<T>['onDeleteColumn'];
54
76
  private readonly _onCellUpdate?: TableModelProps<T>['onCellUpdate'];
55
77
  private readonly _onRowOrderChanged?: TableModelProps<T>['onRowOrderChanged'];
78
+ private readonly _rowActions: TableRowAction[];
79
+ private readonly _onRowAction?: TableModelProps<T>['onRowAction'];
80
+ private readonly _features: TableFeatures;
56
81
 
57
82
  private readonly _rows = signal<T[]>([]);
58
83
  private readonly _sorting: TableSorting<T>;
@@ -62,8 +87,11 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
62
87
  private _columnMeta?: ReadonlySignal<DxGridAxisMeta>;
63
88
 
64
89
  constructor({
65
- table,
90
+ id,
91
+ space,
92
+ view,
66
93
  projection,
94
+ features = {},
67
95
  sorting = [],
68
96
  pinnedRows = { top: [], bottom: [] },
69
97
  onCellUpdate,
@@ -71,11 +99,17 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
71
99
  onDeleteRows,
72
100
  onInsertRow,
73
101
  onRowOrderChanged,
102
+ rowActions = [],
103
+ onRowAction,
74
104
  }: TableModelProps<T>) {
75
105
  super();
76
- this._table = table;
106
+ this._id = id;
107
+ this._space = space;
108
+ this._view = view;
77
109
  this._projection = projection;
78
- this._sorting = new TableSorting(this._rows, table.view?.target, projection);
110
+ this._features = { ...defaultFeatures, ...features };
111
+
112
+ this._sorting = new TableSorting(this._rows, this._view, projection);
79
113
 
80
114
  if (sorting.length > 0) {
81
115
  const [sort] = sorting;
@@ -88,10 +122,20 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
88
122
  this._onDeleteColumn = onDeleteColumn;
89
123
  this._onCellUpdate = onCellUpdate;
90
124
  this._onRowOrderChanged = onRowOrderChanged;
125
+ this._rowActions = rowActions;
126
+ this._onRowAction = onRowAction;
91
127
  }
92
128
 
93
- public get table() {
94
- return this._table;
129
+ public get id() {
130
+ return this._id;
131
+ }
132
+
133
+ public get space() {
134
+ return this._space;
135
+ }
136
+
137
+ public get view() {
138
+ return this._view;
95
139
  }
96
140
 
97
141
  public get projection() {
@@ -102,6 +146,10 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
102
146
  return this._sorting.sortedRows;
103
147
  }
104
148
 
149
+ public get features(): TableFeatures {
150
+ return this._features;
151
+ }
152
+
105
153
  /**
106
154
  * @reactive
107
155
  */
@@ -109,6 +157,17 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
109
157
  return this._sorting;
110
158
  }
111
159
 
160
+ /**
161
+ * Gets the row data at the specified display index.
162
+ */
163
+ public getRowAt(displayIndex: number): T | undefined {
164
+ if (displayIndex < 0 || displayIndex >= this._sorting.sortedRows.value.length) {
165
+ return undefined;
166
+ }
167
+
168
+ return this._sorting.sortedRows.value[displayIndex];
169
+ }
170
+
112
171
  public get pinnedRows(): NonNullable<TableModelProps<T>['pinnedRows']> {
113
172
  return this._pinnedRows;
114
173
  }
@@ -127,6 +186,10 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
127
186
  return this._sorting.isDirty;
128
187
  }
129
188
 
189
+ public get rowActions(): TableRowAction[] {
190
+ return this._rowActions;
191
+ }
192
+
130
193
  //
131
194
  // Initialisation
132
195
  //
@@ -140,7 +203,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
140
203
 
141
204
  private initializeColumnMeta(): void {
142
205
  this._columnMeta = computed(() => {
143
- const fields = this._table.view?.target?.fields ?? [];
206
+ const fields = this._view?.fields ?? [];
144
207
  const meta = Object.fromEntries(
145
208
  fields.map((field, index: number) => [index, { size: field?.size ?? 256, resizeable: true }]),
146
209
  );
@@ -172,7 +235,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
172
235
  rowEffects.push(
173
236
  effect(() => {
174
237
  const obj = this._sorting.sortedRows.value[row];
175
- this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
238
+ this._view?.fields.forEach((field) => touch(getValue(obj, field.path)));
176
239
  this._onCellUpdate?.({ row, col: start.col, plane: 'grid' });
177
240
  }),
178
241
  );
@@ -193,7 +256,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
193
256
 
194
257
  public getRowCount = (): number => this._rows.value.length;
195
258
 
196
- public getColumnCount = (): number => this.table.view?.target?.fields.length ?? 0;
259
+ public getColumnCount = (): number => this._view?.fields.length ?? 0;
197
260
 
198
261
  public insertRow = (rowIndex?: number): void => {
199
262
  const row = rowIndex !== undefined ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
@@ -215,8 +278,21 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
215
278
  this._onDeleteRows?.(row, objectsToDelete);
216
279
  };
217
280
 
281
+ public handleRowAction = (actionId: string, rowIndex: number): void => {
282
+ if (!this._onRowAction) {
283
+ return;
284
+ }
285
+
286
+ const row = this._sorting.getDataIndex(rowIndex);
287
+ const data = this._rows.value[row];
288
+
289
+ if (data) {
290
+ this._onRowAction(actionId, data);
291
+ }
292
+ };
293
+
218
294
  public getCellData = ({ col, row }: DxGridPlanePosition): any => {
219
- const fields = this.table.view?.target?.fields ?? [];
295
+ const fields = this._view?.fields ?? [];
220
296
  if (col < 0 || col >= fields.length) {
221
297
  return undefined;
222
298
  }
@@ -246,7 +322,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
246
322
 
247
323
  public setCellData = ({ col, row }: DxGridPlanePosition, value: any): void => {
248
324
  const rowIdx = this._sorting.getDataIndex(row);
249
- const fields = this.table.view?.target?.fields ?? [];
325
+ const fields = this._view?.fields ?? [];
250
326
  if (col < 0 || col >= fields.length) {
251
327
  return;
252
328
  }
@@ -285,7 +361,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
285
361
  */
286
362
  public updateCellData({ col, row }: DxGridPlanePosition, update: (value: any) => any): void {
287
363
  const dataRow = this._sorting.getDataIndex(row);
288
- const fields = this.table.view?.target?.fields ?? [];
364
+ const fields = this._view?.fields ?? [];
289
365
  const field = fields[col];
290
366
 
291
367
  const value = getValue(this._rows.value[dataRow], field.path);
@@ -298,11 +374,11 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
298
374
  //
299
375
 
300
376
  public deleteColumn(fieldId: string): void {
301
- if (!this.table.view) {
377
+ if (!this._view) {
302
378
  return;
303
379
  }
304
380
 
305
- const field = this.table.view.target?.fields.find((field) => field.id === fieldId);
381
+ const field = this._view?.fields.find((field) => field.id === fieldId);
306
382
  if (field && this._onDeleteColumn) {
307
383
  this._onDeleteColumn(field.id);
308
384
  }
@@ -313,7 +389,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
313
389
  //
314
390
 
315
391
  public setColumnWidth(columnIndex: number, width: number): void {
316
- const fields = this.table.view?.target?.fields ?? [];
392
+ const fields = this._view?.fields ?? [];
317
393
  if (columnIndex < fields.length) {
318
394
  const newWidth = Math.max(0, width);
319
395
  const field = fields[columnIndex];
@@ -98,5 +98,5 @@ const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
98
98
  const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
99
99
  const projection = new ViewProjection(schema, view);
100
100
  const table = create(TableType, { view: makeRef(view) });
101
- return new TableModel({ table, projection, ...props });
101
+ return new TableModel({ id: table.id, view, projection, ...props });
102
102
  };
@@ -34,28 +34,44 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
34
34
  ) {}
35
35
 
36
36
  public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
37
+ let cells: DxGridPlaneCells;
38
+
37
39
  switch (plane) {
38
40
  case 'grid':
39
41
  this._visibleRange.value = range;
40
- return this.getMainGridCells(range);
42
+ cells = this.getMainGridCells(range);
43
+ break;
41
44
  case 'frozenRowsStart':
42
- return this.getHeaderCells(range);
45
+ cells = this.getHeaderCells(range);
46
+ break;
43
47
  case 'frozenColsStart':
44
- return this.getSelectionColumnCells(range);
48
+ cells = this.getSelectionColumnCells(range);
49
+ break;
45
50
  case 'frozenColsEnd':
46
- return this.getActionColumnCells(range);
51
+ cells = this.getActionColumnCells(range);
52
+ break;
47
53
  case 'fixedStartStart':
48
- return this.getSelectAllCell();
54
+ cells = this.getSelectAllCell();
55
+ break;
49
56
  case 'fixedStartEnd':
50
- return this.getNewColumnCell();
57
+ cells = this.getNewColumnCell();
58
+ break;
51
59
  default:
52
- return {};
60
+ cells = {};
61
+ }
62
+
63
+ if (plane === 'grid' && this.model.features.dataEditable === false) {
64
+ Object.values(cells).forEach((cell) => {
65
+ cell.readonly = true;
66
+ });
53
67
  }
68
+
69
+ return cells;
54
70
  }
55
71
 
56
72
  private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
57
73
  const cells: DxGridPlaneCells = {};
58
- const fields = this.model.table.view?.target?.fields ?? [];
74
+ const fields = this.model.view?.fields ?? [];
59
75
 
60
76
  const addCell = (obj: T, field: FieldType, colIndex: number, displayIndex: number): void => {
61
77
  const { props } = this.model.projection.getFieldProjection(field.id);
@@ -151,7 +167,7 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
151
167
 
152
168
  private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
153
169
  const cells: DxGridPlaneCells = {};
154
- const fields = this.model.table.view?.target?.fields ?? [];
170
+ const fields = this.model.view?.fields ?? [];
155
171
  for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
156
172
  const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
157
173
  const sorting = this.model.sorting?.sorting;
@@ -223,9 +239,11 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
223
239
  private getNewColumnCell(): DxGridPlaneCells {
224
240
  return {
225
241
  [toPlaneCellIndex({ col: 0, row: 0 })]: {
226
- accessoryHtml: tableButtons.addColumn.render({
227
- disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
228
- }),
242
+ accessoryHtml: this.model.features.schemaEditable
243
+ ? tableButtons.addColumn.render({
244
+ disabled: (this.model.view?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
245
+ })
246
+ : undefined,
229
247
  className: '!bg-gridHeader',
230
248
  readonly: true,
231
249
  value: '',
@@ -82,6 +82,7 @@ export class TableSorting<T extends BaseTableRow> {
82
82
  if (local?.type === 'active') {
83
83
  return local.sort;
84
84
  }
85
+
85
86
  return this._view?.query.sort?.[0];
86
87
  }
87
88
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Ref, ObjectId, S, TypedObject, Expando } from '@dxos/echo-schema';
5
+ import { EchoObject, Expando, LabelAnnotationId, ObjectId, Ref, S } from '@dxos/echo-schema';
6
6
  // import { ThreadType } from '@dxos/plugin-space/types';
7
7
  import { ViewType } from '@dxos/schema';
8
8
 
@@ -12,11 +12,9 @@ export const TableSchema = S.Struct({
12
12
  view: S.optional(Ref(ViewType)),
13
13
  // TODO(burdon): Should not import from plugin. Either factor out type or use reverse deps when supported.
14
14
  threads: S.optional(S.Array(Ref(Expando /* ThreadType */))), // TODO(dmaretskyi): Breaks edge because plugin-space depends on react-client.
15
+ }).annotations({
16
+ [LabelAnnotationId]: 'name',
15
17
  });
16
18
 
17
- // type TableType = S.Schema.Type<typeof TableSchema>;
18
-
19
- export class TableType extends TypedObject({
20
- typename: 'dxos.org/type/Table',
21
- version: '0.1.0',
22
- })(TableSchema.fields) {}
19
+ export const TableType = TableSchema.pipe(EchoObject('dxos.org/type/Table', '0.1.0'));
20
+ export type TableType = S.Schema.Type<typeof TableType>;
@@ -0,0 +1,71 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type SortDirectionType } from '@dxos/echo-schema';
6
+ import { create, makeRef } from '@dxos/live-object';
7
+ import {
8
+ createView,
9
+ echoSchemaFromPropertyDefinitions,
10
+ ViewProjection,
11
+ type SchemaPropertyDefinition,
12
+ } from '@dxos/schema';
13
+
14
+ import { TableType } from '..';
15
+
16
+ type PropertyDisplayProps = {
17
+ size: number;
18
+ title: string;
19
+ sort: SortDirectionType;
20
+ };
21
+
22
+ export type TablePropertyDefinition = SchemaPropertyDefinition & Partial<PropertyDisplayProps>;
23
+
24
+ export const makeDynamicTable = (typename: string, properties: TablePropertyDefinition[]) => {
25
+ const table = create(TableType, { name: 'dynamic-table' });
26
+ const echoSchema = echoSchemaFromPropertyDefinitions(typename, properties);
27
+ const propertyNames = properties.map((property) => property.name);
28
+
29
+ const view = createView({
30
+ name: 'dynamic-table',
31
+ typename: echoSchema.typename,
32
+ jsonSchema: echoSchema.jsonSchema,
33
+ fields: propertyNames,
34
+ });
35
+
36
+ table.view = makeRef(view);
37
+ const viewProjection = new ViewProjection(echoSchema, view);
38
+
39
+ for (const property of properties) {
40
+ if (!view.fields) {
41
+ continue;
42
+ }
43
+
44
+ const field = view.fields.find((field) => field.path === property.name);
45
+ if (field) {
46
+ if (property.size !== undefined) {
47
+ field.size = property.size;
48
+ }
49
+
50
+ if (property.title !== undefined) {
51
+ const fieldProjection = viewProjection.getFieldProjection(field.id);
52
+ viewProjection.setFieldProjection({
53
+ ...fieldProjection,
54
+ props: { ...fieldProjection.props, title: property.title },
55
+ });
56
+ }
57
+
58
+ if (property.sort) {
59
+ const fieldId = field.id;
60
+ view.query.sort = [{ fieldId, direction: property.sort }];
61
+ }
62
+ }
63
+ }
64
+
65
+ return {
66
+ table,
67
+ schema: echoSchema,
68
+ view,
69
+ viewProjection,
70
+ };
71
+ };
package/src/util/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './dynamic-table';
5
6
  export * from './signals';
6
7
  export * from './table';
7
8
  export * from './table-buttons';
package/src/util/table.ts CHANGED
@@ -4,47 +4,44 @@
4
4
 
5
5
  import {
6
6
  AST,
7
+ type EchoSchema,
7
8
  Format,
8
9
  FormatEnum,
9
10
  type JsonPath,
10
11
  type JsonProp,
11
- type EchoSchema,
12
12
  S,
13
13
  TypedObject,
14
14
  TypeEnum,
15
15
  } from '@dxos/echo-schema';
16
- import { PublicKey } from '@dxos/react-client';
16
+ import { type Client, PublicKey } from '@dxos/react-client';
17
17
  import { create, makeRef, type Space } from '@dxos/react-client/echo';
18
- import { createFieldId, createView, getSchemaProperties, ViewProjection } from '@dxos/schema';
18
+ import { createFieldId, createView, getSchemaProperties, ViewProjection, type ViewType } from '@dxos/schema';
19
19
 
20
20
  import { type TableType } from '../types';
21
21
 
22
22
  type InitialiseTableProps = {
23
+ client: Client;
23
24
  space: Space;
24
25
  table: TableType;
26
+ typename?: string;
25
27
  initialRow?: boolean;
26
- initialSchema?: string;
27
28
  };
28
29
 
29
- // TODO(ZaymonFC): Clean up the branching in this file.
30
-
31
- // TODO(burdon): Pass in type.
32
- // TODO(burdon): User should determine typename.
33
30
  export const initializeTable = async ({
31
+ client,
34
32
  space,
35
33
  table,
34
+ typename,
36
35
  initialRow = true,
37
- initialSchema,
38
- }: InitialiseTableProps): Promise<EchoSchema> => {
39
- if (initialSchema) {
40
- const schema = await space.db.schemaRegistry.query({ typename: initialSchema }).firstOrUndefined();
41
-
36
+ }: InitialiseTableProps): Promise<S.Schema.AnyNoContext> => {
37
+ if (typename) {
38
+ const schema = await space.db.graph.getSchemaByTypename(typename, space.db);
42
39
  if (!schema) {
43
- throw new Error(`Schema not found: ${initialSchema}`);
40
+ throw new Error(`Schema not found: ${typename}`);
44
41
  }
45
42
 
46
- // We need to get the schema properties here. For now, only simple types and refs, not compound types
47
- // are going to be supported.
43
+ // We need to get the schema properties here.
44
+ // For now, only simple types and refs, not compound types are going to be supported.
48
45
  const fields = getSchemaProperties(schema.ast)
49
46
  .filter((prop) => prop.format !== FormatEnum.Ref)
50
47
  .map((prop) => prop.name);
@@ -61,66 +58,71 @@ export const initializeTable = async ({
61
58
 
62
59
  return schema;
63
60
  } else {
64
- const ContactSchema = TypedObject({
65
- typename: `example.com/type/${PublicKey.random().truncate()}`,
66
- version: '0.1.0',
67
- })({
68
- name: S.optional(S.String).annotations({
69
- [AST.TitleAnnotationId]: 'Name',
70
- }),
71
- active: S.optional(S.Boolean),
72
- email: S.optional(Format.Email),
73
- salary: S.optional(Format.Currency()).annotations({
74
- [AST.TitleAnnotationId]: 'Salary',
75
- }), // TODO(burdon): Should default to prop name?
76
- });
77
-
78
- const [contactSchema] = await space.db.schemaRegistry.register([ContactSchema]);
61
+ const [schema] = await space.db.schemaRegistry.register([ContactSchema]);
62
+ const fields = ContactFields;
79
63
 
80
64
  table.view = makeRef(
81
65
  createView({
82
66
  name: 'View',
83
- typename: contactSchema.typename,
84
- jsonSchema: contactSchema.jsonSchema,
85
- fields: ['name', 'email', 'salary', 'active'],
67
+ typename: schema.typename,
68
+ jsonSchema: schema.jsonSchema,
69
+ fields,
86
70
  }),
87
71
  );
88
72
 
89
- const projection = new ViewProjection(contactSchema, table.view.target!);
90
- projection.setFieldProjection({
91
- field: {
92
- id: table.view.target!.fields.find((f) => f.path === 'salary')!.id,
93
- path: 'salary' as JsonPath,
94
- size: 150,
95
- },
96
- });
97
- projection.setFieldProjection({
98
- field: {
99
- id: table.view.target!.fields.find((f) => f.path === 'active')!.id,
100
- path: 'active' as JsonPath,
101
- size: 100,
102
- },
103
- });
73
+ createProjection(schema, table.view.target!);
104
74
 
105
- projection.setFieldProjection({
106
- field: {
107
- id: createFieldId(),
108
- path: 'manager' as JsonPath,
109
- referencePath: 'name' as JsonPath,
110
- },
111
- props: {
112
- property: 'manager' as JsonProp,
113
- type: TypeEnum.Ref,
114
- format: FormatEnum.Ref,
115
- referenceSchema: contactSchema.typename,
116
- title: 'Manager',
117
- },
118
- });
119
75
  if (initialRow) {
120
76
  // TODO(burdon): Last (first) row should not be in db and should be managed by the model.
121
- space.db.add(create(contactSchema, {}));
77
+ space.db.add(create(schema, {}));
122
78
  }
123
79
 
124
- return contactSchema;
80
+ return schema;
125
81
  }
126
82
  };
83
+
84
+ const ContactSchema = TypedObject({
85
+ typename: `example.com/type/${PublicKey.random().truncate()}`,
86
+ version: '0.1.0',
87
+ })({
88
+ name: S.optional(S.String).annotations({ [AST.TitleAnnotationId]: 'Name' }),
89
+ active: S.optional(S.Boolean),
90
+ email: S.optional(Format.Email),
91
+ salary: S.optional(Format.Currency()).annotations({ [AST.TitleAnnotationId]: 'Salary' }),
92
+ });
93
+
94
+ const ContactFields = ['name', 'email', 'salary', 'active'];
95
+
96
+ const createProjection = (schema: EchoSchema, view: ViewType): ViewProjection => {
97
+ const projection = new ViewProjection(schema, view);
98
+ projection.setFieldProjection({
99
+ field: {
100
+ id: view.fields.find((f) => f.path === 'salary')!.id,
101
+ path: 'salary' as JsonPath,
102
+ size: 150,
103
+ },
104
+ });
105
+ projection.setFieldProjection({
106
+ field: {
107
+ id: view.fields.find((f) => f.path === 'active')!.id,
108
+ path: 'active' as JsonPath,
109
+ size: 100,
110
+ },
111
+ });
112
+ projection.setFieldProjection({
113
+ field: {
114
+ id: createFieldId(),
115
+ path: 'manager' as JsonPath,
116
+ referencePath: 'name' as JsonPath,
117
+ },
118
+ props: {
119
+ property: 'manager' as JsonProp,
120
+ type: TypeEnum.Ref,
121
+ format: FormatEnum.Ref,
122
+ referenceSchema: schema.typename,
123
+ title: 'Manager',
124
+ },
125
+ });
126
+
127
+ return projection;
128
+ };