@dxos/react-ui-table 0.7.4 → 0.7.5-labs.a279d8c

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 (116) hide show
  1. package/dist/lib/browser/{chunk-IYCSJDMB.mjs → chunk-3FR6NHPD.mjs} +7 -5
  2. package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1405 -1067
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +1 -1
  7. package/dist/lib/node/{chunk-MHBMVCFZ.cjs → chunk-NB3SFHGA.cjs} +9 -7
  8. package/dist/lib/node/chunk-NB3SFHGA.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +1409 -1051
  10. package/dist/lib/node/index.cjs.map +4 -4
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/types/index.cjs +3 -3
  13. package/dist/lib/node/types/index.cjs.map +1 -1
  14. package/dist/lib/node-esm/{chunk-BMWGPXPL.mjs → chunk-RLVVOZW5.mjs} +7 -5
  15. package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +7 -0
  16. package/dist/lib/node-esm/index.mjs +1405 -1067
  17. package/dist/lib/node-esm/index.mjs.map +4 -4
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/types/index.mjs +1 -1
  20. package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts +1 -1
  21. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +4 -3
  22. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/ColumnSettings.d.ts +3 -2
  24. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/CreateRefPanel.d.ts +3 -2
  26. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RefPanel.d.ts +4 -3
  28. package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -1
  29. package/dist/types/src/components/Table/RowActionsMenu.d.ts +3 -2
  30. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/Table.d.ts +2 -1
  32. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -2
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +18 -0
  38. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
  39. package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
  40. package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
  41. package/dist/types/src/components/index.d.ts +1 -1
  42. package/dist/types/src/components/index.d.ts.map +1 -1
  43. package/dist/types/src/components/testing.d.ts +31 -71
  44. package/dist/types/src/components/testing.d.ts.map +1 -1
  45. package/dist/types/src/index.d.ts +1 -0
  46. package/dist/types/src/index.d.ts.map +1 -1
  47. package/dist/types/src/model/index.d.ts +1 -0
  48. package/dist/types/src/model/index.d.ts.map +1 -1
  49. package/dist/types/src/model/modal-controller.d.ts +6 -2
  50. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  51. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-model.d.ts +19 -31
  53. package/dist/types/src/model/table-model.d.ts.map +1 -1
  54. package/dist/types/src/model/table-presentation.d.ts +22 -0
  55. package/dist/types/src/model/table-presentation.d.ts.map +1 -0
  56. package/dist/types/src/model/table-presentation.test.d.ts +2 -0
  57. package/dist/types/src/model/table-presentation.test.d.ts.map +1 -0
  58. package/dist/types/src/model/table-sorting.d.ts +53 -0
  59. package/dist/types/src/model/table-sorting.d.ts.map +1 -0
  60. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  61. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  62. package/dist/types/src/translations.d.ts +2 -1
  63. package/dist/types/src/translations.d.ts.map +1 -1
  64. package/dist/types/src/types/table.d.ts +46 -154
  65. package/dist/types/src/types/table.d.ts.map +1 -1
  66. package/dist/types/src/util/index.d.ts +1 -0
  67. package/dist/types/src/util/index.d.ts.map +1 -1
  68. package/dist/types/src/util/table-buttons.d.ts +63 -19
  69. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  70. package/dist/types/src/util/table-controls.d.ts +32 -9
  71. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  72. package/dist/types/src/util/table.d.ts +3 -2
  73. package/dist/types/src/util/table.d.ts.map +1 -1
  74. package/dist/types/tsconfig.tsbuildinfo +1 -0
  75. package/package.json +30 -29
  76. package/src/components/Table/ColumnActionsMenu.tsx +12 -12
  77. package/src/components/Table/ColumnSettings.tsx +11 -11
  78. package/src/components/Table/CreateRefPanel.tsx +13 -14
  79. package/src/components/Table/RefPanel.tsx +9 -9
  80. package/src/components/Table/RowActionsMenu.tsx +6 -7
  81. package/src/components/Table/Table.stories.tsx +67 -38
  82. package/src/components/Table/Table.tsx +239 -148
  83. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
  84. package/src/components/TableCellEditor/TableCellEditor.tsx +6 -4
  85. package/src/components/TableToolbar/TableToolbar.tsx +72 -0
  86. package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
  87. package/src/components/index.ts +1 -1
  88. package/src/components/testing.ts +10 -8
  89. package/src/index.ts +2 -0
  90. package/src/model/index.ts +1 -0
  91. package/src/model/modal-controller.ts +15 -48
  92. package/src/model/selection-model.ts +12 -1
  93. package/src/model/table-model.test.ts +4 -76
  94. package/src/model/table-model.ts +73 -304
  95. package/src/model/table-presentation.test.ts +102 -0
  96. package/src/model/table-presentation.ts +223 -0
  97. package/src/model/table-sorting.ts +201 -0
  98. package/src/playwright/TableManager.ts +5 -1
  99. package/src/playwright/smoke.spec.ts +94 -9
  100. package/src/translations.ts +2 -1
  101. package/src/types/table.ts +4 -4
  102. package/src/util/index.ts +1 -0
  103. package/src/util/table-buttons.ts +127 -48
  104. package/src/util/table-controls.ts +71 -7
  105. package/src/util/table.ts +93 -52
  106. package/dist/lib/browser/chunk-IYCSJDMB.mjs.map +0 -7
  107. package/dist/lib/node/chunk-MHBMVCFZ.cjs.map +0 -7
  108. package/dist/lib/node-esm/chunk-BMWGPXPL.mjs.map +0 -7
  109. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
  110. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  111. package/dist/types/src/components/Toolbar/common.d.ts +0 -14
  112. package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
  113. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  114. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  115. package/src/components/Toolbar/Toolbar.tsx +0 -87
  116. package/src/components/Toolbar/common.tsx +0 -71
@@ -2,69 +2,148 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- type TableButton = 'columnSettings' | 'rowMenu' | 'newColumn' | 'referencedCell';
5
+ export type TableButton = 'columnSettings' | 'rowMenu' | 'newColumn' | 'referencedCell' | 'sort';
6
6
 
7
- // TODO(thure): Lots of copy-pasted business here, this could by DRYed out substantially.
8
-
9
- // TODO(thure): These unique attributes come with more caveats than e.g. applying multiple attributes – one key-value
10
- // pair for identifying the button type and another for passing the fieldId.
11
-
12
- const TABLE_ATTRS: { [K in TableButton]: string } = {
13
- referencedCell: 'data-table-ref-cell-button',
7
+ export const BUTTON_IDENTIFIERS: { [K in TableButton]: string } = {
14
8
  columnSettings: 'data-table-column-settings-button',
15
- rowMenu: 'data-table-row-menu-button',
16
9
  newColumn: 'data-table-new-column-button',
10
+ referencedCell: 'data-table-ref-cell-button',
11
+ rowMenu: 'data-table-row-menu-button',
12
+ sort: 'data-table-sort-button',
17
13
  } as const;
18
14
 
19
- const ICONS: { [K in TableButton]: string } = {
20
- referencedCell: 'ph--link-simple-horizontal--regular',
21
- columnSettings: 'ph--caret-down--regular',
22
- rowMenu: 'ph--dots-three--regular',
23
- newColumn: 'ph--plus--regular',
24
- } as const;
15
+ type ButtonData =
16
+ | { type: 'columnSettings'; fieldId: string }
17
+ | { type: 'newColumn'; disabled?: boolean }
18
+ | { type: 'referencedCell'; schemaId: string; targetId: string }
19
+ | { type: 'rowMenu'; rowIndex: number }
20
+ | { type: 'sort'; fieldId: string; direction?: 'asc' | 'desc' };
25
21
 
26
- const createButtonHtml = ({
27
- button,
28
- value = '',
22
+ const createButton = ({
23
+ attr,
24
+ data,
29
25
  disabled = false,
26
+ icon,
30
27
  testId,
28
+ type = 'primary',
31
29
  }: {
32
- button: TableButton;
33
- value?: string;
30
+ attr: string;
31
+ data: Record<string, string>;
34
32
  disabled?: boolean;
33
+ icon: string;
35
34
  testId: string;
36
- }): string => {
37
- const buttonClasses = 'ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-2' as const;
38
- return `<button class="${buttonClasses}" ${TABLE_ATTRS[button]}="${value}" data-testid="${testId}" ${disabled ? 'disabled' : ''}><svg><use href="/icons.svg#${ICONS[button]}"/></svg></button>`;
35
+ type?: 'primary' | 'secondary';
36
+ }) => {
37
+ const dataAttrs = Object.entries(data)
38
+ .map(([k, v]) => `${k}="${v}"`)
39
+ .join(' ');
40
+
41
+ const positionClass = type === 'primary' ? 'inline-end-2' : 'inline-end-9';
42
+ return `<button ${attr} data-testid="${testId}" class="ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? 'disabled' : ''}><svg><use href="/icons.svg#${icon}"/></svg></button>`;
39
43
  };
40
44
 
41
- export const tableButtons = {
42
- referencedCell: {
43
- attr: TABLE_ATTRS.referencedCell,
44
- icon: ICONS.referencedCell,
45
- render: ({ targetId, schemaId }: { targetId: string; schemaId: string }) =>
46
- createButtonHtml({
47
- button: 'referencedCell',
48
- value: `${schemaId}#${targetId}`,
49
- testId: 'table-ref-cell-button',
50
- }),
45
+ const addColumnButton = {
46
+ attr: BUTTON_IDENTIFIERS.newColumn,
47
+ icon: 'ph--plus--regular',
48
+ render: ({ disabled }: Omit<Extract<ButtonData, { type: 'newColumn' }>, 'type'>) => {
49
+ return createButton({
50
+ attr: BUTTON_IDENTIFIERS.newColumn,
51
+ icon: addColumnButton.icon,
52
+ disabled,
53
+ data: {},
54
+ testId: 'table-new-column-button',
55
+ });
51
56
  },
52
- columnSettings: {
53
- attr: TABLE_ATTRS.columnSettings,
54
- icon: ICONS.columnSettings,
55
- render: ({ fieldId }: { fieldId: string }) =>
56
- createButtonHtml({ button: 'columnSettings', value: fieldId, testId: 'table-column-settings-button' }),
57
+ getData: (_el: HTMLElement): Extract<ButtonData, { type: 'newColumn' }> => ({ type: 'newColumn' }),
58
+ } as const;
59
+
60
+ const columnSettingsButton = {
61
+ attr: BUTTON_IDENTIFIERS.columnSettings,
62
+ icon: 'ph--caret-down--regular',
63
+ render: ({ fieldId }: Omit<Extract<ButtonData, { type: 'columnSettings' }>, 'type'>) => {
64
+ return createButton({
65
+ attr: BUTTON_IDENTIFIERS.columnSettings,
66
+ icon: columnSettingsButton.icon,
67
+ data: {
68
+ 'data-field-id': fieldId,
69
+ },
70
+ testId: 'table-column-settings-button',
71
+ });
57
72
  },
58
- rowMenu: {
59
- attr: TABLE_ATTRS.rowMenu,
60
- icon: ICONS.rowMenu,
61
- render: ({ rowIndex }: { rowIndex: number }) =>
62
- createButtonHtml({ button: 'rowMenu', value: rowIndex.toString(), testId: 'table-row-menu-button' }),
73
+ getData: (el: HTMLElement): Extract<ButtonData, { type: 'columnSettings' }> => ({
74
+ type: 'columnSettings',
75
+ fieldId: el.getAttribute('data-field-id')!,
76
+ }),
77
+ } as const;
78
+
79
+ const referencedCellButton = {
80
+ attr: BUTTON_IDENTIFIERS.referencedCell,
81
+ icon: 'ph--link-simple-horizontal--regular',
82
+ render: ({ targetId, schemaId }: Omit<Extract<ButtonData, { type: 'referencedCell' }>, 'type'>) => {
83
+ return createButton({
84
+ attr: BUTTON_IDENTIFIERS.referencedCell,
85
+ icon: referencedCellButton.icon,
86
+ data: {
87
+ 'data-target-id': targetId,
88
+ 'data-schema-id': schemaId,
89
+ },
90
+ testId: 'table-ref-cell-button',
91
+ });
92
+ },
93
+ getData: (el: HTMLElement): Extract<ButtonData, { type: 'referencedCell' }> => ({
94
+ type: 'referencedCell',
95
+ targetId: el.getAttribute('data-target-id')!,
96
+ schemaId: el.getAttribute('data-schema-id')!,
97
+ }),
98
+ } as const;
99
+
100
+ const rowMenuButton = {
101
+ attr: BUTTON_IDENTIFIERS.rowMenu,
102
+ icon: 'ph--dots-three--regular',
103
+ render: ({ rowIndex }: Omit<Extract<ButtonData, { type: 'rowMenu' }>, 'type'>) => {
104
+ return createButton({
105
+ attr: BUTTON_IDENTIFIERS.rowMenu,
106
+ icon: rowMenuButton.icon,
107
+ data: {
108
+ 'data-row-index': rowIndex.toString(),
109
+ },
110
+ testId: 'table-row-menu-button',
111
+ });
63
112
  },
64
- addColumn: {
65
- attr: TABLE_ATTRS.newColumn,
66
- icon: ICONS.newColumn,
67
- render: ({ disabled }: { disabled?: boolean } = {}) =>
68
- createButtonHtml({ button: 'newColumn', disabled, testId: 'table-new-column-button' }),
113
+ getData: (el: HTMLElement): Extract<ButtonData, { type: 'rowMenu' }> => ({
114
+ type: 'rowMenu',
115
+ rowIndex: Number(el.getAttribute('data-row-index')!),
116
+ }),
117
+ } as const;
118
+
119
+ const sortButton = {
120
+ attr: BUTTON_IDENTIFIERS.sort,
121
+ icon: 'ph--sort-ascending--regular',
122
+ render: ({ fieldId, direction }: Omit<Extract<ButtonData, { type: 'sort' }>, 'type'>) => {
123
+ return createButton({
124
+ attr: BUTTON_IDENTIFIERS.sort,
125
+ icon: direction === 'desc' ? 'ph--sort-descending--regular' : 'ph--sort-ascending--regular',
126
+ data: {
127
+ 'data-field-id': fieldId,
128
+ 'data-direction': direction ?? '',
129
+ },
130
+ testId: 'table-sort-button',
131
+ // TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
132
+ // This is a bit hacky.
133
+ type: 'secondary',
134
+ });
69
135
  },
136
+ getData: (el: HTMLElement): Extract<ButtonData, { type: 'sort' }> => ({
137
+ type: 'sort',
138
+ fieldId: el.getAttribute('data-field-id')!,
139
+ direction: el.getAttribute('data-direction') as 'asc' | 'desc' | undefined,
140
+ }),
141
+ } as const;
142
+
143
+ export const tableButtons = {
144
+ addColumn: addColumnButton,
145
+ columnSettings: columnSettingsButton,
146
+ referencedCell: referencedCellButton,
147
+ rowMenu: rowMenuButton,
148
+ sort: sortButton,
70
149
  } as const;
@@ -2,26 +2,90 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export const checkboxAttributes = {
5
+ // Core types.
6
+ export type TableControl = 'checkbox' | 'switch';
7
+
8
+ export type ControlData =
9
+ | { type: 'checkbox'; rowIndex: number; header?: boolean }
10
+ | { type: 'switch'; colIndex: number; rowIndex: number };
11
+
12
+ type CommonRenderProps = {
13
+ checked?: boolean;
14
+ disabled?: boolean;
15
+ };
16
+
17
+ export type RenderCheckboxProps = CommonRenderProps & Omit<Extract<ControlData, { type: 'checkbox' }>, 'type'>;
18
+ export type RenderSwitchProps = CommonRenderProps & Omit<Extract<ControlData, { type: 'switch' }>, 'type'>;
19
+
20
+ export const CONTROL_IDENTIFIERS = {
6
21
  checkbox: 'data-table-checkbox',
7
- header: 'data-table-header',
22
+ switch: 'data-table-switch',
8
23
  } as const;
9
24
 
10
- export type RenderCheckboxProps = { rowIndex: number; header?: boolean; checked?: boolean; disabled?: boolean };
25
+ const BASE_CLASSES = {
26
+ checkbox: 'absolute inset-block-[.375rem] inline-end-[.375rem] ch-checkbox',
27
+ switch: 'absolute inset-block-[.25rem] inline-end-[.25rem] ch-checkbox--switch',
28
+ } as const;
29
+
30
+ const renderAttributes = (data: Record<string, string>) => {
31
+ return Object.entries(data)
32
+ .map(([k, v]) => `${k}="${v}"`)
33
+ .join(' ');
34
+ };
35
+
36
+ const renderInput = (
37
+ baseClass: string,
38
+ attrs: Record<string, string>,
39
+ checked = false,
40
+ disabled = false,
41
+ preventToggle = false,
42
+ testId: string,
43
+ ) => {
44
+ return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? 'checked' : ''} ${preventToggle ? 'onclick="return false"' : ''} ${disabled ? 'disabled' : ''} data-testid="${testId}"/>`;
45
+ };
46
+
11
47
  export const renderCheckbox = ({
12
48
  rowIndex,
13
49
  header = false,
14
50
  checked = false,
15
51
  disabled = false,
16
52
  }: RenderCheckboxProps): string => {
17
- const baseClasses = 'absolute inset-block-[6px] inline-end-[6px] ch-checkbox' as const;
18
- const { attributes } = tableControls.checkbox;
19
- return `<input type="checkbox" class="${baseClasses}" ${attributes.checkbox}="${rowIndex}" ${header ? attributes.header : ''} ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''} data-testid="table-selection"/>`;
53
+ const attrs = {
54
+ [CONTROL_IDENTIFIERS.checkbox]: '',
55
+ 'data-row-index': rowIndex.toString(),
56
+ ...(header && { 'data-header': '' }),
57
+ };
58
+
59
+ return renderInput(BASE_CLASSES.checkbox, attrs, checked, disabled, true, 'table-selection');
60
+ };
61
+
62
+ export const renderSwitch = ({ colIndex, rowIndex, checked = false, disabled = false }: RenderSwitchProps): string => {
63
+ const attrs = {
64
+ [CONTROL_IDENTIFIERS.switch]: '',
65
+ 'data-row-index': rowIndex.toString(),
66
+ 'data-col-index': colIndex.toString(),
67
+ };
68
+
69
+ return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, 'table-switch');
20
70
  };
21
71
 
22
72
  export const tableControls = {
23
73
  checkbox: {
24
- attributes: checkboxAttributes,
74
+ attr: CONTROL_IDENTIFIERS.checkbox,
25
75
  render: renderCheckbox,
76
+ getData: (el: HTMLElement): Extract<ControlData, { type: 'checkbox' }> => ({
77
+ type: 'checkbox',
78
+ rowIndex: Number(el.getAttribute('data-row-index')),
79
+ header: el.hasAttribute('data-header'),
80
+ }),
81
+ },
82
+ switch: {
83
+ attr: CONTROL_IDENTIFIERS.switch,
84
+ render: renderSwitch,
85
+ getData: (el: HTMLElement): Extract<ControlData, { type: 'switch' }> => ({
86
+ type: 'switch',
87
+ rowIndex: Number(el.getAttribute('data-row-index')),
88
+ colIndex: Number(el.getAttribute('data-col-index')),
89
+ }),
26
90
  },
27
91
  } as const;
package/src/util/table.ts CHANGED
@@ -8,15 +8,14 @@ import {
8
8
  FormatEnum,
9
9
  type JsonPath,
10
10
  type JsonProp,
11
- type MutableSchema,
11
+ type EchoSchema,
12
12
  S,
13
13
  TypedObject,
14
14
  TypeEnum,
15
15
  } from '@dxos/echo-schema';
16
- import { log } from '@dxos/log';
17
16
  import { PublicKey } from '@dxos/react-client';
18
- import { create, type Space } from '@dxos/react-client/echo';
19
- import { createFieldId, createView, ViewProjection } from '@dxos/schema';
17
+ import { create, makeRef, type Space } from '@dxos/react-client/echo';
18
+ import { createFieldId, createView, getSchemaProperties, ViewProjection } from '@dxos/schema';
20
19
 
21
20
  import { type TableType } from '../types';
22
21
 
@@ -24,62 +23,104 @@ type InitialiseTableProps = {
24
23
  space: Space;
25
24
  table: TableType;
26
25
  initialRow?: boolean;
26
+ initialSchema?: string;
27
27
  };
28
28
 
29
+ // TODO(ZaymonFC): Clean up the branching in this file.
30
+
29
31
  // TODO(burdon): Pass in type.
30
32
  // TODO(burdon): User should determine typename.
31
- export const initializeTable = ({ space, table, initialRow = true }: InitialiseTableProps): MutableSchema => {
32
- log.info('initializeTable', { table });
33
+ export const initializeTable = async ({
34
+ space,
35
+ table,
36
+ initialRow = true,
37
+ initialSchema,
38
+ }: InitialiseTableProps): Promise<EchoSchema> => {
39
+ if (initialSchema) {
40
+ const schema = await space.db.schemaRegistry.query({ typename: initialSchema }).firstOrUndefined();
33
41
 
34
- const ContactSchema = TypedObject({
35
- typename: `example.com/type/${PublicKey.random().truncate()}`,
36
- version: '0.1.0',
37
- })({
38
- name: S.optional(S.String).annotations({
39
- [AST.TitleAnnotationId]: 'Name',
40
- }),
41
- email: S.optional(Format.Email),
42
- salary: S.optional(Format.Currency()).annotations({
43
- [AST.TitleAnnotationId]: 'Salary',
44
- }), // TODO(burdon): Should default to prop name?
45
- });
42
+ if (!schema) {
43
+ throw new Error(`Schema not found: ${initialSchema}`);
44
+ }
46
45
 
47
- const contactSchema = space.db.schemaRegistry.addSchema(ContactSchema);
48
- table.view = createView({
49
- name: 'Test',
50
- typename: contactSchema.typename,
51
- jsonSchema: contactSchema.jsonSchema,
52
- fields: ['name', 'email', 'salary'],
53
- });
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.
48
+ const fields = getSchemaProperties(schema.ast)
49
+ .filter((prop) => prop.type !== 'object' || prop.format === FormatEnum.Ref)
50
+ .map((prop) => prop.name);
54
51
 
55
- const projection = new ViewProjection(contactSchema, table.view!);
56
- projection.setFieldProjection({
57
- field: {
58
- id: table.view.fields[2].id,
59
- path: 'salary' as JsonPath,
60
- size: 150,
61
- },
62
- });
52
+ table.view = makeRef(
53
+ createView({
54
+ // TODO(ZaymonFC): Don't hardcode name?
55
+ name: 'View',
56
+ typename: schema.typename,
57
+ jsonSchema: schema.jsonSchema,
58
+ fields,
59
+ }),
60
+ );
63
61
 
64
- projection.setFieldProjection({
65
- field: {
66
- id: createFieldId(),
67
- path: 'manager' as JsonPath,
68
- referencePath: 'name' as JsonPath,
69
- },
70
- props: {
71
- property: 'manager' as JsonProp,
72
- type: TypeEnum.Ref,
73
- format: FormatEnum.Ref,
74
- referenceSchema: contactSchema.typename,
75
- title: 'Manager',
76
- },
77
- });
62
+ return schema;
63
+ } 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
+ });
78
77
 
79
- if (initialRow) {
80
- // TODO(burdon): Last (first) row should not be in db and should be managed by the model.
81
- space.db.add(create(contactSchema, {}));
82
- }
78
+ const [contactSchema] = await space.db.schemaRegistry.register([ContactSchema]);
79
+
80
+ table.view = makeRef(
81
+ createView({
82
+ name: 'View',
83
+ typename: contactSchema.typename,
84
+ jsonSchema: contactSchema.jsonSchema,
85
+ fields: ['name', 'email', 'salary', 'active'],
86
+ }),
87
+ );
83
88
 
84
- return contactSchema;
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
+ });
104
+
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
+ if (initialRow) {
120
+ // TODO(burdon): Last (first) row should not be in db and should be managed by the model.
121
+ space.db.add(create(contactSchema, {}));
122
+ }
123
+
124
+ return contactSchema;
125
+ }
85
126
  };
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types/table.ts"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, ObjectId, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport const TableSchema = S.Struct({\n id: ObjectId, // TODO(burdon): Where should this be?\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n // TODO(burdon): Should not import from plugin. Either factor out type or use reverse deps when supported.\n threads: S.optional(S.Array(ref(ThreadType))),\n});\n\n// type TableType = S.Schema.Type<typeof TableSchema>;\n\nexport class TableType extends TypedObject({\n typename: 'dxos.org/type/Table',\n version: '0.1.0',\n})(TableSchema.fields) {}\n"],
5
- "mappings": ";AAIA,SAASA,KAAKC,UAAUC,GAAGC,mBAAmB;AAC9C,SAASC,kBAAkB;AAC3B,SAASC,gBAAgB;AAElB,IAAMC,cAAcC,EAAEC,OAAO;EAClCC,IAAIC;EACJC,MAAMJ,EAAEK,SAASL,EAAEM,MAAM;EACzBC,MAAMP,EAAEK,SAASG,IAAIC,QAAAA,CAAAA;;EAErBC,SAASV,EAAEK,SAASL,EAAEW,MAAMH,IAAII,UAAAA,CAAAA,CAAAA;AAClC,CAAA;AAIO,IAAMC,YAAN,cAAwBC,YAAY;EACzCC,UAAU;EACVC,SAAS;AACX,CAAA,EAAGjB,YAAYkB,MAAM,EAAA;AAAG;",
6
- "names": ["ref", "ObjectId", "S", "TypedObject", "ThreadType", "ViewType", "TableSchema", "S", "Struct", "id", "ObjectId", "name", "optional", "String", "view", "ref", "ViewType", "threads", "Array", "ThreadType", "TableType", "TypedObject", "typename", "version", "fields"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types/table.ts"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, ObjectId, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport const TableSchema = S.Struct({\n id: ObjectId, // TODO(burdon): Where should this be?\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n // TODO(burdon): Should not import from plugin. Either factor out type or use reverse deps when supported.\n threads: S.optional(S.Array(ref(ThreadType))),\n});\n\n// type TableType = S.Schema.Type<typeof TableSchema>;\n\nexport class TableType extends TypedObject({\n typename: 'dxos.org/type/Table',\n version: '0.1.0',\n})(TableSchema.fields) {}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAIA,yBAA8C;AAC9C,0BAA2B;AAC3B,oBAAyB;AAElB,IAAMA,cAAcC,qBAAEC,OAAO;EAClCC,IAAIC;EACJC,MAAMJ,qBAAEK,SAASL,qBAAEM,MAAM;EACzBC,MAAMP,qBAAEK,aAASG,wBAAIC,sBAAAA,CAAAA;;EAErBC,SAASV,qBAAEK,SAASL,qBAAEW,UAAMH,wBAAII,8BAAAA,CAAAA,CAAAA;AAClC,CAAA;AAIO,IAAMC,YAAN,kBAAwBC,gCAAY;EACzCC,UAAU;EACVC,SAAS;AACX,CAAA,EAAGjB,YAAYkB,MAAM,EAAA;AAAG;",
6
- "names": ["TableSchema", "S", "Struct", "id", "ObjectId", "name", "optional", "String", "view", "ref", "ViewType", "threads", "Array", "ThreadType", "TableType", "TypedObject", "typename", "version", "fields"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types/table.ts"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { ref, ObjectId, S, TypedObject } from '@dxos/echo-schema';\nimport { ThreadType } from '@dxos/plugin-space';\nimport { ViewType } from '@dxos/schema';\n\nexport const TableSchema = S.Struct({\n id: ObjectId, // TODO(burdon): Where should this be?\n name: S.optional(S.String),\n view: S.optional(ref(ViewType)),\n // TODO(burdon): Should not import from plugin. Either factor out type or use reverse deps when supported.\n threads: S.optional(S.Array(ref(ThreadType))),\n});\n\n// type TableType = S.Schema.Type<typeof TableSchema>;\n\nexport class TableType extends TypedObject({\n typename: 'dxos.org/type/Table',\n version: '0.1.0',\n})(TableSchema.fields) {}\n"],
5
- "mappings": ";;;AAIA,SAASA,KAAKC,UAAUC,GAAGC,mBAAmB;AAC9C,SAASC,kBAAkB;AAC3B,SAASC,gBAAgB;AAElB,IAAMC,cAAcC,EAAEC,OAAO;EAClCC,IAAIC;EACJC,MAAMJ,EAAEK,SAASL,EAAEM,MAAM;EACzBC,MAAMP,EAAEK,SAASG,IAAIC,QAAAA,CAAAA;;EAErBC,SAASV,EAAEK,SAASL,EAAEW,MAAMH,IAAII,UAAAA,CAAAA,CAAAA;AAClC,CAAA;AAIO,IAAMC,YAAN,cAAwBC,YAAY;EACzCC,UAAU;EACVC,SAAS;AACX,CAAA,EAAGjB,YAAYkB,MAAM,EAAA;AAAG;",
6
- "names": ["ref", "ObjectId", "S", "TypedObject", "ThreadType", "ViewType", "TableSchema", "S", "Struct", "id", "ObjectId", "name", "optional", "String", "view", "ref", "ViewType", "threads", "Array", "ThreadType", "TableType", "TypedObject", "typename", "version", "fields"]
7
- }
@@ -1,21 +0,0 @@
1
- import React, { type PropsWithChildren } from 'react';
2
- import { type ThemedClassName } from '@dxos/react-ui';
3
- export type ToolbarAction = {
4
- type: 'add-row';
5
- } | {
6
- type: 'comment';
7
- };
8
- export type ToolbarActionType = ToolbarAction['type'];
9
- export type ToolbarActionHandler = (action: ToolbarAction) => void;
10
- export type ToolbarProps = ThemedClassName<PropsWithChildren<{
11
- onAction?: ToolbarActionHandler;
12
- }>>;
13
- declare const useToolbarContext: (consumerName: string) => ToolbarProps;
14
- export declare const Toolbar: {
15
- Root: ({ classNames, children, onAction }: ToolbarProps) => React.JSX.Element;
16
- Editing: () => React.JSX.Element;
17
- Actions: () => React.JSX.Element;
18
- Separator: () => React.JSX.Element;
19
- };
20
- export { useToolbarContext };
21
- //# sourceMappingURL=Toolbar.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../../../../src/components/Toolbar/Toolbar.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAA6B,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AAUjG,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;AAEnE,MAAM,MAAM,YAAY,GAAG,eAAe,CACxC,iBAAiB,CAAC;IAChB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC,CAAC,CACH,CAAC;AAEF,QAAA,MAA+B,iBAAiB,wCAA0C,CAAC;AAkD3F,eAAO,MAAM,OAAO;+CAhDqC,YAAY;;;;CAqDpE,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1,14 +0,0 @@
1
- import React from 'react';
2
- import { type IconProps, type ToolbarButtonProps as NaturalToolbarButtonProps, type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps } from '@dxos/react-ui';
3
- export declare const buttonStyles = "min-bs-0 p-2";
4
- export declare const tooltipProps: {
5
- side: "top";
6
- classNames: string;
7
- };
8
- export declare const ToolbarSeparator: () => React.JSX.Element;
9
- type ToolbarButtonProps = NaturalToolbarButtonProps & Pick<IconProps, 'icon'>;
10
- export declare const ToolbarButton: ({ icon, children, ...props }: ToolbarButtonProps) => React.JSX.Element;
11
- export type ToolbarToggleButtonProps = NaturalToolbarToggleGroupItemProps & Pick<IconProps, 'icon'>;
12
- export declare const ToolbarToggleButton: ({ icon, children, ...props }: ToolbarToggleButtonProps) => React.JSX.Element;
13
- export {};
14
- //# sourceMappingURL=common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../../src/components/Toolbar/common.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAEL,KAAK,SAAS,EAGd,KAAK,kBAAkB,IAAI,yBAAyB,EACpD,KAAK,2BAA2B,IAAI,kCAAkC,EACvE,MAAM,gBAAgB,CAAC;AAIxB,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAC3C,eAAO,MAAM,YAAY;;;CAA+C,CAAC;AAEzE,eAAO,MAAM,gBAAgB,yBAAkD,CAAC;AAMhF,KAAK,kBAAkB,GAAG,yBAAyB,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAE9E,eAAO,MAAM,aAAa,iCAAkC,kBAAkB,sBAiB7E,CAAC;AAMF,MAAM,MAAM,wBAAwB,GAAG,kCAAkC,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEpG,eAAO,MAAM,mBAAmB,iCAAkC,wBAAwB,sBAiBzF,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './Toolbar';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/Toolbar/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC"}
@@ -1,87 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { createContext } from '@radix-ui/react-context';
6
- import React, { type PropsWithChildren } from 'react';
7
-
8
- import { Toolbar as NaturalToolbar, type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
-
10
- import { ToolbarButton, ToolbarSeparator } from './common';
11
- import { translationKey } from '../../translations';
12
-
13
- //
14
- // Root
15
- //
16
-
17
- // TODO(Zan): This should become a union later.
18
- export type ToolbarAction = { type: 'add-row' } | { type: 'comment' };
19
-
20
- export type ToolbarActionType = ToolbarAction['type'];
21
-
22
- export type ToolbarActionHandler = (action: ToolbarAction) => void;
23
-
24
- export type ToolbarProps = ThemedClassName<
25
- PropsWithChildren<{
26
- onAction?: ToolbarActionHandler;
27
- }>
28
- >;
29
-
30
- const [ToolbarContextProvider, useToolbarContext] = createContext<ToolbarProps>('Toolbar');
31
-
32
- const ToolbarRoot = ({ classNames, children, onAction }: ToolbarProps) => {
33
- return (
34
- <ToolbarContextProvider onAction={onAction}>
35
- <NaturalToolbar.Root
36
- classNames={['is-full shrink-0 overflow-x-auto overflow-y-hidden p-1 attention-surface', classNames]}
37
- >
38
- {children}
39
- </NaturalToolbar.Root>
40
- </ToolbarContextProvider>
41
- );
42
- };
43
-
44
- const Editing = () => {
45
- const { onAction } = useToolbarContext('Editing');
46
- const { t } = useTranslation(translationKey);
47
-
48
- return (
49
- <>
50
- <ToolbarButton
51
- value='add-row'
52
- icon='ph--plus--regular'
53
- data-testid='table.toolbar.add-row'
54
- onClick={() => onAction?.({ type: 'add-row' })}
55
- >
56
- {t('add row')}
57
- </ToolbarButton>
58
- </>
59
- );
60
- };
61
-
62
- const Actions = () => {
63
- const { onAction } = useToolbarContext('Actions');
64
- const { t } = useTranslation(translationKey);
65
-
66
- return (
67
- <>
68
- <ToolbarButton
69
- value='comment'
70
- icon='ph--chat-text--regular'
71
- data-testid='table.toolbar.comment'
72
- onClick={() => onAction?.({ type: 'comment' })}
73
- >
74
- {t('create comment')}
75
- </ToolbarButton>
76
- </>
77
- );
78
- };
79
-
80
- export const Toolbar = {
81
- Root: ToolbarRoot,
82
- Editing,
83
- Actions,
84
- Separator: ToolbarSeparator,
85
- };
86
-
87
- export { useToolbarContext };