@dxos/react-ui-table 0.8.4-main.bc674ce → 0.8.4-main.c351d160a8
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.
- package/dist/lib/browser/{chunk-OZBLY7XM.mjs → chunk-BF54SSNL.mjs} +12 -18
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-PTHMYAAM.mjs} +33 -28
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +178 -176
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +3 -3
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-GI72MQSG.mjs} +33 -28
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-SIRDIPHZ.mjs} +12 -18
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +178 -176
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +3 -3
- package/dist/lib/node-esm/testing/index.mjs.map +2 -2
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +6 -6
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/model/table-model.d.ts +5 -4
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +1 -6
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +6 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -42
- package/src/components/Table/ColumnSettings.tsx +3 -4
- package/src/components/Table/DynamicTable.stories.tsx +6 -5
- package/src/components/Table/DynamicTable.tsx +11 -10
- package/src/components/Table/Relations.stories.tsx +13 -10
- package/src/components/Table/Table.stories.tsx +54 -32
- package/src/components/Table/Table.tsx +51 -56
- package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
- package/src/components/TableCellEditor/FormCellEditor.tsx +11 -5
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +4 -3
- package/src/components/TableCellEditor/TableCellEditor.tsx +3 -3
- package/src/components/TableToolbar/TableToolbar.tsx +6 -6
- package/src/components/testing.ts +1 -1
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useProjectionModel.ts +3 -3
- package/src/hooks/useTableModel.ts +1 -1
- package/src/model/table-model.test.ts +5 -5
- package/src/model/table-model.ts +9 -8
- package/src/model/table-presentation.test.ts +5 -5
- package/src/model/table-presentation.ts +12 -11
- package/src/playwright/TableManager.ts +10 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +45 -2
- package/src/testing/useTestTableModel.ts +3 -3
- package/src/types/Table.ts +11 -22
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +13 -12
- package/src/util/query-ast.ts +3 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
|
@@ -35,11 +35,13 @@ import {
|
|
|
35
35
|
gridSeparatorInlineEnd,
|
|
36
36
|
} from '@dxos/react-ui-grid';
|
|
37
37
|
import { DxEditRequest } from '@dxos/react-ui-grid';
|
|
38
|
-
import { mx } from '@dxos/ui-theme';
|
|
38
|
+
import { composableProps, mx } from '@dxos/ui-theme';
|
|
39
|
+
import { type ComposableProps } from '@dxos/ui-types';
|
|
39
40
|
|
|
40
41
|
import { type InsertRowResult, ModalController, type TableModel, type TablePresentation } from '../../model';
|
|
41
42
|
import { tableButtons, tableControls } from '../../util';
|
|
42
43
|
import { type OnCreateHandler, type TableCellEditorProps, TableValueEditor } from '../TableCellEditor';
|
|
44
|
+
import { TableToolbar } from '../TableToolbar';
|
|
43
45
|
|
|
44
46
|
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
45
47
|
import { ColumnSettings } from './ColumnSettings';
|
|
@@ -53,51 +55,37 @@ const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
|
|
|
53
55
|
// Table.Root
|
|
54
56
|
//
|
|
55
57
|
|
|
56
|
-
export type TableRootProps = PropsWithChildren
|
|
58
|
+
export type TableRootProps = PropsWithChildren;
|
|
57
59
|
|
|
58
|
-
const TableRoot = ({ children
|
|
59
|
-
return
|
|
60
|
-
<div
|
|
61
|
-
role='none'
|
|
62
|
-
className={mx(
|
|
63
|
-
'relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
|
|
64
|
-
role === 'card--popover' && 'popover-card-height',
|
|
65
|
-
role === 'section' && 'attention-surface',
|
|
66
|
-
role === 'card--intrinsic' && '[&_.dx-grid]:bs-[--dx-grid-content-block-size]',
|
|
67
|
-
['card--popover', 'section', 'card--extrinsic'].includes(role) && 'overflow-hidden',
|
|
68
|
-
['article', 'slide'].includes(role) && 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0',
|
|
69
|
-
)}
|
|
70
|
-
>
|
|
71
|
-
{children}
|
|
72
|
-
</div>
|
|
73
|
-
);
|
|
60
|
+
const TableRoot = ({ children }: TableRootProps) => {
|
|
61
|
+
return <>{children}</>;
|
|
74
62
|
};
|
|
75
63
|
|
|
76
64
|
//
|
|
77
65
|
// Table.Main
|
|
78
66
|
//
|
|
79
67
|
|
|
68
|
+
const emptyRowsAtom = Atom.make<unknown[]>([]);
|
|
69
|
+
const emptyCellUpdateAtom = Atom.make<number>(0);
|
|
70
|
+
|
|
80
71
|
export type TableController = {
|
|
81
72
|
update?: (cell?: DxGridPosition) => void;
|
|
82
73
|
handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
|
|
83
74
|
};
|
|
84
75
|
|
|
85
|
-
export type TableMainProps<T extends Type.
|
|
76
|
+
export type TableMainProps<T extends Type.AnyEntity = Type.AnyEntity> = ComposableProps<HTMLDivElement> & {
|
|
86
77
|
schema?: T;
|
|
87
78
|
model?: TableModel;
|
|
88
79
|
presentation?: TablePresentation;
|
|
89
|
-
// TODO(burdon):
|
|
80
|
+
// TODO(burdon): Factor out attention.
|
|
90
81
|
ignoreAttention?: boolean;
|
|
91
82
|
onCreate?: OnCreateHandler;
|
|
92
83
|
onRowClick?: (row: any) => void;
|
|
93
84
|
testId?: string;
|
|
94
85
|
};
|
|
95
86
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const TableMainInner = <T extends Type.Entity.Any = Type.Entity.Any>(
|
|
100
|
-
{ schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId }: TableMainProps<T>,
|
|
87
|
+
const TableMainInner = <T extends Type.AnyEntity = Type.AnyEntity>(
|
|
88
|
+
{ schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId, ...props }: TableMainProps<T>,
|
|
101
89
|
forwardedRef: Ref<TableController>,
|
|
102
90
|
) => {
|
|
103
91
|
const registry = useContext(RegistryContext);
|
|
@@ -424,47 +412,54 @@ const TableMainInner = <T extends Type.Entity.Any = Type.Entity.Any>(
|
|
|
424
412
|
}, [dxGrid, model, presentation]);
|
|
425
413
|
|
|
426
414
|
if (!model || !modals) {
|
|
427
|
-
return <span role='none' className='attention-surface' />;
|
|
415
|
+
return <span role='none' className='dx-attention-surface' />;
|
|
428
416
|
}
|
|
429
417
|
|
|
430
418
|
return (
|
|
431
|
-
<
|
|
432
|
-
<
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
419
|
+
<div {...composableProps(props, { role: 'none', className: 'dx-container relative' })}>
|
|
420
|
+
<Grid.Root id={model.id ?? 'table-grid'}>
|
|
421
|
+
<TableValueEditor<T>
|
|
422
|
+
model={model}
|
|
423
|
+
modals={modals}
|
|
424
|
+
schema={schema}
|
|
425
|
+
onFocus={handleFocus}
|
|
426
|
+
onSave={handleSave}
|
|
427
|
+
onCreate={onCreate}
|
|
428
|
+
/>
|
|
429
|
+
<Grid.Content
|
|
430
|
+
className={mx('[--dx-grid-base:var(--base-surface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
|
|
431
|
+
frozen={frozen}
|
|
432
|
+
columns={columnMeta}
|
|
433
|
+
columnDefault={columnDefault}
|
|
434
|
+
rowDefault={rowDefault}
|
|
435
|
+
limitRows={rows.length}
|
|
436
|
+
limitColumns={columnCount}
|
|
437
|
+
overscroll='trap'
|
|
438
|
+
onAxisResize={handleAxisResize}
|
|
439
|
+
onClick={handleGridClick}
|
|
440
|
+
onKeyDownCapture={handleKeyDown}
|
|
441
|
+
onWheelCapture={handleWheel}
|
|
442
|
+
{...(testId && { 'data-testid': testId })}
|
|
443
|
+
ref={setDxGrid}
|
|
444
|
+
/>
|
|
445
|
+
<RowActionsMenu model={model} modals={modals} />
|
|
446
|
+
<ColumnActionsMenu model={model} modals={modals} />
|
|
447
|
+
<ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
|
|
448
|
+
</Grid.Root>
|
|
449
|
+
</div>
|
|
460
450
|
);
|
|
461
451
|
};
|
|
462
452
|
|
|
463
|
-
const TableMain = forwardRef(TableMainInner) as <T extends Type.
|
|
453
|
+
const TableMain = forwardRef(TableMainInner) as <T extends Type.AnyEntity = Type.AnyEntity>(
|
|
464
454
|
props: TableMainProps<T> & { ref?: Ref<TableController> },
|
|
465
455
|
) => JSX.Element;
|
|
466
456
|
|
|
457
|
+
//
|
|
458
|
+
// Table
|
|
459
|
+
//
|
|
460
|
+
|
|
467
461
|
export const Table = {
|
|
468
462
|
Root: TableRoot,
|
|
469
463
|
Main: TableMain,
|
|
464
|
+
Toolbar: TableToolbar,
|
|
470
465
|
};
|
|
@@ -22,15 +22,15 @@ export const CellValidationMessage = ({
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const bgClass = variant === 'error' ? 'bg-
|
|
26
|
-
const textClass = variant === 'error' ? 'text-
|
|
25
|
+
const bgClass = variant === 'error' ? 'bg-error-surface' : 'bg-warning-surface';
|
|
26
|
+
const textClass = variant === 'error' ? 'text-error-surface-text' : 'text-warning-surface-text';
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<div
|
|
30
30
|
role='none'
|
|
31
|
-
className={`absolute ${bgClass} ${textClass} rounded-
|
|
31
|
+
className={`absolute ${bgClass} ${textClass} rounded-h-sm text-xs p-1`}
|
|
32
32
|
style={{
|
|
33
|
-
...{ '--dx-
|
|
33
|
+
...{ '--dx-grid-cell-width': `${box?.inlineSize ?? 200}px` },
|
|
34
34
|
zIndex: 10,
|
|
35
35
|
insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
|
|
36
36
|
insetInlineStart: box.insetInlineStart,
|
|
@@ -20,9 +20,9 @@ import { narrowSchema } from '../../util';
|
|
|
20
20
|
|
|
21
21
|
const createOptionLabel: Label = ['create new object label', { ns: translationKey }];
|
|
22
22
|
|
|
23
|
-
export type OnCreateHandler = (schema: Type.
|
|
23
|
+
export type OnCreateHandler = (schema: Type.AnyEntity, values: any) => Parameters<typeof Ref.make>[0];
|
|
24
24
|
|
|
25
|
-
export type FormCellEditorProps<T extends Type.
|
|
25
|
+
export type FormCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
|
|
26
26
|
__gridScope: any;
|
|
27
27
|
schema?: T;
|
|
28
28
|
model?: TableModel;
|
|
@@ -32,7 +32,7 @@ export type FormCellEditorProps<T extends Type.Entity.Any = Type.Entity.Any> = {
|
|
|
32
32
|
onCreate?: OnCreateHandler;
|
|
33
33
|
} & Omit<FormRootProps<any>, 'values' | 'schema' | 'onCreate'>;
|
|
34
34
|
|
|
35
|
-
export const FormCellEditor = <T extends Type.
|
|
35
|
+
export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
36
36
|
__gridScope,
|
|
37
37
|
schema,
|
|
38
38
|
model,
|
|
@@ -85,8 +85,12 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
|
|
|
85
85
|
|
|
86
86
|
// NOTE: Important to get a mutable deep clone to eject from the echo object.
|
|
87
87
|
// TODO(wittjosiah): Consider using something like Obj.clone for this use case.
|
|
88
|
-
const
|
|
88
|
+
const initialFormValues = useMemo(() => (originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}), [originalRow]);
|
|
89
|
+
const [formValues, setFormValues] = useState<any>(initialFormValues);
|
|
89
90
|
|
|
91
|
+
const handleValuesChanged = useCallback<NonNullable<FormRootProps<any>['onValuesChanged']>>((values) => {
|
|
92
|
+
setFormValues(values);
|
|
93
|
+
}, []);
|
|
90
94
|
const handleOpenChange = useCallback((nextOpen: boolean) => {
|
|
91
95
|
if (nextOpen === false) {
|
|
92
96
|
setEditing(null);
|
|
@@ -162,7 +166,7 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
|
|
|
162
166
|
<Popover.Root open={editing} onOpenChange={handleOpenChange}>
|
|
163
167
|
<Popover.VirtualTrigger virtualRef={anchorRef} />
|
|
164
168
|
<Popover.Portal>
|
|
165
|
-
<Popover.Content tabIndex={-1} classNames='
|
|
169
|
+
<Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-fine'>
|
|
166
170
|
<Popover.Arrow />
|
|
167
171
|
<Popover.Viewport>
|
|
168
172
|
<Form.Root
|
|
@@ -170,6 +174,8 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
|
|
|
170
174
|
autoFocus
|
|
171
175
|
schema={narrowedSchema}
|
|
172
176
|
values={formValues}
|
|
177
|
+
onValuesChanged={handleValuesChanged}
|
|
178
|
+
projection={model?.projection}
|
|
173
179
|
createInitialValuePath={fieldProjection.field.referencePath}
|
|
174
180
|
createOptionIcon='ph--plus--regular'
|
|
175
181
|
createOptionLabel={createOptionLabel}
|
|
@@ -6,11 +6,12 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
+
import { View } from '@dxos/echo';
|
|
9
10
|
import { faker } from '@dxos/random';
|
|
10
11
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
11
12
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
12
13
|
import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
|
|
13
|
-
import {
|
|
14
|
+
import { ViewModel } from '@dxos/schema';
|
|
14
15
|
import { Task } from '@dxos/types';
|
|
15
16
|
|
|
16
17
|
import { useTestTableModel } from '../../testing';
|
|
@@ -45,14 +46,14 @@ const meta = {
|
|
|
45
46
|
component: DefaultStory,
|
|
46
47
|
render: DefaultStory,
|
|
47
48
|
decorators: [
|
|
48
|
-
withTheme,
|
|
49
|
+
withTheme(),
|
|
49
50
|
withLayout({ layout: 'column' }),
|
|
50
51
|
withClientProvider({
|
|
51
52
|
types: [View.View, Task.Task, Table.Table],
|
|
52
53
|
createIdentity: true,
|
|
53
54
|
createSpace: true,
|
|
54
55
|
onCreateSpace: async ({ space }) => {
|
|
55
|
-
const { view, jsonSchema } = await
|
|
56
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Task.Task.typename });
|
|
56
57
|
const table = Table.make({ view, jsonSchema });
|
|
57
58
|
space.db.add(table);
|
|
58
59
|
Array.from({ length: 10 }).forEach(() => {
|
|
@@ -31,7 +31,7 @@ import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
|
|
|
31
31
|
|
|
32
32
|
const editorSlots = {
|
|
33
33
|
scroll: {
|
|
34
|
-
className: '!
|
|
34
|
+
className: '!py-(--dx-grid-cell-editor-padding-block)',
|
|
35
35
|
},
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -42,7 +42,7 @@ export type QueryResult = Pick<Completion, 'label'> & {
|
|
|
42
42
|
data: any;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export type TableCellEditorProps<T extends Type.
|
|
45
|
+
export type TableCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
|
|
46
46
|
schema?: T;
|
|
47
47
|
model?: TableModel;
|
|
48
48
|
modals?: ModalController;
|
|
@@ -51,7 +51,7 @@ export type TableCellEditorProps<T extends Type.Entity.Any = Type.Entity.Any> =
|
|
|
51
51
|
onSave?: () => void;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
export const TableValueEditor = <T extends Type.
|
|
54
|
+
export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
55
55
|
__gridScope,
|
|
56
56
|
schema,
|
|
57
57
|
model,
|
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
type ActionGraphEdges,
|
|
11
11
|
type ActionGraphNodes,
|
|
12
12
|
type ActionGraphProps,
|
|
13
|
+
Menu,
|
|
13
14
|
type MenuAction,
|
|
14
|
-
MenuProvider,
|
|
15
|
-
ToolbarMenu,
|
|
16
15
|
createGapSeparator,
|
|
17
16
|
createMenuAction,
|
|
18
17
|
useMenuActions,
|
|
@@ -75,7 +74,7 @@ const createTableToolbarActions = ({
|
|
|
75
74
|
}
|
|
76
75
|
const gap = createGapSeparator();
|
|
77
76
|
nodes.push(...gap.nodes);
|
|
78
|
-
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
|
|
77
|
+
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target, relation: 'child' }));
|
|
79
78
|
if (customActions) {
|
|
80
79
|
const custom = get(customActions);
|
|
81
80
|
nodes.push(...custom.nodes);
|
|
@@ -95,6 +94,7 @@ export const TableToolbar = ({
|
|
|
95
94
|
onAdd,
|
|
96
95
|
onSave,
|
|
97
96
|
customActions,
|
|
97
|
+
...props
|
|
98
98
|
}: TableToolbarProps) => {
|
|
99
99
|
const registry = useContext(RegistryContext);
|
|
100
100
|
const stateAtom = useMemo(() => Atom.make<TableToolbarState>({ viewDirty }), []);
|
|
@@ -111,8 +111,8 @@ export const TableToolbar = ({
|
|
|
111
111
|
const menu = useMenuActions(actionsCreator);
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
</
|
|
114
|
+
<Menu.Root {...menu} attendableId={attendableId}>
|
|
115
|
+
<Menu.Toolbar {...props} classNames={classNames} />
|
|
116
|
+
</Menu.Root>
|
|
117
117
|
);
|
|
118
118
|
};
|
package/src/hooks/useAddRow.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
import { type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import { useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { Type } from '@dxos/echo';
|
|
8
|
+
import { JsonSchema, Type } from '@dxos/echo';
|
|
9
9
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
10
10
|
import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import { type Table } from '../types';
|
|
13
13
|
|
|
14
|
-
export const useProjectionModel = <S extends Type.
|
|
14
|
+
export const useProjectionModel = <S extends Type.AnyEntity>(
|
|
15
15
|
schema: S | undefined,
|
|
16
16
|
table: Table.Table | undefined,
|
|
17
17
|
registry: Registry.Registry,
|
|
@@ -23,7 +23,7 @@ export const useProjectionModel = <S extends Type.Entity.Any>(
|
|
|
23
23
|
const view = await table.view.load();
|
|
24
24
|
// For mutable schemas (EchoSchema), use the live jsonSchema reference for reactivity.
|
|
25
25
|
// For immutable schemas, create a snapshot.
|
|
26
|
-
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema :
|
|
26
|
+
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : JsonSchema.toJsonSchema(schema);
|
|
27
27
|
|
|
28
28
|
// Always use createEchoChangeCallback since the view is ECHO-backed.
|
|
29
29
|
// Pass schema only when mutable to allow schema mutations.
|
|
@@ -29,7 +29,7 @@ export type UseTableModelProps<T extends TableRow = TableRow> = {
|
|
|
29
29
|
onRowAction?: (actionId: string, data: T) => void;
|
|
30
30
|
} & Pick<
|
|
31
31
|
TableModelProps<T>,
|
|
32
|
-
'features' | 'onInsertRow' | 'onDeleteRows' | '
|
|
32
|
+
'features' | 'onInsertRow' | 'onDeleteRows' | 'onColumnDelete' | 'onCellUpdate' | 'onRowOrderChange'
|
|
33
33
|
>;
|
|
34
34
|
|
|
35
35
|
export const useTableModel = <T extends TableRow = TableRow>({
|
|
@@ -6,9 +6,9 @@ import { Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Filter, Query, Type } from '@dxos/echo';
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { ProjectionModel,
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { Table } from '../types';
|
|
14
14
|
|
|
@@ -121,14 +121,14 @@ const Test = Schema.Struct({
|
|
|
121
121
|
completed: Schema.Boolean,
|
|
122
122
|
}).pipe(
|
|
123
123
|
Type.object({
|
|
124
|
-
typename: 'example.
|
|
124
|
+
typename: 'com.example.type.test',
|
|
125
125
|
version: '0.1.0',
|
|
126
126
|
}),
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
130
130
|
const schema = createEchoSchema(Test);
|
|
131
|
-
const view =
|
|
131
|
+
const view = ViewModel.make({
|
|
132
132
|
query: Query.select(Filter.type(schema)),
|
|
133
133
|
jsonSchema: schema.jsonSchema,
|
|
134
134
|
});
|
|
@@ -137,7 +137,7 @@ const createTableModel = (registry: Registry.Registry, props: Partial<TableModel
|
|
|
137
137
|
registry,
|
|
138
138
|
view,
|
|
139
139
|
baseSchema: schema.jsonSchema,
|
|
140
|
-
change: createDirectChangeCallback(view.projection, schema
|
|
140
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
141
141
|
});
|
|
142
142
|
projection.normalizeView();
|
|
143
143
|
return new TableModel({
|
package/src/model/table-model.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
import { type Database, Format, Obj, Order, Query, type QueryAST, Ref } from '@dxos/echo';
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
9
10
|
import { type JsonProp, type JsonSchemaType, type Mutable, toEffectSchema } from '@dxos/echo/internal';
|
|
10
11
|
import { getSnapshot } from '@dxos/echo/internal';
|
|
11
12
|
import { getValue, setValue } from '@dxos/effect';
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
type DxGridPlaneRange,
|
|
20
21
|
type DxGridPosition,
|
|
21
22
|
} from '@dxos/react-ui-grid';
|
|
22
|
-
import { type ProjectionModel, type PropertyType, type ValidationError,
|
|
23
|
+
import { type ProjectionModel, type PropertyType, type ValidationError, validateSchema } from '@dxos/schema';
|
|
23
24
|
|
|
24
25
|
import { type Table } from '../types';
|
|
25
26
|
import { extractOrder } from '../util';
|
|
@@ -122,7 +123,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
|
|
|
122
123
|
onResolveSchema?: (typename: string) => Promise<JsonSchemaType>;
|
|
123
124
|
onInsertRow?: (data?: any) => InsertRowResult;
|
|
124
125
|
onDeleteRows?: (index: number, obj: T[]) => void;
|
|
125
|
-
|
|
126
|
+
onColumnDelete?: (fieldId: string) => void;
|
|
126
127
|
onCellUpdate?: (cell: DxGridPosition) => void;
|
|
127
128
|
onRowOrderChange?: () => void;
|
|
128
129
|
onRowAction?: (actionId: string, data: T) => void;
|
|
@@ -139,7 +140,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
139
140
|
|
|
140
141
|
private readonly _onInsertRow?: (data?: any) => InsertRowResult;
|
|
141
142
|
private readonly _onDeleteRows?: TableModelProps<T>['onDeleteRows'];
|
|
142
|
-
private readonly
|
|
143
|
+
private readonly _onColumnDelete?: TableModelProps<T>['onColumnDelete'];
|
|
143
144
|
private readonly _onCellUpdate?: TableModelProps<T>['onCellUpdate'];
|
|
144
145
|
private readonly _onRowOrderChange?: TableModelProps<T>['onRowOrderChange'];
|
|
145
146
|
private readonly _onRowAction?: TableModelProps<T>['onRowAction'];
|
|
@@ -176,7 +177,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
176
177
|
pinnedRows = { top: [], bottom: [] },
|
|
177
178
|
rowActions = [],
|
|
178
179
|
onCellUpdate,
|
|
179
|
-
|
|
180
|
+
onColumnDelete,
|
|
180
181
|
onDeleteRows,
|
|
181
182
|
onInsertRow,
|
|
182
183
|
onRowOrderChange,
|
|
@@ -292,7 +293,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
292
293
|
this._rowActions = rowActions;
|
|
293
294
|
this._onInsertRow = onInsertRow;
|
|
294
295
|
this._onDeleteRows = onDeleteRows;
|
|
295
|
-
this.
|
|
296
|
+
this._onColumnDelete = onColumnDelete;
|
|
296
297
|
this._onCellUpdate = onCellUpdate;
|
|
297
298
|
this._onRowOrderChange = onRowOrderChange;
|
|
298
299
|
this._onRowAction = onRowAction;
|
|
@@ -691,7 +692,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
691
692
|
const currentRow = this._registry.get(this._rows)[row];
|
|
692
693
|
invariant(currentRow, 'Invalid row index');
|
|
693
694
|
|
|
694
|
-
const snapshot = getSnapshot(currentRow);
|
|
695
|
+
const snapshot = { ...getSnapshot(currentRow) };
|
|
695
696
|
setValue(snapshot, field.path, transformedValue);
|
|
696
697
|
|
|
697
698
|
const schema = Obj.getSchema(currentRow);
|
|
@@ -796,8 +797,8 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
796
797
|
}
|
|
797
798
|
|
|
798
799
|
const field = this._projection?.getFields().find((field) => field.id === fieldId);
|
|
799
|
-
if (field && this.
|
|
800
|
-
this.
|
|
800
|
+
if (field && this._onColumnDelete) {
|
|
801
|
+
this._onColumnDelete(field.id);
|
|
801
802
|
}
|
|
802
803
|
}
|
|
803
804
|
|
|
@@ -6,9 +6,9 @@ import { Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Filter, Query, Type } from '@dxos/echo';
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { ProjectionModel,
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { Table } from '../types';
|
|
14
14
|
|
|
@@ -92,14 +92,14 @@ const Test = Schema.Struct({
|
|
|
92
92
|
count: Schema.Number,
|
|
93
93
|
}).pipe(
|
|
94
94
|
Type.object({
|
|
95
|
-
typename: 'example.
|
|
95
|
+
typename: 'com.example.type.test',
|
|
96
96
|
version: '0.1.0',
|
|
97
97
|
}),
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
101
101
|
const schema = createEchoSchema(Test);
|
|
102
|
-
const view =
|
|
102
|
+
const view = ViewModel.make({
|
|
103
103
|
query: Query.select(Filter.type(schema)),
|
|
104
104
|
jsonSchema: schema.jsonSchema,
|
|
105
105
|
});
|
|
@@ -108,7 +108,7 @@ const createTableModel = (registry: Registry.Registry, props: Partial<TableModel
|
|
|
108
108
|
registry,
|
|
109
109
|
view,
|
|
110
110
|
baseSchema: schema.jsonSchema,
|
|
111
|
-
change: createDirectChangeCallback(view.projection, schema
|
|
111
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
112
112
|
});
|
|
113
113
|
projection.normalizeView();
|
|
114
114
|
return new TableModel({ registry, object, projection, ...props });
|
|
@@ -6,6 +6,7 @@ import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
9
10
|
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
11
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
12
|
import {
|
|
@@ -15,7 +16,7 @@ import {
|
|
|
15
16
|
type DxGridPlaneRange,
|
|
16
17
|
toPlaneCellIndex,
|
|
17
18
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import {
|
|
19
|
+
import { VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
19
20
|
import { mx } from '@dxos/ui-theme';
|
|
20
21
|
|
|
21
22
|
import { tableButtons, tableControls } from '../util';
|
|
@@ -107,7 +108,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
107
108
|
private createDataCell(
|
|
108
109
|
cells: DxGridPlaneCells,
|
|
109
110
|
obj: T,
|
|
110
|
-
field: FieldType,
|
|
111
|
+
field: View.FieldType,
|
|
111
112
|
colIndex: number,
|
|
112
113
|
displayIndex: number,
|
|
113
114
|
): void {
|
|
@@ -210,7 +211,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
210
211
|
const targetObj = getValue(obj, field.path)?.target;
|
|
211
212
|
if (targetObj) {
|
|
212
213
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
213
|
-
cell.accessoryHtml = `<div role="none" class="absolute
|
|
214
|
+
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor dxn=${dxn} class="dx-button w-6 aspect-square min-h-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
|
|
@@ -335,11 +336,11 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
335
336
|
value: '',
|
|
336
337
|
resizeHandle: 'col',
|
|
337
338
|
accessoryHtml: `
|
|
338
|
-
<span class="grow min-
|
|
339
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
339
340
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
340
341
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
341
342
|
`,
|
|
342
|
-
className: '!bg-
|
|
343
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
343
344
|
};
|
|
344
345
|
}
|
|
345
346
|
|
|
@@ -382,7 +383,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
382
383
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
383
384
|
return {
|
|
384
385
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
385
|
-
className: '!bg-
|
|
386
|
+
className: '!bg-toolbar-surface',
|
|
386
387
|
readonly: true,
|
|
387
388
|
value: '',
|
|
388
389
|
},
|
|
@@ -396,7 +397,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
396
397
|
header: true,
|
|
397
398
|
checked: this.model.selection.allRowsSelected,
|
|
398
399
|
}),
|
|
399
|
-
className: '!bg-
|
|
400
|
+
className: '!bg-toolbar-surface',
|
|
400
401
|
readonly: true,
|
|
401
402
|
value: '',
|
|
402
403
|
},
|
|
@@ -411,7 +412,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
411
412
|
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
412
413
|
})
|
|
413
414
|
: undefined,
|
|
414
|
-
className: '!bg-
|
|
415
|
+
className: '!bg-toolbar-surface',
|
|
415
416
|
readonly: true,
|
|
416
417
|
value: '',
|
|
417
418
|
},
|
|
@@ -465,7 +466,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
465
466
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
466
467
|
if (!selected) {
|
|
467
468
|
if (selectionMode === 'single') {
|
|
468
|
-
return ['
|
|
469
|
+
return ['cursor-pointer!'];
|
|
469
470
|
} else {
|
|
470
471
|
return undefined;
|
|
471
472
|
}
|
|
@@ -473,8 +474,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
473
474
|
|
|
474
475
|
switch (selectionMode) {
|
|
475
476
|
case 'single':
|
|
476
|
-
return ['
|
|
477
|
+
return ['dx-grid__cell--no-focus-unfurl !bg-current-surface hover:bg-hover-surface cursor-pointer!'];
|
|
477
478
|
case 'multiple':
|
|
478
|
-
return ['!bg-
|
|
479
|
+
return ['!bg-grid-cell-selected'];
|
|
479
480
|
}
|
|
480
481
|
};
|