@dxos/react-ui-form 0.7.5-feature-compute.4d9d99a → 0.7.5-labs.071a3e2
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 +717 -468
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +742 -508
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +717 -468
- 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 +12 -21
- 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.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 +7 -6
- 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 +47 -355
- 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.tsx +5 -2
- package/src/components/deprecated/FormInput.tsx +20 -7
- package/src/hooks/useForm.ts +54 -47
- package/src/translations.ts +8 -0
package/src/hooks/useForm.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type BaseObject,
|
|
8
|
-
import { type
|
|
7
|
+
import { type BaseObject, getValue, setValue } from '@dxos/echo-schema';
|
|
8
|
+
import { AST, type S, type SimpleType, type JsonPath, createJsonPath, fromEffectValidationPath } from '@dxos/effect';
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
import { validateSchema, type ValidationError } from '@dxos/schema';
|
|
@@ -20,9 +20,9 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
20
20
|
//
|
|
21
21
|
|
|
22
22
|
values: Partial<T>;
|
|
23
|
-
errors: Record<
|
|
24
|
-
touched: Record<
|
|
25
|
-
changed: Record<
|
|
23
|
+
errors: Record<JsonPath, string>;
|
|
24
|
+
touched: Record<JsonPath, boolean>;
|
|
25
|
+
changed: Record<JsonPath, boolean>;
|
|
26
26
|
canSave: boolean;
|
|
27
27
|
handleSave: () => void;
|
|
28
28
|
|
|
@@ -30,10 +30,10 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
30
30
|
// Form input component helpers.
|
|
31
31
|
//
|
|
32
32
|
|
|
33
|
-
getStatus: (
|
|
34
|
-
getValue: <V>(
|
|
35
|
-
onValueChange: <V>(
|
|
36
|
-
|
|
33
|
+
getStatus: (path: string | (string | number)[]) => { status?: 'error'; error?: string };
|
|
34
|
+
getValue: <V>(path: (string | number)[]) => V | undefined;
|
|
35
|
+
onValueChange: <V>(path: (string | number)[], type: SimpleType, value: V) => void;
|
|
36
|
+
onTouched: (path: (string | number)[]) => void;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -95,16 +95,15 @@ export const useForm = <T extends BaseObject>({
|
|
|
95
95
|
setValues(initialValues);
|
|
96
96
|
}, [initialValues]);
|
|
97
97
|
|
|
98
|
-
const [touched, setTouched] = useState<Record<
|
|
99
|
-
const [changed, setChanged] = useState<Record<
|
|
100
|
-
const [errors, setErrors] = useState<Record<
|
|
98
|
+
const [touched, setTouched] = useState<Record<JsonPath, boolean>>(createKeySet(initialValues, false));
|
|
99
|
+
const [changed, setChanged] = useState<Record<JsonPath, boolean>>(createKeySet(initialValues, false));
|
|
100
|
+
const [errors, setErrors] = useState<Record<JsonPath, string>>({});
|
|
101
101
|
const [saving, setSaving] = useState(false);
|
|
102
102
|
|
|
103
103
|
//
|
|
104
104
|
// Validation.
|
|
105
105
|
//
|
|
106
106
|
|
|
107
|
-
// TODO(burdon): Validate each property separately.
|
|
108
107
|
const validate = useCallback(
|
|
109
108
|
(values: Partial<T>): values is T => {
|
|
110
109
|
let errors: ValidationError[] = validateSchema(schema, values) ?? [];
|
|
@@ -137,8 +136,15 @@ export const useForm = <T extends BaseObject>({
|
|
|
137
136
|
const canSave = useMemo(
|
|
138
137
|
() =>
|
|
139
138
|
!saving &&
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
// Check if any error paths that are touched have errors
|
|
140
|
+
!Object.entries(touched).some(
|
|
141
|
+
([path, isTouched]) =>
|
|
142
|
+
isTouched &&
|
|
143
|
+
Object.keys(errors).some(
|
|
144
|
+
(errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
[touched, errors, saving],
|
|
142
148
|
);
|
|
143
149
|
|
|
144
150
|
const handleSave = useCallback(async () => {
|
|
@@ -157,19 +163,27 @@ export const useForm = <T extends BaseObject>({
|
|
|
157
163
|
//
|
|
158
164
|
|
|
159
165
|
const getStatus = useCallback<FormHandler<T>['getStatus']>(
|
|
160
|
-
(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
(path) => {
|
|
167
|
+
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
168
|
+
const matchingError = Object.entries(errors).find(
|
|
169
|
+
([errorPath]) =>
|
|
170
|
+
errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
status: matchingError ? 'error' : undefined,
|
|
175
|
+
error: matchingError ? matchingError[1] : undefined,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
[errors],
|
|
165
179
|
);
|
|
166
180
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return getValue(values, property as any as JsonPath);
|
|
181
|
+
const getFormValue = <V>(path: (string | number)[]): V | undefined => {
|
|
182
|
+
return getValue(values, createJsonPath(path));
|
|
170
183
|
};
|
|
171
184
|
|
|
172
|
-
const onValueChange = (
|
|
185
|
+
const onValueChange = (path: (string | number)[], type: SimpleType, value: any) => {
|
|
186
|
+
const jsonPath = createJsonPath(path);
|
|
173
187
|
let parsedValue = value;
|
|
174
188
|
try {
|
|
175
189
|
if (type === 'number') {
|
|
@@ -180,9 +194,9 @@ export const useForm = <T extends BaseObject>({
|
|
|
180
194
|
parsedValue = undefined;
|
|
181
195
|
}
|
|
182
196
|
|
|
183
|
-
const newValues = { ...setValue(values,
|
|
197
|
+
const newValues = { ...setValue(values, jsonPath, parsedValue) };
|
|
184
198
|
setValues(newValues);
|
|
185
|
-
setChanged((prev) => ({ ...prev, [
|
|
199
|
+
setChanged((prev) => ({ ...prev, [jsonPath]: true }));
|
|
186
200
|
onValuesChanged?.(newValues);
|
|
187
201
|
|
|
188
202
|
const isValid = validate(newValues);
|
|
@@ -191,19 +205,10 @@ export const useForm = <T extends BaseObject>({
|
|
|
191
205
|
}
|
|
192
206
|
};
|
|
193
207
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
setTouched((touched) => ({ ...touched, [name]: true }));
|
|
199
|
-
|
|
200
|
-
// TODO(Zan): This should be configurable behavior.
|
|
201
|
-
if (event.relatedTarget?.getAttribute('type') === 'submit') {
|
|
202
|
-
// NOTE: We do this here instead of onSave because the blur event is triggered before the submit event
|
|
203
|
-
// and results in the submit button being disabled when the form is invalid.
|
|
204
|
-
setTouched(createKeySet(values, true));
|
|
205
|
-
}
|
|
206
|
-
|
|
208
|
+
const onTouched = useCallback(
|
|
209
|
+
(path: (string | number)[]) => {
|
|
210
|
+
const jsonPath = createJsonPath(path);
|
|
211
|
+
setTouched((touched) => ({ ...touched, [jsonPath]: true }));
|
|
207
212
|
validate(values);
|
|
208
213
|
},
|
|
209
214
|
[validate, values],
|
|
@@ -222,23 +227,25 @@ export const useForm = <T extends BaseObject>({
|
|
|
222
227
|
getStatus,
|
|
223
228
|
getValue: getFormValue,
|
|
224
229
|
onValueChange,
|
|
225
|
-
|
|
230
|
+
onTouched,
|
|
226
231
|
} satisfies FormHandler<T>;
|
|
227
232
|
};
|
|
228
233
|
|
|
229
|
-
const createKeySet = <T extends BaseObject, V>(obj: T, value: V): Record<
|
|
234
|
+
const createKeySet = <T extends BaseObject, V>(obj: T, value: V): Record<JsonPath, V> => {
|
|
230
235
|
invariant(obj);
|
|
231
|
-
return Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: value }), {} as Record<
|
|
236
|
+
return Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: value }), {} as Record<JsonPath, V>);
|
|
232
237
|
};
|
|
233
238
|
|
|
234
|
-
const flatMap =
|
|
239
|
+
const flatMap = (errors: ValidationError[]) => {
|
|
235
240
|
return errors.reduce(
|
|
236
241
|
(result, { path, message }) => {
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
// Convert the validation error path format to our JsonPath format
|
|
243
|
+
const jsonPath = fromEffectValidationPath(path);
|
|
244
|
+
if (!(jsonPath in result)) {
|
|
245
|
+
result[jsonPath] = message;
|
|
239
246
|
}
|
|
240
247
|
return result;
|
|
241
248
|
},
|
|
242
|
-
{} as Record<
|
|
249
|
+
{} as Record<JsonPath, string>,
|
|
243
250
|
);
|
|
244
251
|
};
|
package/src/translations.ts
CHANGED
|
@@ -42,6 +42,7 @@ export default [
|
|
|
42
42
|
'format ref': 'Reference',
|
|
43
43
|
'format regex': 'RegExp',
|
|
44
44
|
'format string': 'String',
|
|
45
|
+
'format single-select': 'Select',
|
|
45
46
|
'format text': 'Long text',
|
|
46
47
|
'format time': 'Time',
|
|
47
48
|
'format timestamp': 'Timestamp',
|
|
@@ -53,6 +54,13 @@ export default [
|
|
|
53
54
|
// Placeholders
|
|
54
55
|
'placeholder latitude': 'Latitude',
|
|
55
56
|
'placeholder longitude': 'Longitude',
|
|
57
|
+
|
|
58
|
+
// SelectOptionsInput
|
|
59
|
+
'select option label': 'Label',
|
|
60
|
+
'select option label placeholder': 'Option label',
|
|
61
|
+
'select option color': 'Color',
|
|
62
|
+
'select option delete': 'Delete',
|
|
63
|
+
'select option add': 'Add option',
|
|
56
64
|
},
|
|
57
65
|
},
|
|
58
66
|
},
|