@dxos/react-ui-table 0.6.13-main.ed424a1 → 0.6.13
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/index.mjs +127 -43
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts +3 -6
- package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts +3 -3
- package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts +12 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +1 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts +63 -0
- package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts.map +1 -0
- package/dist/types/src/components/ColumnMenu/index.d.ts +1 -1
- package/dist/types/src/components/ColumnMenu/index.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +9 -21
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContext.d.ts +4 -2
- package/dist/types/src/components/Table/TableContext.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableHead.d.ts.map +1 -1
- package/dist/types/src/components/Table/index.d.ts +1 -2
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- package/dist/types/src/helpers.d.ts +1 -1
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -1
- package/dist/types/src/schema/{Schema.stories.d.ts → SchemaToTable.stories.d.ts} +3 -2
- package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -0
- package/dist/types/src/schema/index.d.ts +1 -1
- package/dist/types/src/schema/index.d.ts.map +1 -1
- package/dist/types/src/schema/schema.d.ts +5 -0
- package/dist/types/src/schema/schema.d.ts.map +1 -0
- package/dist/types/src/schema/schema.test.d.ts +2 -0
- package/dist/types/src/schema/schema.test.d.ts.map +1 -0
- package/dist/types/src/schema/{column-utils.d.ts → schemaToColumns.d.ts} +2 -2
- package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -0
- package/dist/types/src/schema/types.d.ts +4 -10
- package/dist/types/src/schema/types.d.ts.map +1 -1
- package/package.json +20 -21
- package/src/components/ColumnMenu/ColumnMenu.stories.tsx +12 -13
- package/src/components/ColumnMenu/ColumnMenu.tsx +5 -8
- package/src/components/ColumnMenu/{ColumnSettings.stories.tsx → ColumnSettingsForm.stories.tsx} +23 -27
- package/src/components/ColumnMenu/{ColumnSettings.tsx → ColumnSettingsForm.tsx} +43 -44
- package/src/components/ColumnMenu/index.ts +1 -1
- package/src/components/Table/Table.stories.tsx +85 -97
- package/src/components/Table/Table.tsx +9 -8
- package/src/components/Table/TableContext.tsx +7 -13
- package/src/components/Table/TableHead.tsx +1 -0
- package/src/components/Table/index.ts +1 -2
- package/src/components/Table/props.ts +4 -4
- package/src/helpers.tsx +6 -4
- package/src/hooks/useRowSelection.ts +0 -1
- package/src/schema/{Schema.stories.tsx → SchemaToTable.stories.tsx} +23 -11
- package/src/schema/index.ts +1 -1
- package/src/schema/schema.test.ts +148 -0
- package/src/schema/schema.ts +93 -0
- package/src/schema/{column-utils.ts → schemaToColumns.ts} +9 -9
- package/src/schema/types.ts +6 -14
- package/src/translations.ts +1 -1
- package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts +0 -15
- package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts.map +0 -1
- package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts +0 -84
- package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts.map +0 -1
- package/dist/types/src/schema/Schema.stories.d.ts.map +0 -1
- package/dist/types/src/schema/column-utils.d.ts.map +0 -1
|
@@ -16,13 +16,13 @@ import {
|
|
|
16
16
|
} from '@tanstack/react-table';
|
|
17
17
|
import React, {
|
|
18
18
|
type ComponentPropsWithoutRef,
|
|
19
|
-
type ReactNode,
|
|
20
19
|
Fragment,
|
|
21
|
-
memo,
|
|
22
20
|
useCallback,
|
|
23
21
|
useEffect,
|
|
24
22
|
useState,
|
|
25
23
|
useContext,
|
|
24
|
+
type ReactNode,
|
|
25
|
+
memo,
|
|
26
26
|
} from 'react';
|
|
27
27
|
|
|
28
28
|
import { DensityProvider, type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
|
|
@@ -55,6 +55,7 @@ const TableViewport = ({ children, classNames, asChild, ...props }: TableViewpor
|
|
|
55
55
|
const { scrollContextRef } = useContext(TableRootContext);
|
|
56
56
|
|
|
57
57
|
const classes = mx(classNames, 'overflow-auto');
|
|
58
|
+
|
|
58
59
|
return asChild ? (
|
|
59
60
|
<Slot ref={scrollContextRef} className={classes} {...props}>
|
|
60
61
|
{children}
|
|
@@ -69,17 +70,17 @@ const TableViewport = ({ children, classNames, asChild, ...props }: TableViewpor
|
|
|
69
70
|
export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>) => {
|
|
70
71
|
const {
|
|
71
72
|
role,
|
|
73
|
+
onColumnResize,
|
|
74
|
+
columnVisibility,
|
|
72
75
|
header = true,
|
|
73
76
|
rowsSelectable,
|
|
74
77
|
debug,
|
|
75
|
-
pinLastRow,
|
|
76
|
-
columnVisibility,
|
|
77
|
-
onColumnResize,
|
|
78
78
|
onDataSelectionChange,
|
|
79
|
+
pinLastRow,
|
|
79
80
|
} = props;
|
|
80
81
|
|
|
81
|
-
const columns = useDefaultValue(props.columns,
|
|
82
|
-
const incomingData = useDefaultValue(props.data,
|
|
82
|
+
const columns = useDefaultValue(props.columns, []);
|
|
83
|
+
const incomingData = useDefaultValue(props.data, []);
|
|
83
84
|
|
|
84
85
|
const [data, setData] = useState([...incomingData]);
|
|
85
86
|
|
|
@@ -109,7 +110,7 @@ export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>)
|
|
|
109
110
|
// Columns
|
|
110
111
|
columns,
|
|
111
112
|
defaultColumn: {
|
|
112
|
-
size:
|
|
113
|
+
size: 256, // Required in order remove default width.
|
|
113
114
|
maxSize: 1024,
|
|
114
115
|
},
|
|
115
116
|
|
|
@@ -3,32 +3,26 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type RowData } from '@tanstack/react-table';
|
|
6
|
-
import React, {
|
|
7
|
-
useContext,
|
|
8
|
-
createContext,
|
|
9
|
-
type Context,
|
|
10
|
-
type JSX,
|
|
11
|
-
type PropsWithChildren,
|
|
12
|
-
type ReactNode,
|
|
13
|
-
} from 'react';
|
|
14
|
-
|
|
15
|
-
import { raise } from '@dxos/debug';
|
|
6
|
+
import React, { useContext, createContext, type JSX, type ReactNode, type FC, type Context } from 'react';
|
|
16
7
|
|
|
17
8
|
import { type TableContextValue } from './props';
|
|
18
9
|
|
|
19
10
|
export const TABLE_NAME = 'Table';
|
|
20
11
|
|
|
21
|
-
|
|
12
|
+
// Create the context without a default value.
|
|
13
|
+
// The expectation is that the provider will always supply a value, so no need to define a default here.
|
|
14
|
+
const TableContext = createContext<TableContextValue<RowData>>({} as TableContextValue<RowData>);
|
|
22
15
|
|
|
23
16
|
export type TypedTableProvider<TData extends RowData> = {
|
|
24
17
|
(props: TableContextValue<TData> & { children: ReactNode }): JSX.Element;
|
|
25
18
|
displayName: string;
|
|
26
19
|
};
|
|
27
20
|
|
|
28
|
-
export const TableProvider = ({ children, ...value }
|
|
21
|
+
export const TableProvider: FC<TableContextValue<RowData> & { children: ReactNode }> = ({ children, ...value }) => {
|
|
29
22
|
return <TableContext.Provider value={value}>{children}</TableContext.Provider>;
|
|
30
23
|
};
|
|
31
24
|
|
|
25
|
+
// Create a custom hook to consume the context.
|
|
32
26
|
export const useTableContext = <TData extends RowData>(): TableContextValue<TData> => {
|
|
33
|
-
return useContext(TableContext as Context<TableContextValue<TData>>)
|
|
27
|
+
return useContext(TableContext as Context<TableContextValue<TData>>);
|
|
34
28
|
};
|
|
@@ -25,7 +25,7 @@ export type TableFlags = Partial<{
|
|
|
25
25
|
* This is ‘current’ in the `aria-current` sense.
|
|
26
26
|
*/
|
|
27
27
|
export type TableCurrent<TData extends RowData> = Partial<{
|
|
28
|
-
// App state
|
|
28
|
+
// App state
|
|
29
29
|
currentDatum: TData;
|
|
30
30
|
onDatumClick: (datum: TData) => void;
|
|
31
31
|
}>;
|
|
@@ -39,13 +39,13 @@ export type TableProps<TData extends RowData> = TableFlags &
|
|
|
39
39
|
onColumnResize: (state: Record<string, number>) => void;
|
|
40
40
|
onColumnReorder: (columnId: string, direction: 'left' | 'right') => void;
|
|
41
41
|
columnVisibility: VisibilityState;
|
|
42
|
-
// Controllable row selection
|
|
42
|
+
// Controllable row selection
|
|
43
43
|
rowSelection: RowSelectionState;
|
|
44
44
|
defaultRowSelection: RowSelectionState;
|
|
45
45
|
onRowSelectionChange: (rowSelection: RowSelectionState) => void;
|
|
46
|
-
// Derived from row selection
|
|
46
|
+
// Derived from row selection
|
|
47
47
|
onDataSelectionChange: (dataSelection: TData[]) => void;
|
|
48
|
-
// `table` element props
|
|
48
|
+
// `table` element props
|
|
49
49
|
classNames: ClassNameValue;
|
|
50
50
|
pinLastRow: boolean;
|
|
51
51
|
}>;
|
package/src/helpers.tsx
CHANGED
|
@@ -21,7 +21,8 @@ import { log } from '@dxos/log';
|
|
|
21
21
|
import { type ClassNameValue, Input, Tooltip } from '@dxos/react-ui';
|
|
22
22
|
import { getSize, mx } from '@dxos/react-ui-theme';
|
|
23
23
|
|
|
24
|
-
import { CellCombobox
|
|
24
|
+
import { CellCombobox } from './components';
|
|
25
|
+
import { useTableContext } from './components/Table/TableContext';
|
|
25
26
|
import { textPadding } from './theme';
|
|
26
27
|
|
|
27
28
|
export type ValueUpdater<TData extends RowData, TValue> = (row: TData, id: string, value: TValue) => void;
|
|
@@ -469,7 +470,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
469
470
|
size: 50,
|
|
470
471
|
minSize: 50,
|
|
471
472
|
header: (column) => label ?? column.header.id,
|
|
472
|
-
// TODO(zan): Implement sort algorithm
|
|
473
|
+
// TODO(zan): Implement sort algorithm
|
|
473
474
|
enableSorting: true,
|
|
474
475
|
cell: SwitchBuilderCell,
|
|
475
476
|
...props,
|
|
@@ -485,7 +486,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
485
486
|
const IconOff = off?.Icon ?? X;
|
|
486
487
|
return {
|
|
487
488
|
size: size ?? 32,
|
|
488
|
-
// TODO(zan): Implement sort algorithm
|
|
489
|
+
// TODO(zan): Implement sort algorithm
|
|
489
490
|
enableSorting: false,
|
|
490
491
|
header: (column) => <div className={'justify-center'}>{label ?? column.header.id}</div>,
|
|
491
492
|
cell: (cell) => {
|
|
@@ -505,13 +506,14 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
505
506
|
/**
|
|
506
507
|
* This display column type is for nested/unhandled data to appear as JSON.
|
|
507
508
|
*/
|
|
508
|
-
|
|
509
|
+
display({ label, classNames, ...props }: KeyColumnOptions<TData> = {}): Partial<ColumnDef<TData, PublicKey>> {
|
|
509
510
|
return {
|
|
510
511
|
size: 120,
|
|
511
512
|
minSize: 120,
|
|
512
513
|
header: (cell) => label ?? cell.header.id,
|
|
513
514
|
cell: (cell) => {
|
|
514
515
|
const value = cell.getValue();
|
|
516
|
+
|
|
515
517
|
return (
|
|
516
518
|
<div className='overflow-scroll'>
|
|
517
519
|
<code className='text-xs'>{JSON.stringify(value)}</code>
|
|
@@ -4,22 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as Arbitrary from '@effect/schema/Arbitrary';
|
|
8
8
|
import * as fc from 'fast-check';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
|
|
11
11
|
import { S } from '@dxos/echo-schema';
|
|
12
|
-
import {
|
|
12
|
+
import { DensityProvider } from '@dxos/react-ui';
|
|
13
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
13
14
|
|
|
14
|
-
import { schemaToColumnDefs } from './
|
|
15
|
+
import { schemaToColumnDefs } from './schemaToColumns';
|
|
15
16
|
import { Table } from '../components';
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
|
-
title: 'react-ui-table/
|
|
19
|
-
|
|
19
|
+
title: 'react-ui-table/SchemaTable',
|
|
20
|
+
args: {},
|
|
21
|
+
decorators: [
|
|
22
|
+
withTheme,
|
|
23
|
+
(Story: any) => (
|
|
24
|
+
<DensityProvider density='fine'>
|
|
25
|
+
<Story />
|
|
26
|
+
</DensityProvider>
|
|
27
|
+
),
|
|
28
|
+
],
|
|
20
29
|
};
|
|
21
30
|
|
|
22
|
-
const
|
|
31
|
+
const exampleSchema = S.Struct({
|
|
23
32
|
field1: S.String,
|
|
24
33
|
field2: S.Number,
|
|
25
34
|
field3: S.Date,
|
|
@@ -32,17 +41,20 @@ const TestSchema = S.Struct({
|
|
|
32
41
|
),
|
|
33
42
|
});
|
|
34
43
|
|
|
35
|
-
type
|
|
44
|
+
type ExampleSchema = S.Schema.Type<typeof exampleSchema>;
|
|
36
45
|
|
|
37
|
-
const columns = schemaToColumnDefs(
|
|
38
|
-
const items = fc.sample(Arbitrary.make(TestSchema), 10);
|
|
46
|
+
const columns = schemaToColumnDefs(exampleSchema);
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
const exampleSchemaArbitrary = Arbitrary.make(exampleSchema);
|
|
49
|
+
|
|
50
|
+
const items = fc.sample(exampleSchemaArbitrary, 10);
|
|
51
|
+
|
|
52
|
+
export const SchemaTable = {
|
|
41
53
|
render: () => {
|
|
42
54
|
return (
|
|
43
55
|
<Table.Root>
|
|
44
56
|
<Table.Viewport classNames='fixed inset-0'>
|
|
45
|
-
<Table.Main<
|
|
57
|
+
<Table.Main<ExampleSchema>
|
|
46
58
|
role='grid'
|
|
47
59
|
rowsSelectable='multi'
|
|
48
60
|
keyAccessor={(row) => JSON.stringify(row)}
|
package/src/schema/index.ts
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { S } from '@dxos/echo-schema';
|
|
8
|
+
import { describe, test } from '@dxos/test';
|
|
9
|
+
|
|
10
|
+
import { classifySchemaProperties } from './schema';
|
|
11
|
+
|
|
12
|
+
describe('schema->column-type', () => {
|
|
13
|
+
test('basic', () => {
|
|
14
|
+
const simpleSchema = S.Struct({
|
|
15
|
+
field1: S.String,
|
|
16
|
+
field2: S.Number,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
20
|
+
|
|
21
|
+
expect(columns).to.deep.equal([
|
|
22
|
+
['field1', 'string'],
|
|
23
|
+
['field2', 'number'],
|
|
24
|
+
]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('optional properties', () => {
|
|
28
|
+
const simpleSchema = S.Struct({
|
|
29
|
+
field1: S.optional(S.String),
|
|
30
|
+
field2: S.optional(S.Number),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
34
|
+
|
|
35
|
+
expect(columns).to.deep.equal([
|
|
36
|
+
['field1', 'string'],
|
|
37
|
+
['field2', 'number'],
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('dates', () => {
|
|
42
|
+
const simpleSchema = S.Struct({
|
|
43
|
+
field1: S.Date,
|
|
44
|
+
field2: S.optional(S.Date),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
48
|
+
|
|
49
|
+
expect(columns).to.deep.equal([
|
|
50
|
+
['field1', 'date'],
|
|
51
|
+
['field2', 'date'],
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Piped validators', () => {
|
|
56
|
+
const simpleSchema = S.Struct({
|
|
57
|
+
name: S.optional(S.String.pipe(S.nonEmpty(), S.maxLength(10))),
|
|
58
|
+
age: S.Number.pipe(S.negative()),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
62
|
+
|
|
63
|
+
expect(columns).to.deep.equal([
|
|
64
|
+
['name', 'string'],
|
|
65
|
+
['age', 'number'],
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('nested schema', () => {
|
|
70
|
+
const simpleSchema = S.Struct({
|
|
71
|
+
name: S.String,
|
|
72
|
+
nested: S.Struct({
|
|
73
|
+
age: S.Number,
|
|
74
|
+
}),
|
|
75
|
+
someUnion: S.Union(S.String, S.Number),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
79
|
+
|
|
80
|
+
expect(columns).to.deep.equal([
|
|
81
|
+
['name', 'string'],
|
|
82
|
+
['nested.age', 'number'],
|
|
83
|
+
['someUnion', 'display'],
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('deeply nested schema', () => {
|
|
88
|
+
const simpleSchema = S.Struct({
|
|
89
|
+
name: S.String,
|
|
90
|
+
nested: S.Struct({
|
|
91
|
+
age: S.Number,
|
|
92
|
+
deeplyNested: S.Struct({
|
|
93
|
+
height: S.Number,
|
|
94
|
+
}),
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
99
|
+
|
|
100
|
+
expect(columns).to.deep.equal([
|
|
101
|
+
['name', 'string'],
|
|
102
|
+
['nested.age', 'number'],
|
|
103
|
+
['nested.deeplyNested.height', 'number'],
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('even more deeply nested schema with optionals', () => {
|
|
108
|
+
const simpleSchema = S.Struct({
|
|
109
|
+
name: S.String,
|
|
110
|
+
nested: S.Struct({
|
|
111
|
+
age: S.Number,
|
|
112
|
+
deeplyNested: S.optional(
|
|
113
|
+
S.Struct({
|
|
114
|
+
height: S.optional(S.Number),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
121
|
+
|
|
122
|
+
expect(columns).to.deep.equal([
|
|
123
|
+
['name', 'string'],
|
|
124
|
+
['nested.age', 'number'],
|
|
125
|
+
['nested.deeplyNested.height', 'number'],
|
|
126
|
+
]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('multiple-nested schemata as sibblings', () => {
|
|
130
|
+
const simpleSchema = S.Struct({
|
|
131
|
+
name: S.String,
|
|
132
|
+
nested: S.Struct({
|
|
133
|
+
age: S.Number,
|
|
134
|
+
}),
|
|
135
|
+
nested2: S.Struct({
|
|
136
|
+
height: S.Number,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const columns = classifySchemaProperties(simpleSchema);
|
|
141
|
+
|
|
142
|
+
expect(columns).to.deep.equal([
|
|
143
|
+
['name', 'string'],
|
|
144
|
+
['nested.age', 'number'],
|
|
145
|
+
['nested2.height', 'number'],
|
|
146
|
+
]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as AST from '@effect/schema/AST';
|
|
6
|
+
import type * as S from '@effect/schema/Schema';
|
|
7
|
+
|
|
8
|
+
import { type ColumnType } from './types';
|
|
9
|
+
|
|
10
|
+
// TODO(burdon): Reconcile with react-ui-table.
|
|
11
|
+
export type ClassifiedColumnType = ColumnType | 'display';
|
|
12
|
+
|
|
13
|
+
// TODO(burdon): Rename getX? and return typed array?
|
|
14
|
+
export const classifySchemaProperties = (schema: S.Schema<any, any>): [string, ClassifiedColumnType][] => {
|
|
15
|
+
const recurse = (node: AST.AST, path: string[], acc: [string, ClassifiedColumnType][]) => {
|
|
16
|
+
const properties = AST.getPropertySignatures(node);
|
|
17
|
+
properties.forEach((prop) => {
|
|
18
|
+
const propName = prop.name.toString();
|
|
19
|
+
|
|
20
|
+
if (prop.isOptional) {
|
|
21
|
+
const unwrappedAst = unwrapOptionProperty(prop);
|
|
22
|
+
if (isStruct(unwrappedAst)) {
|
|
23
|
+
return recurse(unwrappedAst, [...path, propName], acc);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (isStruct(prop.type)) {
|
|
28
|
+
recurse(prop.type, [...path, propName], acc);
|
|
29
|
+
} else {
|
|
30
|
+
acc.push([path.concat(propName).join('.'), propertyToColumn(prop)]);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return acc;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return recurse(schema.ast, [], []);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const isStruct = (node: AST.AST) => AST.isTypeLiteral(node);
|
|
41
|
+
|
|
42
|
+
const isOptionalUnion = (prop: AST.PropertySignature) => AST.isUnion(prop.type) && prop.isOptional;
|
|
43
|
+
|
|
44
|
+
const unwrapOptionProperty = (prop: AST.PropertySignature) => {
|
|
45
|
+
if (!isOptionalUnion(prop)) {
|
|
46
|
+
throw new Error(`Not an optional property: ${String(prop.name)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!AST.isUnion(prop.type)) {
|
|
50
|
+
throw new Error(`Not a union type: ${String(prop.name)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [type, _undefinedCase] = prop.type.types;
|
|
54
|
+
return type;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const typeToColumn = (type: AST.AST): ClassifiedColumnType => {
|
|
58
|
+
if (AST.isStringKeyword(type)) {
|
|
59
|
+
return 'string';
|
|
60
|
+
} else if (AST.isNumberKeyword(type)) {
|
|
61
|
+
return 'number';
|
|
62
|
+
} else if (AST.isBooleanKeyword(type)) {
|
|
63
|
+
return 'boolean';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (AST.isRefinement(type)) {
|
|
67
|
+
return typeToColumn(type.from);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// TODO(zan): How should we be thinking about transformations?
|
|
71
|
+
// - Which of these are we storing in the database?
|
|
72
|
+
// - For types that aren't the 'DateFromString' transformation, should we be using the 'from' or 'to' type?
|
|
73
|
+
|
|
74
|
+
if (AST.isTransformation(type)) {
|
|
75
|
+
const identifier = AST.getIdentifierAnnotation(type);
|
|
76
|
+
if (identifier._tag === 'Some') {
|
|
77
|
+
if (identifier.value === 'DateFromString') {
|
|
78
|
+
return 'date';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return 'display';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const propertyToColumn = (prop: AST.PropertySignature): ClassifiedColumnType => {
|
|
87
|
+
let type = prop.type;
|
|
88
|
+
if (prop.isOptional) {
|
|
89
|
+
type = unwrapOptionProperty(prop);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return typeToColumn(type);
|
|
93
|
+
};
|
|
@@ -4,38 +4,38 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ColumnDef } from '@tanstack/react-table';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { FieldValueType, mapSchemaToFields } from '@dxos/schema';
|
|
7
|
+
import type { S } from '@dxos/echo-schema';
|
|
9
8
|
|
|
9
|
+
import { classifySchemaProperties } from './schema';
|
|
10
10
|
import { createColumnBuilder } from '../helpers';
|
|
11
11
|
|
|
12
12
|
export const schemaToColumnDefs = <T>(schema: S.Schema<T, any>): ColumnDef<T, any>[] => {
|
|
13
13
|
const { helper, builder } = createColumnBuilder<T>();
|
|
14
14
|
|
|
15
|
-
const classified =
|
|
15
|
+
const classified = classifySchemaProperties(schema);
|
|
16
16
|
return classified.map(([name, type]) => {
|
|
17
17
|
const propertyKey = name.toString();
|
|
18
18
|
|
|
19
19
|
let column: Partial<ColumnDef<any, any>> | undefined;
|
|
20
20
|
switch (type) {
|
|
21
|
-
case
|
|
21
|
+
case 'string': {
|
|
22
22
|
column = builder.string({ label: propertyKey });
|
|
23
23
|
break;
|
|
24
24
|
}
|
|
25
|
-
case
|
|
25
|
+
case 'number': {
|
|
26
26
|
column = builder.number({ label: propertyKey });
|
|
27
27
|
break;
|
|
28
28
|
}
|
|
29
|
-
case
|
|
29
|
+
case 'boolean': {
|
|
30
30
|
column = builder.switch({ label: propertyKey });
|
|
31
31
|
break;
|
|
32
32
|
}
|
|
33
|
-
case
|
|
33
|
+
case 'date': {
|
|
34
34
|
column = builder.date({ label: propertyKey });
|
|
35
35
|
break;
|
|
36
36
|
}
|
|
37
|
-
case
|
|
38
|
-
column = builder.
|
|
37
|
+
case 'display': {
|
|
38
|
+
column = builder.display({ label: propertyKey, id: propertyKey });
|
|
39
39
|
break;
|
|
40
40
|
}
|
|
41
41
|
default: {
|
package/src/schema/types.ts
CHANGED
|
@@ -2,26 +2,18 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type FieldValueType } from '@dxos/schema';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @deprecated
|
|
9
|
-
*/
|
|
10
|
-
// TODO(burdon): Reconcile with @dxos/schema/View.
|
|
11
5
|
export type TableDef = {
|
|
12
6
|
id: string;
|
|
13
|
-
name?: string;
|
|
14
|
-
columns:
|
|
7
|
+
name?: string; // TODO(burdon): Required?
|
|
8
|
+
columns: ColumnProps[];
|
|
15
9
|
};
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// TODO(burdon): Reconcile with @dxos/schema/Field.
|
|
21
|
-
export type ColumnDef = {
|
|
11
|
+
export type ColumnType = 'number' | 'boolean' | 'date' | 'string' | 'ref';
|
|
12
|
+
|
|
13
|
+
export type ColumnProps = {
|
|
22
14
|
id: string;
|
|
23
15
|
prop: string;
|
|
24
|
-
type:
|
|
16
|
+
type: ColumnType;
|
|
25
17
|
size?: number;
|
|
26
18
|
label?: string;
|
|
27
19
|
|
package/src/translations.ts
CHANGED
|
@@ -10,7 +10,7 @@ export default [
|
|
|
10
10
|
[translationKey]: {
|
|
11
11
|
'no selection label': ' ',
|
|
12
12
|
'number column type label': 'Number',
|
|
13
|
-
'boolean column type label': '
|
|
13
|
+
'boolean column type label': 'Toggle',
|
|
14
14
|
'date column type label': 'Date',
|
|
15
15
|
'string column type label': 'Text',
|
|
16
16
|
'ref column type label': 'Reference',
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { type TableDef, type ColumnDef } from '../../schema';
|
|
3
|
-
export type ColumnSettingsProps = {
|
|
4
|
-
column: ColumnDef;
|
|
5
|
-
tableDef: TableDef;
|
|
6
|
-
tablesToReference: TableDef[];
|
|
7
|
-
onUpdate?: (id: string, column: ColumnDef) => void;
|
|
8
|
-
onDelete?: (id: string) => void;
|
|
9
|
-
onClose?: () => void;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* @deprecated
|
|
13
|
-
*/
|
|
14
|
-
export declare const ColumnSettings: ({ column, tableDef, tablesToReference, onUpdate, onDelete, onClose, }: ColumnSettingsProps) => React.JSX.Element;
|
|
15
|
-
//# sourceMappingURL=ColumnSettings.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnSettings.d.ts","sourceRoot":"","sources":["../../../../../src/components/ColumnMenu/ColumnSettings.tsx"],"names":[],"mappings":"AAIA,OAAO,KAA2B,MAAM,OAAO,CAAC;AAMhD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG7D,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IAEnB,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,0EAOxB,mBAAmB,sBA6KrB,CAAC"}
|