@dxos/react-ui-form 0.7.5-main.9d26e3a → 0.7.5-main.9d2a38b
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 +76 -40
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +72 -36
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +76 -40
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +9 -3
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/components/FieldEditor/FieldEditor.tsx +56 -39
- package/src/components/Form/Form.tsx +30 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +25 -4
- package/src/components/ViewEditor/ViewEditor.tsx +9 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
|
|
2
|
+
import { type BaseObject, S, type PropertyKey } from '@dxos/echo-schema';
|
|
3
3
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
4
4
|
import { type SchemaProperty } from '@dxos/schema';
|
|
5
|
-
import { type InputComponent } from './Input';
|
|
5
|
+
import { type InputProps, type InputComponent } from './Input';
|
|
6
6
|
import { type FormOptions } from '../../hooks';
|
|
7
7
|
export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
8
8
|
export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
@@ -16,15 +16,21 @@ export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
|
16
16
|
autoSave?: boolean;
|
|
17
17
|
testId?: string;
|
|
18
18
|
onCancel?: () => void;
|
|
19
|
+
lookupComponent?: (args: {
|
|
20
|
+
prop: string;
|
|
21
|
+
schema: S.Schema<any>;
|
|
22
|
+
inputProps: InputProps<T>;
|
|
23
|
+
}) => React.ReactElement | undefined;
|
|
19
24
|
/**
|
|
20
25
|
* Map of custom renderers for specific properties.
|
|
26
|
+
* @deprecated Use lookupComponent instead.
|
|
21
27
|
*/
|
|
22
28
|
Custom?: Partial<Record<string, InputComponent<T>>>;
|
|
23
29
|
} & Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'>>;
|
|
24
30
|
/**
|
|
25
31
|
* General purpose form component that displays field controls based on the given schema.
|
|
26
32
|
*/
|
|
27
|
-
export declare const Form: <T extends BaseObject>({ classNames, schema, values: initialValues, path, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, Custom, }: FormProps<T>) => React.JSX.Element;
|
|
33
|
+
export declare const Form: <T extends BaseObject>({ classNames, schema, values: initialValues, path, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom, }: FormProps<T>) => React.JSX.Element;
|
|
28
34
|
type FormActionsProps = {
|
|
29
35
|
readonly?: boolean;
|
|
30
36
|
canSubmit: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Form.tsx"],"names":[],"mappings":"AAMA,OAAO,KAA6B,MAAM,OAAO,CAAC;AAElD,OAAO,EAAO,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Form.tsx"],"names":[],"mappings":"AAMA,OAAO,KAA6B,MAAM,OAAO,CAAC;AAElD,OAAO,EAAO,KAAK,UAAU,EAAE,CAAC,EAAE,KAAK,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAG/F,OAAO,EAAc,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AAElF,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAIxE,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5E,OAAO,EAAoB,KAAK,WAAW,EAAW,MAAM,aAAa,CAAC;AAO1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;AAEpG,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,UAAU,IAAI,eAAe,CAC3D;IACE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAEnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAGhB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;KAC3B,KAAK,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACrD,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,iBAAiB,GAAG,YAAY,GAAG,QAAQ,CAAC,CACjF,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,UAAU,0KAgBtC,SAAS,CAAC,CAAC,CAAC,sBA8Jd,CAAC;AAqJF,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,WAAW,gDAAiD,gBAAgB,sBAsBxF,CAAC"}
|
|
@@ -9,10 +9,11 @@ export type ViewEditorProps = ThemedClassName<{
|
|
|
9
9
|
registry?: SchemaRegistry;
|
|
10
10
|
readonly?: boolean;
|
|
11
11
|
showHeading?: boolean;
|
|
12
|
+
onTypenameChanged: (typename: string) => void;
|
|
12
13
|
onDelete: (fieldId: string) => void;
|
|
13
14
|
}>;
|
|
14
15
|
/**
|
|
15
16
|
* Schema-based object form.
|
|
16
17
|
*/
|
|
17
|
-
export declare const ViewEditor: ({ classNames, schema, view, registry, readonly, showHeading, onDelete, }: ViewEditorProps) => React.JSX.Element;
|
|
18
|
+
export declare const ViewEditor: ({ classNames, schema, view, registry, readonly, showHeading, onTypenameChanged, onDelete, }: ViewEditorProps) => React.JSX.Element;
|
|
18
19
|
//# sourceMappingURL=ViewEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewEditor.d.ts","sourceRoot":"","sources":["../../../../../src/components/ViewEditor/ViewEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAe,KAAK,UAAU,EAAK,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAc,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AAGlF,OAAO,EAA+B,KAAK,QAAQ,EAAoC,MAAM,cAAc,CAAC;AAoB5G,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC;IAC5C,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"ViewEditor.d.ts","sourceRoot":"","sources":["../../../../../src/components/ViewEditor/ViewEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAe,KAAK,UAAU,EAAK,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAc,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AAGlF,OAAO,EAA+B,KAAK,QAAQ,EAAoC,MAAM,cAAc,CAAC;AAoB5G,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC;IAC5C,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU,gGASpB,eAAe,sBA8GjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewEditor.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ViewEditor/ViewEditor.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAU5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ViewEditor.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ViewEditor/ViewEditor.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAU5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA+D1C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CAQjC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC;AAEtB,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"5.7.
|
|
1
|
+
{"version":"5.7.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-form",
|
|
3
|
-
"version": "0.7.5-main.
|
|
3
|
+
"version": "0.7.5-main.9d2a38b",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
"date-fns": "^3.3.1",
|
|
30
30
|
"effect": "^3.12.1",
|
|
31
31
|
"jsonpath": "^1.1.1",
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/echo-
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/keys": "0.7.5-main.
|
|
38
|
-
"@dxos/invariant": "0.7.5-main.
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/react-client": "0.7.5-main.
|
|
42
|
-
"@dxos/react-ui-list": "0.7.5-main.
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/util": "0.7.5-main.
|
|
32
|
+
"@dxos/async": "0.7.5-main.9d2a38b",
|
|
33
|
+
"@dxos/debug": "0.7.5-main.9d2a38b",
|
|
34
|
+
"@dxos/echo-db": "0.7.5-main.9d2a38b",
|
|
35
|
+
"@dxos/echo-schema": "0.7.5-main.9d2a38b",
|
|
36
|
+
"@dxos/effect": "0.7.5-main.9d2a38b",
|
|
37
|
+
"@dxos/keys": "0.7.5-main.9d2a38b",
|
|
38
|
+
"@dxos/invariant": "0.7.5-main.9d2a38b",
|
|
39
|
+
"@dxos/live-object": "0.7.5-main.9d2a38b",
|
|
40
|
+
"@dxos/log": "0.7.5-main.9d2a38b",
|
|
41
|
+
"@dxos/react-client": "0.7.5-main.9d2a38b",
|
|
42
|
+
"@dxos/react-ui-list": "0.7.5-main.9d2a38b",
|
|
43
|
+
"@dxos/schema": "0.7.5-main.9d2a38b",
|
|
44
|
+
"@dxos/react-ui-types": "0.7.5-main.9d2a38b",
|
|
45
|
+
"@dxos/util": "0.7.5-main.9d2a38b"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
"react": "~18.2.0",
|
|
54
54
|
"react-dom": "~18.2.0",
|
|
55
55
|
"vite": "5.4.7",
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/random": "0.7.5-main.
|
|
59
|
-
"@dxos/react-ui-syntax-highlighter": "0.7.5-main.
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/
|
|
56
|
+
"@dxos/echo-signals": "0.7.5-main.9d2a38b",
|
|
57
|
+
"@dxos/react-ui": "0.7.5-main.9d2a38b",
|
|
58
|
+
"@dxos/random": "0.7.5-main.9d2a38b",
|
|
59
|
+
"@dxos/react-ui-syntax-highlighter": "0.7.5-main.9d2a38b",
|
|
60
|
+
"@dxos/react-ui-theme": "0.7.5-main.9d2a38b",
|
|
61
|
+
"@dxos/storybook-utils": "0.7.5-main.9d2a38b"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"@effect/schema": "^0.75.5",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"effect": "^3.9.2",
|
|
67
67
|
"react": "~18.2.0",
|
|
68
68
|
"react-dom": "~18.2.0",
|
|
69
|
-
"@dxos/react-ui": "0.7.5-main.
|
|
70
|
-
"@dxos/react-ui-theme": "0.7.5-main.
|
|
69
|
+
"@dxos/react-ui": "0.7.5-main.9d2a38b",
|
|
70
|
+
"@dxos/react-ui-theme": "0.7.5-main.9d2a38b"
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"access": "public"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
5
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type SchemaRegistry } from '@dxos/echo-db';
|
|
8
8
|
import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
|
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
type PropertyType,
|
|
17
17
|
type ViewType,
|
|
18
18
|
type ViewProjection,
|
|
19
|
+
type SchemaProperty,
|
|
19
20
|
} from '@dxos/schema';
|
|
20
21
|
|
|
21
22
|
import { translationKey } from '../../translations';
|
|
22
|
-
import { Form, type FormProps, SelectInput } from '../Form';
|
|
23
|
+
import { Form, type FormProps, type InputComponent, SelectInput } from '../Form';
|
|
23
24
|
|
|
24
25
|
export type FieldEditorProps = {
|
|
25
26
|
view: ViewType;
|
|
@@ -57,9 +58,9 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
57
58
|
};
|
|
58
59
|
}, [registry]);
|
|
59
60
|
|
|
60
|
-
const [
|
|
61
|
+
const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
|
|
61
62
|
useEffect(() => {
|
|
62
|
-
|
|
63
|
+
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
63
64
|
}, [schemas, props?.referenceSchema]);
|
|
64
65
|
|
|
65
66
|
// TODO(burdon): Need to wrap otherwise throws error:
|
|
@@ -78,9 +79,15 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
78
79
|
}
|
|
79
80
|
return { fieldSchema };
|
|
80
81
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
setReferenceSchema((prev) => {
|
|
83
|
+
if (_props.referenceSchema !== prev?.typename) {
|
|
84
|
+
const newSchema = schemas.find((schema) => schema.typename === _props.referenceSchema);
|
|
85
|
+
if (newSchema) {
|
|
86
|
+
return newSchema;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return prev;
|
|
90
|
+
});
|
|
84
91
|
|
|
85
92
|
setProps((props) => {
|
|
86
93
|
const type = formatToType[_props.format as keyof typeof formatToType];
|
|
@@ -122,6 +129,46 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
122
129
|
onSave();
|
|
123
130
|
}, [onSave]);
|
|
124
131
|
|
|
132
|
+
const Custom: Partial<Record<string, InputComponent<PropertyType>>> = useMemo(
|
|
133
|
+
() => ({
|
|
134
|
+
['format' satisfies keyof PropertyType]: (props) => (
|
|
135
|
+
<SelectInput<PropertyType>
|
|
136
|
+
{...props}
|
|
137
|
+
options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
|
|
138
|
+
value,
|
|
139
|
+
label: t(`format ${value}`),
|
|
140
|
+
}))}
|
|
141
|
+
/>
|
|
142
|
+
),
|
|
143
|
+
['referenceSchema' satisfies keyof PropertyType]: (props) => (
|
|
144
|
+
<SelectInput<PropertyType>
|
|
145
|
+
{...props}
|
|
146
|
+
options={schemas.map((schema) => ({
|
|
147
|
+
value: schema.typename,
|
|
148
|
+
}))}
|
|
149
|
+
/>
|
|
150
|
+
),
|
|
151
|
+
['referencePath' satisfies keyof PropertyType]: (props) => (
|
|
152
|
+
<SelectInput<PropertyType>
|
|
153
|
+
{...props}
|
|
154
|
+
options={
|
|
155
|
+
referenceSchema
|
|
156
|
+
? getSchemaProperties(referenceSchema.ast)
|
|
157
|
+
.sort(sortProperties)
|
|
158
|
+
.map((p) => ({ value: p.name }))
|
|
159
|
+
: []
|
|
160
|
+
}
|
|
161
|
+
/>
|
|
162
|
+
),
|
|
163
|
+
}),
|
|
164
|
+
[t, schemas, referenceSchema],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const propIsNotType = useCallback(
|
|
168
|
+
(props: SchemaProperty<PropertyType>[]) => props.filter((p) => p.name !== 'type'),
|
|
169
|
+
[],
|
|
170
|
+
);
|
|
171
|
+
|
|
125
172
|
if (!fieldSchema) {
|
|
126
173
|
log.warn('invalid format', { props });
|
|
127
174
|
return null;
|
|
@@ -133,43 +180,13 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
133
180
|
autoFocus
|
|
134
181
|
values={props}
|
|
135
182
|
schema={fieldSchema}
|
|
136
|
-
filter={
|
|
183
|
+
filter={propIsNotType}
|
|
137
184
|
sort={['property', 'format']}
|
|
138
185
|
onValuesChanged={handleValuesChanged}
|
|
139
186
|
onValidate={handleValidate}
|
|
140
187
|
onSave={handleSave}
|
|
141
188
|
onCancel={handleCancel}
|
|
142
|
-
Custom={
|
|
143
|
-
['format' satisfies keyof PropertyType]: (props) => (
|
|
144
|
-
<SelectInput<PropertyType>
|
|
145
|
-
{...props}
|
|
146
|
-
options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
|
|
147
|
-
value,
|
|
148
|
-
label: t(`format ${value}`),
|
|
149
|
-
}))}
|
|
150
|
-
/>
|
|
151
|
-
),
|
|
152
|
-
['referenceSchema' satisfies keyof PropertyType]: (props) => (
|
|
153
|
-
<SelectInput<PropertyType>
|
|
154
|
-
{...props}
|
|
155
|
-
options={schemas.map((schema) => ({
|
|
156
|
-
value: schema.typename,
|
|
157
|
-
}))}
|
|
158
|
-
/>
|
|
159
|
-
),
|
|
160
|
-
['referencePath' satisfies keyof PropertyType]: (props) => (
|
|
161
|
-
<SelectInput<PropertyType>
|
|
162
|
-
{...props}
|
|
163
|
-
options={
|
|
164
|
-
schema
|
|
165
|
-
? getSchemaProperties(schema.ast)
|
|
166
|
-
.sort(sortProperties)
|
|
167
|
-
.map((p) => ({ value: p.name }))
|
|
168
|
-
: []
|
|
169
|
-
}
|
|
170
|
-
/>
|
|
171
|
-
),
|
|
172
|
-
}}
|
|
189
|
+
Custom={Custom}
|
|
173
190
|
/>
|
|
174
191
|
);
|
|
175
192
|
};
|
|
@@ -15,7 +15,7 @@ import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
|
|
|
15
15
|
import { isNotFalsy } from '@dxos/util';
|
|
16
16
|
|
|
17
17
|
import { SelectInput } from './Defaults';
|
|
18
|
-
import { InputHeader, type InputComponent } from './Input';
|
|
18
|
+
import { InputHeader, type InputProps, type InputComponent } from './Input';
|
|
19
19
|
import { getInputComponent } from './factory';
|
|
20
20
|
import { type FormHandler, type FormOptions, useForm } from '../../hooks';
|
|
21
21
|
import { translationKey } from '../../translations';
|
|
@@ -43,8 +43,14 @@ export type FormProps<T extends BaseObject> = ThemedClassName<
|
|
|
43
43
|
testId?: string;
|
|
44
44
|
onCancel?: () => void;
|
|
45
45
|
|
|
46
|
+
lookupComponent?: (args: {
|
|
47
|
+
prop: string;
|
|
48
|
+
schema: S.Schema<any>;
|
|
49
|
+
inputProps: InputProps<T>;
|
|
50
|
+
}) => React.ReactElement | undefined;
|
|
46
51
|
/**
|
|
47
52
|
* Map of custom renderers for specific properties.
|
|
53
|
+
* @deprecated Use lookupComponent instead.
|
|
48
54
|
*/
|
|
49
55
|
Custom?: Partial<Record<string, InputComponent<T>>>;
|
|
50
56
|
} & Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'>
|
|
@@ -68,6 +74,7 @@ export const Form = <T extends BaseObject>({
|
|
|
68
74
|
onValidate,
|
|
69
75
|
onSave,
|
|
70
76
|
onCancel,
|
|
77
|
+
lookupComponent,
|
|
71
78
|
Custom,
|
|
72
79
|
}: FormProps<T>) => {
|
|
73
80
|
const onValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
|
|
@@ -109,6 +116,28 @@ export const Form = <T extends BaseObject>({
|
|
|
109
116
|
const label = title ?? pipe(name, capitalize);
|
|
110
117
|
const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
|
|
111
118
|
|
|
119
|
+
const FoundComponent = lookupComponent?.({
|
|
120
|
+
prop: name,
|
|
121
|
+
schema: S.make(ast),
|
|
122
|
+
inputProps: {
|
|
123
|
+
property: name,
|
|
124
|
+
type,
|
|
125
|
+
format,
|
|
126
|
+
label,
|
|
127
|
+
disabled: readonly,
|
|
128
|
+
placeholder,
|
|
129
|
+
...inputProps,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (FoundComponent) {
|
|
134
|
+
return (
|
|
135
|
+
<div key={name} role='none' className={padding}>
|
|
136
|
+
{FoundComponent}
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
112
141
|
// Get generic input.
|
|
113
142
|
// TODO(ZaymonFC): We might need to switch this to using globs since we're now indexing into arrays.
|
|
114
143
|
let InputComponent = Custom?.[key.join('.')];
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
-
import React, { useCallback, useState } from 'react';
|
|
8
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Format, type EchoSchema, S, toJsonSchema, TypedObject } from '@dxos/echo-schema';
|
|
11
|
-
import { useSpace } from '@dxos/react-client/echo';
|
|
11
|
+
import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
12
12
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
13
13
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
14
|
-
import {
|
|
14
|
+
import { ViewProjection, ViewType, createView } from '@dxos/schema';
|
|
15
15
|
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
16
16
|
|
|
17
17
|
import { ViewEditor } from './ViewEditor';
|
|
@@ -41,6 +41,21 @@ const DefaultStory = () => {
|
|
|
41
41
|
}
|
|
42
42
|
}, [space]);
|
|
43
43
|
|
|
44
|
+
const views = useQuery(space, Filter.schema(ViewType));
|
|
45
|
+
const currentTypename = useMemo(() => view?.query?.type, [view]);
|
|
46
|
+
const updateViewTypename = useCallback(
|
|
47
|
+
(newTypename: string) => {
|
|
48
|
+
if (!schema) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const matchingViews = views.filter((view) => view.query.type === currentTypename);
|
|
52
|
+
for (const view of matchingViews) {
|
|
53
|
+
view.query.type = newTypename;
|
|
54
|
+
}
|
|
55
|
+
schema.updateTypename(newTypename);
|
|
56
|
+
},
|
|
57
|
+
[views, schema],
|
|
58
|
+
);
|
|
44
59
|
const handleDelete = useCallback((property: string) => projection?.deleteFieldProjection(property), [projection]);
|
|
45
60
|
|
|
46
61
|
if (!schema || !view || !projection) {
|
|
@@ -50,7 +65,13 @@ const DefaultStory = () => {
|
|
|
50
65
|
return (
|
|
51
66
|
<TestLayout json={{ schema, view, projection }}>
|
|
52
67
|
<TestPanel>
|
|
53
|
-
<ViewEditor
|
|
68
|
+
<ViewEditor
|
|
69
|
+
schema={schema}
|
|
70
|
+
view={view}
|
|
71
|
+
registry={space?.db.schemaRegistry}
|
|
72
|
+
onTypenameChanged={updateViewTypename}
|
|
73
|
+
onDelete={handleDelete}
|
|
74
|
+
/>
|
|
54
75
|
</TestPanel>
|
|
55
76
|
</TestLayout>
|
|
56
77
|
);
|
|
@@ -35,6 +35,7 @@ export type ViewEditorProps = ThemedClassName<{
|
|
|
35
35
|
registry?: SchemaRegistry;
|
|
36
36
|
readonly?: boolean;
|
|
37
37
|
showHeading?: boolean;
|
|
38
|
+
onTypenameChanged: (typename: string) => void;
|
|
38
39
|
onDelete: (fieldId: string) => void;
|
|
39
40
|
}>;
|
|
40
41
|
|
|
@@ -48,6 +49,7 @@ export const ViewEditor = ({
|
|
|
48
49
|
registry,
|
|
49
50
|
readonly,
|
|
50
51
|
showHeading = false,
|
|
52
|
+
onTypenameChanged,
|
|
51
53
|
onDelete,
|
|
52
54
|
}: ViewEditorProps) => {
|
|
53
55
|
const { t } = useTranslation(translationKey);
|
|
@@ -67,12 +69,15 @@ export const ViewEditor = ({
|
|
|
67
69
|
const handleViewUpdate = useCallback(
|
|
68
70
|
({ name, typename }: ViewMetaType) => {
|
|
69
71
|
requestAnimationFrame(() => {
|
|
70
|
-
view.name
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
if (view.name !== name) {
|
|
73
|
+
view.name = name;
|
|
74
|
+
}
|
|
75
|
+
if (view.query.type !== typename) {
|
|
76
|
+
onTypenameChanged(typename);
|
|
77
|
+
}
|
|
73
78
|
});
|
|
74
79
|
},
|
|
75
|
-
[view, schema],
|
|
80
|
+
[view, schema, onTypenameChanged],
|
|
76
81
|
);
|
|
77
82
|
|
|
78
83
|
const handleSelect = useCallback((field: FieldType) => {
|