@dxos/react-ui-form 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b
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 +743 -466
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +777 -515
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +743 -466
- package/dist/lib/node-esm/index.mjs.map +4 -4
- 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/ArrayField.d.ts +16 -0
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/Defaults.d.ts +5 -6
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +13 -16
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -1
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +7 -0
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContent.d.ts +26 -0
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContext.d.ts +20 -0
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Input.d.ts +5 -6
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +4 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +7 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/index.d.ts +1 -0
- package/dist/types/src/components/Form/custom/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts +2 -2
- package/dist/types/src/components/Form/factory.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/src/components/deprecated/FormInput.d.ts +5 -0
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +9 -10
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +6 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -24
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.tsx +58 -40
- package/src/components/Form/ArrayField.tsx +111 -0
- package/src/components/Form/Defaults.tsx +18 -33
- package/src/components/Form/Form.stories.tsx +15 -5
- package/src/components/Form/Form.tsx +39 -330
- package/src/components/Form/FormActions.tsx +41 -0
- package/src/components/Form/FormContent.tsx +173 -0
- package/src/components/Form/FormContext.tsx +48 -0
- package/src/components/Form/Input.tsx +5 -6
- package/src/components/Form/custom/GeoPointInput.tsx +6 -15
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
- package/src/components/Form/custom/SelectOptionsInput.tsx +192 -0
- package/src/components/Form/custom/TupleInput.tsx +5 -9
- package/src/components/Form/custom/index.ts +1 -0
- package/src/components/Form/factory.tsx +2 -5
- package/src/components/ViewEditor/ViewEditor.stories.tsx +25 -4
- package/src/components/ViewEditor/ViewEditor.tsx +14 -6
- package/src/components/deprecated/FormInput.tsx +19 -3
- package/src/hooks/useForm.ts +54 -47
- package/src/translations.ts +8 -0
|
@@ -30,6 +30,7 @@ declare const _default: {
|
|
|
30
30
|
'format ref': string;
|
|
31
31
|
'format regex': string;
|
|
32
32
|
'format string': string;
|
|
33
|
+
'format single-select': string;
|
|
33
34
|
'format text': string;
|
|
34
35
|
'format time': string;
|
|
35
36
|
'format timestamp': string;
|
|
@@ -39,6 +40,11 @@ declare const _default: {
|
|
|
39
40
|
'format uuid': string;
|
|
40
41
|
'placeholder latitude': string;
|
|
41
42
|
'placeholder longitude': string;
|
|
43
|
+
'select option label': string;
|
|
44
|
+
'select option label placeholder': string;
|
|
45
|
+
'select option color': string;
|
|
46
|
+
'select option delete': string;
|
|
47
|
+
'select option add': string;
|
|
42
48
|
};
|
|
43
49
|
};
|
|
44
50
|
}[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBA4DE"}
|
|
@@ -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.e9bb01b",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -25,24 +25,25 @@
|
|
|
25
25
|
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
|
26
26
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
|
27
27
|
"@effect/schema": "^0.75.5",
|
|
28
|
-
"@radix-ui/react-context": "
|
|
28
|
+
"@radix-ui/react-context": "1.1.1",
|
|
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/effect": "0.7.5-main.
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/keys": "0.7.5-main.
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/react-client": "0.7.5-main.
|
|
42
|
-
"@dxos/react-ui-
|
|
43
|
-
"@dxos/react-ui-
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/util": "0.7.5-main.
|
|
32
|
+
"@dxos/async": "0.7.5-main.e9bb01b",
|
|
33
|
+
"@dxos/debug": "0.7.5-main.e9bb01b",
|
|
34
|
+
"@dxos/echo-db": "0.7.5-main.e9bb01b",
|
|
35
|
+
"@dxos/effect": "0.7.5-main.e9bb01b",
|
|
36
|
+
"@dxos/invariant": "0.7.5-main.e9bb01b",
|
|
37
|
+
"@dxos/keys": "0.7.5-main.e9bb01b",
|
|
38
|
+
"@dxos/echo-schema": "0.7.5-main.e9bb01b",
|
|
39
|
+
"@dxos/live-object": "0.7.5-main.e9bb01b",
|
|
40
|
+
"@dxos/log": "0.7.5-main.e9bb01b",
|
|
41
|
+
"@dxos/react-client": "0.7.5-main.e9bb01b",
|
|
42
|
+
"@dxos/react-ui-pickers": "0.7.5-main.e9bb01b",
|
|
43
|
+
"@dxos/react-ui-list": "0.7.5-main.e9bb01b",
|
|
44
|
+
"@dxos/react-ui-types": "0.7.5-main.e9bb01b",
|
|
45
|
+
"@dxos/util": "0.7.5-main.e9bb01b",
|
|
46
|
+
"@dxos/schema": "0.7.5-main.e9bb01b"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -53,12 +54,12 @@
|
|
|
53
54
|
"react": "~18.2.0",
|
|
54
55
|
"react-dom": "~18.2.0",
|
|
55
56
|
"vite": "5.4.7",
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/react-ui-
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/
|
|
57
|
+
"@dxos/echo-signals": "0.7.5-main.e9bb01b",
|
|
58
|
+
"@dxos/react-ui": "0.7.5-main.e9bb01b",
|
|
59
|
+
"@dxos/react-ui-syntax-highlighter": "0.7.5-main.e9bb01b",
|
|
60
|
+
"@dxos/react-ui-theme": "0.7.5-main.e9bb01b",
|
|
61
|
+
"@dxos/random": "0.7.5-main.e9bb01b",
|
|
62
|
+
"@dxos/storybook-utils": "0.7.5-main.e9bb01b"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
65
|
"@effect/schema": "^0.75.5",
|
|
@@ -66,8 +67,8 @@
|
|
|
66
67
|
"effect": "^3.9.2",
|
|
67
68
|
"react": "~18.2.0",
|
|
68
69
|
"react-dom": "~18.2.0",
|
|
69
|
-
"@dxos/react-ui": "0.7.5-main.
|
|
70
|
-
"@dxos/react-ui-theme": "0.7.5-main.
|
|
70
|
+
"@dxos/react-ui": "0.7.5-main.e9bb01b",
|
|
71
|
+
"@dxos/react-ui-theme": "0.7.5-main.e9bb01b"
|
|
71
72
|
},
|
|
72
73
|
"publishConfig": {
|
|
73
74
|
"access": "public"
|
|
@@ -37,7 +37,7 @@ const meta: Meta<StoryProps> = {
|
|
|
37
37
|
title: 'ui/react-ui-form/FieldEditor',
|
|
38
38
|
component: FieldEditor,
|
|
39
39
|
render: DefaultStory,
|
|
40
|
-
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
40
|
+
decorators: [withLayout({ fullscreen: true, tooltips: true }), withTheme],
|
|
41
41
|
parameters: {
|
|
42
42
|
translations,
|
|
43
43
|
},
|
|
@@ -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, SelectOptionInput } 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];
|
|
@@ -88,7 +95,7 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
88
95
|
return { ...props, ..._props, type };
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
return
|
|
98
|
+
return _props as PropertyType;
|
|
92
99
|
});
|
|
93
100
|
},
|
|
94
101
|
[schemas, props.format, props.referenceSchema],
|
|
@@ -122,6 +129,47 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
122
129
|
onSave();
|
|
123
130
|
}, [onSave]);
|
|
124
131
|
|
|
132
|
+
const Custom: Partial<Record<string, InputComponent>> = useMemo(
|
|
133
|
+
() => ({
|
|
134
|
+
['format' satisfies keyof PropertyType]: (props) => (
|
|
135
|
+
<SelectInput
|
|
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
|
|
145
|
+
{...props}
|
|
146
|
+
options={schemas.map((schema) => ({
|
|
147
|
+
value: schema.typename,
|
|
148
|
+
}))}
|
|
149
|
+
/>
|
|
150
|
+
),
|
|
151
|
+
['referencePath' satisfies keyof PropertyType]: (props) => (
|
|
152
|
+
<SelectInput
|
|
153
|
+
{...props}
|
|
154
|
+
options={
|
|
155
|
+
referenceSchema
|
|
156
|
+
? getSchemaProperties(referenceSchema.ast)
|
|
157
|
+
.sort(sortProperties)
|
|
158
|
+
.map((p) => ({ value: p.name }))
|
|
159
|
+
: []
|
|
160
|
+
}
|
|
161
|
+
/>
|
|
162
|
+
),
|
|
163
|
+
['options' satisfies keyof PropertyType]: (props) => <SelectOptionInput {...props} />,
|
|
164
|
+
}),
|
|
165
|
+
[t, schemas, referenceSchema],
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const propIsNotType = useCallback(
|
|
169
|
+
(props: SchemaProperty<PropertyType>[]) => props.filter((p) => p.name !== 'type'),
|
|
170
|
+
[],
|
|
171
|
+
);
|
|
172
|
+
|
|
125
173
|
if (!fieldSchema) {
|
|
126
174
|
log.warn('invalid format', { props });
|
|
127
175
|
return null;
|
|
@@ -133,43 +181,13 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
133
181
|
autoFocus
|
|
134
182
|
values={props}
|
|
135
183
|
schema={fieldSchema}
|
|
136
|
-
filter={
|
|
184
|
+
filter={propIsNotType}
|
|
137
185
|
sort={['property', 'format']}
|
|
138
186
|
onValuesChanged={handleValuesChanged}
|
|
139
187
|
onValidate={handleValidate}
|
|
140
188
|
onSave={handleSave}
|
|
141
189
|
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
|
-
}}
|
|
190
|
+
Custom={Custom}
|
|
173
191
|
/>
|
|
174
192
|
);
|
|
175
193
|
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { pipe } from 'effect';
|
|
6
|
+
import { capitalize } from 'effect/String';
|
|
7
|
+
import React, { useCallback, useMemo } from 'react';
|
|
8
|
+
|
|
9
|
+
import { AST } from '@dxos/echo-schema';
|
|
10
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from '@dxos/effect';
|
|
11
|
+
import { invariant } from '@dxos/invariant';
|
|
12
|
+
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
13
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
14
|
+
import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
|
|
15
|
+
|
|
16
|
+
import { type ComponentLookup } from './Form';
|
|
17
|
+
import { FormField } from './FormContent';
|
|
18
|
+
import { useFormValues, type FormInputStateProps } from './FormContext';
|
|
19
|
+
import { InputHeader, type InputComponent } from './Input';
|
|
20
|
+
import { translationKey } from '../../translations';
|
|
21
|
+
|
|
22
|
+
const padding = 'px-2';
|
|
23
|
+
|
|
24
|
+
type ArrayFieldProps = {
|
|
25
|
+
property: SchemaProperty<any>;
|
|
26
|
+
readonly?: boolean;
|
|
27
|
+
inputProps: FormInputStateProps;
|
|
28
|
+
path?: (string | number)[];
|
|
29
|
+
Custom?: Partial<Record<string, InputComponent>>;
|
|
30
|
+
lookupComponent?: ComponentLookup;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }: ArrayFieldProps) => {
|
|
34
|
+
const { t } = useTranslation(translationKey);
|
|
35
|
+
const { ast, name, type, title } = property;
|
|
36
|
+
const values = useFormValues(path ?? []) as any[];
|
|
37
|
+
invariant(Array.isArray(values), `Values at path ${path?.join('.')} must be an array.`);
|
|
38
|
+
const label = title ?? pipe(name, capitalize);
|
|
39
|
+
|
|
40
|
+
const tupleType = findNode(ast, AST.isTupleType);
|
|
41
|
+
const elementType = (tupleType as AST.TupleType | undefined)?.rest[0]?.type;
|
|
42
|
+
|
|
43
|
+
const getDefaultObjectValue = (typeNode: AST.AST): any => {
|
|
44
|
+
const baseNode = findNode(typeNode, isDiscriminatedUnion);
|
|
45
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST.isTypeLiteral);
|
|
46
|
+
if (!typeLiteral) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [prop.name, prop.defaultValue]));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const newValue = useMemo(() => {
|
|
54
|
+
if (type === 'object' && elementType) {
|
|
55
|
+
return getDefaultObjectValue(elementType);
|
|
56
|
+
} else {
|
|
57
|
+
return SimpleType.getDefaultValue(type);
|
|
58
|
+
}
|
|
59
|
+
}, [type, elementType]);
|
|
60
|
+
|
|
61
|
+
const handleAdd = useCallback(() => {
|
|
62
|
+
inputProps.onValueChange(type, [...values, newValue]);
|
|
63
|
+
}, [type, elementType, inputProps, newValue, values]);
|
|
64
|
+
|
|
65
|
+
const handleRemove = useCallback(
|
|
66
|
+
(index: number) => {
|
|
67
|
+
const newValues = values.filter((_, i) => i !== index);
|
|
68
|
+
inputProps.onValueChange(type, newValues);
|
|
69
|
+
},
|
|
70
|
+
[type, inputProps, values],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (!elementType) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div role='none' className={mx(padding)}>
|
|
79
|
+
<InputHeader>{label}</InputHeader>
|
|
80
|
+
<div role='none' className='flex flex-col gap-1'>
|
|
81
|
+
{values.map((_value, index) => (
|
|
82
|
+
<div key={index} role='none' className='flex items-center gap-1'>
|
|
83
|
+
<div role='none' className='flex-1'>
|
|
84
|
+
<FormField
|
|
85
|
+
property={{
|
|
86
|
+
...property,
|
|
87
|
+
array: false, // NOTE(ZaymonFC): This breaks arrays of arrays but ¯\_(ツ)_/¯. Ping me if you need that.
|
|
88
|
+
ast: elementType,
|
|
89
|
+
}}
|
|
90
|
+
path={[...(path ?? []), index]}
|
|
91
|
+
readonly={readonly}
|
|
92
|
+
inline
|
|
93
|
+
Custom={Custom}
|
|
94
|
+
lookupComponent={lookupComponent}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
<IconButton
|
|
98
|
+
icon='ph--trash--regular'
|
|
99
|
+
iconOnly
|
|
100
|
+
label={t('button remove')}
|
|
101
|
+
onClick={() => handleRemove(index)}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
<div role='none' className='flex justify-between items-center plb-1'>
|
|
107
|
+
<IconButton icon='ph--plus--regular' iconOnly label={t('button add')} onClick={handleAdd} />
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { type BaseObject } from '@dxos/echo-schema';
|
|
8
7
|
import { Input, Select } from '@dxos/react-ui';
|
|
9
8
|
|
|
10
9
|
import { type InputProps, InputHeader } from './Input';
|
|
11
10
|
|
|
12
|
-
export const TextInput =
|
|
13
|
-
property,
|
|
11
|
+
export const TextInput = ({
|
|
14
12
|
type,
|
|
15
13
|
label,
|
|
16
14
|
inputOnly,
|
|
@@ -20,8 +18,8 @@ export const TextInput = <T extends BaseObject>({
|
|
|
20
18
|
getValue,
|
|
21
19
|
onValueChange,
|
|
22
20
|
onBlur,
|
|
23
|
-
}: InputProps
|
|
24
|
-
const { status, error } = getStatus
|
|
21
|
+
}: InputProps) => {
|
|
22
|
+
const { status, error } = getStatus();
|
|
25
23
|
|
|
26
24
|
return (
|
|
27
25
|
<Input.Root validationValence={status}>
|
|
@@ -33,8 +31,8 @@ export const TextInput = <T extends BaseObject>({
|
|
|
33
31
|
<Input.TextInput
|
|
34
32
|
disabled={disabled}
|
|
35
33
|
placeholder={placeholder}
|
|
36
|
-
value={getValue(
|
|
37
|
-
onChange={(event) => onValueChange(
|
|
34
|
+
value={getValue() ?? ''}
|
|
35
|
+
onChange={(event) => onValueChange(type, event.target.value)}
|
|
38
36
|
onBlur={onBlur}
|
|
39
37
|
/>
|
|
40
38
|
{inputOnly && <Input.Validation>{error}</Input.Validation>}
|
|
@@ -42,8 +40,7 @@ export const TextInput = <T extends BaseObject>({
|
|
|
42
40
|
);
|
|
43
41
|
};
|
|
44
42
|
|
|
45
|
-
export const NumberInput =
|
|
46
|
-
property,
|
|
43
|
+
export const NumberInput = ({
|
|
47
44
|
type,
|
|
48
45
|
label,
|
|
49
46
|
inputOnly,
|
|
@@ -53,8 +50,8 @@ export const NumberInput = <T extends BaseObject>({
|
|
|
53
50
|
getValue,
|
|
54
51
|
onValueChange,
|
|
55
52
|
onBlur,
|
|
56
|
-
}: InputProps
|
|
57
|
-
const { status, error } = getStatus
|
|
53
|
+
}: InputProps) => {
|
|
54
|
+
const { status, error } = getStatus();
|
|
58
55
|
|
|
59
56
|
return (
|
|
60
57
|
<Input.Root validationValence={status}>
|
|
@@ -67,8 +64,8 @@ export const NumberInput = <T extends BaseObject>({
|
|
|
67
64
|
type='number'
|
|
68
65
|
disabled={disabled}
|
|
69
66
|
placeholder={placeholder}
|
|
70
|
-
value={getValue(
|
|
71
|
-
onChange={(event) => onValueChange(
|
|
67
|
+
value={getValue() ?? 0}
|
|
68
|
+
onChange={(event) => onValueChange(type, event.target.value)}
|
|
72
69
|
onBlur={onBlur}
|
|
73
70
|
/>
|
|
74
71
|
{inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
|
|
@@ -76,16 +73,8 @@ export const NumberInput = <T extends BaseObject>({
|
|
|
76
73
|
);
|
|
77
74
|
};
|
|
78
75
|
|
|
79
|
-
export const BooleanInput =
|
|
80
|
-
|
|
81
|
-
type,
|
|
82
|
-
label,
|
|
83
|
-
inputOnly,
|
|
84
|
-
getStatus,
|
|
85
|
-
getValue,
|
|
86
|
-
onValueChange,
|
|
87
|
-
}: InputProps<T>) => {
|
|
88
|
-
const { status, error } = getStatus?.(property);
|
|
76
|
+
export const BooleanInput = ({ type, label, inputOnly, getStatus, getValue, onValueChange }: InputProps) => {
|
|
77
|
+
const { status, error } = getStatus();
|
|
89
78
|
|
|
90
79
|
return (
|
|
91
80
|
<Input.Root validationValence={status}>
|
|
@@ -94,21 +83,17 @@ export const BooleanInput = <T extends BaseObject>({
|
|
|
94
83
|
<Input.Label>{label}</Input.Label>
|
|
95
84
|
</InputHeader>
|
|
96
85
|
)}
|
|
97
|
-
<Input.Switch
|
|
98
|
-
checked={getValue<boolean>(property)}
|
|
99
|
-
onCheckedChange={(value) => onValueChange(property, type, value)}
|
|
100
|
-
/>
|
|
86
|
+
<Input.Switch checked={getValue()} onCheckedChange={(value) => onValueChange(type, value)} />
|
|
101
87
|
{inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
|
|
102
88
|
</Input.Root>
|
|
103
89
|
);
|
|
104
90
|
};
|
|
105
91
|
|
|
106
|
-
export type SelectInputOptions
|
|
92
|
+
export type SelectInputOptions = InputProps & {
|
|
107
93
|
options?: Array<{ value: string | number; label?: string }>;
|
|
108
94
|
};
|
|
109
95
|
|
|
110
|
-
export const SelectInput =
|
|
111
|
-
property,
|
|
96
|
+
export const SelectInput = ({
|
|
112
97
|
type,
|
|
113
98
|
label,
|
|
114
99
|
inputOnly,
|
|
@@ -118,8 +103,8 @@ export const SelectInput = <T extends BaseObject>({
|
|
|
118
103
|
getStatus,
|
|
119
104
|
getValue,
|
|
120
105
|
onValueChange,
|
|
121
|
-
}: SelectInputOptions
|
|
122
|
-
const { status, error } = getStatus
|
|
106
|
+
}: SelectInputOptions) => {
|
|
107
|
+
const { status, error } = getStatus();
|
|
123
108
|
|
|
124
109
|
return (
|
|
125
110
|
<Input.Root validationValence={status}>
|
|
@@ -128,7 +113,7 @@ export const SelectInput = <T extends BaseObject>({
|
|
|
128
113
|
<Input.Label>{label}</Input.Label>
|
|
129
114
|
</InputHeader>
|
|
130
115
|
)}
|
|
131
|
-
<Select.Root value={getValue(
|
|
116
|
+
<Select.Root value={getValue()} onValueChange={(value) => onValueChange(type, value)}>
|
|
132
117
|
{/* TODO(burdon): Placeholder not working? */}
|
|
133
118
|
<Select.TriggerButton classNames='is-full' disabled={disabled} placeholder={placeholder} />
|
|
134
119
|
<Select.Portal>
|
|
@@ -36,7 +36,7 @@ type TestType = S.Schema.Type<typeof TestSchema>;
|
|
|
36
36
|
|
|
37
37
|
type StoryProps<T extends BaseObject> = { schema: S.Schema<T> } & FormProps<T>;
|
|
38
38
|
|
|
39
|
-
const DefaultStory = <T extends BaseObject>({ schema, values: initialValues }: StoryProps<T>) => {
|
|
39
|
+
const DefaultStory = <T extends BaseObject>({ schema, values: initialValues, ...props }: StoryProps<T>) => {
|
|
40
40
|
const [values, setValues] = useState(initialValues);
|
|
41
41
|
const handleSave = useCallback<NonNullable<FormProps<T>['onSave']>>((values) => {
|
|
42
42
|
setValues(values);
|
|
@@ -45,7 +45,7 @@ const DefaultStory = <T extends BaseObject>({ schema, values: initialValues }: S
|
|
|
45
45
|
return (
|
|
46
46
|
<TestLayout json={{ values, schema: schema.ast.toJSON() }}>
|
|
47
47
|
<TestPanel>
|
|
48
|
-
<Form<T> schema={schema} values={values} onSave={handleSave} />
|
|
48
|
+
<Form<T> schema={schema} values={values} onSave={handleSave} {...props} />
|
|
49
49
|
</TestPanel>
|
|
50
50
|
</TestLayout>
|
|
51
51
|
);
|
|
@@ -78,7 +78,6 @@ export const Default: Story<TestType> = {
|
|
|
78
78
|
},
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
// TODO(burdon): Should accept partial values.
|
|
82
81
|
export const Org: Story<Testing.OrgSchemaType> = {
|
|
83
82
|
args: {
|
|
84
83
|
schema: Testing.OrgSchema,
|
|
@@ -89,6 +88,17 @@ export const Org: Story<Testing.OrgSchemaType> = {
|
|
|
89
88
|
},
|
|
90
89
|
};
|
|
91
90
|
|
|
91
|
+
export const OrgAutoSave: Story<Testing.OrgSchemaType> = {
|
|
92
|
+
args: {
|
|
93
|
+
schema: Testing.OrgSchema,
|
|
94
|
+
values: {
|
|
95
|
+
name: 'DXOS',
|
|
96
|
+
// website: 'https://dxos.org',
|
|
97
|
+
},
|
|
98
|
+
autoSave: true,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
92
102
|
// TODO(burdon): Type issue with employer reference.
|
|
93
103
|
// TODO(burdon): Test table/form with compound values (e.g., address).
|
|
94
104
|
// export const Contact: Story<Testing.ContactSchemaType> = {
|
|
@@ -114,7 +124,7 @@ const ShapeSchema = S.Struct({
|
|
|
114
124
|
}),
|
|
115
125
|
S.Struct({
|
|
116
126
|
type: S.Literal('square').annotations({ [AST.TitleAnnotationId]: 'Type' }),
|
|
117
|
-
size: S.optional(S.Number.annotations({ [AST.TitleAnnotationId]: 'Size' })),
|
|
127
|
+
size: S.optional(S.Number.pipe(S.nonNegative()).annotations({ [AST.TitleAnnotationId]: 'Size' })),
|
|
118
128
|
}),
|
|
119
129
|
).annotations({ [AST.TitleAnnotationId]: 'Shape' }),
|
|
120
130
|
),
|
|
@@ -139,7 +149,7 @@ const DiscriminatedUnionStory = ({ values: initialValues }: DiscriminatedUnionSt
|
|
|
139
149
|
onSave={handleSave}
|
|
140
150
|
Custom={{
|
|
141
151
|
['shape.type' as const]: (props) => (
|
|
142
|
-
<SelectInput
|
|
152
|
+
<SelectInput
|
|
143
153
|
{...props}
|
|
144
154
|
options={['circle', 'square'].map((value) => ({
|
|
145
155
|
value,
|