@dxos/react-ui-form 0.8.4-main.67995b8 → 0.8.4-main.70d3990
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 +909 -675
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +909 -675
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +2 -2
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +81 -5
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +76 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +2 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +10 -8
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +144 -33
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +17 -11
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +5 -5
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +6 -3
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +15 -5
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +76 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +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/ObjectPicker/ObjectPicker.d.ts +41 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +82 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +20 -6
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +80 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +6 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +6 -6
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +9 -3
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +14 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field.d.ts +3 -3
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts +2 -2
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/refs.d.ts +1 -1
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -38
- package/src/components/ControlSection/ControlSection.stories.tsx +11 -14
- package/src/components/ControlSection/ControlSection.tsx +26 -12
- package/src/components/FieldEditor/FieldEditor.stories.tsx +16 -16
- package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
- package/src/components/FieldEditor/FieldEditor.tsx +28 -20
- package/src/components/Form/ArrayField.tsx +15 -12
- package/src/components/Form/Defaults.tsx +23 -21
- package/src/components/Form/Form.stories.tsx +144 -108
- package/src/components/Form/Form.tsx +33 -19
- package/src/components/Form/FormActions.tsx +9 -3
- package/src/components/Form/FormContent.tsx +172 -50
- package/src/components/Form/FormContext.tsx +11 -11
- package/src/components/Form/Input.tsx +6 -3
- package/src/components/Form/RefField.tsx +111 -41
- package/src/components/Form/custom/GeoPointInput.tsx +29 -21
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +11 -11
- package/src/components/Form/custom/SelectOptionsInput.tsx +31 -32
- package/src/components/Form/custom/TupleInput.tsx +2 -2
- package/src/components/Form/factory.tsx +8 -5
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +161 -0
- package/src/components/ObjectPicker/index.ts +5 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +123 -51
- package/src/components/ViewEditor/ViewEditor.test.tsx +13 -15
- package/src/components/ViewEditor/ViewEditor.tsx +271 -219
- package/src/components/index.ts +1 -2
- package/src/components/testing.tsx +15 -9
- package/src/hooks/useForm.ts +15 -10
- package/src/hooks/useQueryRefOptions.ts +15 -6
- package/src/hooks/useRefQueryLookupHandler.ts +5 -5
- package/src/translations.ts +16 -9
- package/src/util/field.ts +33 -43
- package/src/util/formatting.ts +22 -22
- package/src/util/refs.ts +7 -7
- package/src/util/schema.ts +2 -4
- package/dist/types/src/components/deprecated/Form.d.ts +0 -15
- package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/index.d.ts +0 -2
- package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
- package/src/components/deprecated/Form.stories.tsx +0 -62
- package/src/components/deprecated/Form.tsx +0 -119
- package/src/components/deprecated/FormInput.tsx +0 -71
- package/src/components/deprecated/index.ts +0 -5
package/src/hooks/useForm.ts
CHANGED
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as Schema from 'effect/Schema';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
6
7
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
8
|
|
|
8
|
-
import { type
|
|
9
|
-
import { type
|
|
9
|
+
import { type AnyProperties, getValue, setValue } from '@dxos/echo/internal';
|
|
10
|
+
import { type JsonPath, type SimpleType, createJsonPath, fromEffectValidationPath } from '@dxos/effect';
|
|
10
11
|
import { invariant } from '@dxos/invariant';
|
|
11
12
|
import { log } from '@dxos/log';
|
|
12
|
-
import {
|
|
13
|
+
import { type ValidationError, validateSchema } from '@dxos/schema';
|
|
13
14
|
import { type MaybePromise } from '@dxos/util';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Return type from `useForm` hook.
|
|
17
18
|
*/
|
|
18
|
-
export type FormHandler<T extends
|
|
19
|
+
export type FormHandler<T extends AnyProperties> = {
|
|
19
20
|
//
|
|
20
21
|
// Form state management.
|
|
21
22
|
//
|
|
@@ -26,6 +27,8 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
26
27
|
changed: Record<JsonPath, boolean>;
|
|
27
28
|
canSave: boolean;
|
|
28
29
|
formIsValid: boolean;
|
|
30
|
+
|
|
31
|
+
// TODO(burdon): Return onSave.
|
|
29
32
|
handleSave: () => void;
|
|
30
33
|
|
|
31
34
|
//
|
|
@@ -41,7 +44,7 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
41
44
|
/**
|
|
42
45
|
* Hook options.
|
|
43
46
|
*/
|
|
44
|
-
export interface FormOptions<T extends
|
|
47
|
+
export interface FormOptions<T extends AnyProperties> {
|
|
45
48
|
/**
|
|
46
49
|
* Effect schema (Type literal).
|
|
47
50
|
*/
|
|
@@ -82,7 +85,7 @@ export interface FormOptions<T extends BaseObject> {
|
|
|
82
85
|
* Creates a hook for managing form state, including values, validation, and submission.
|
|
83
86
|
* Deeply integrated with `@dxos/schema` for schema-based validation.
|
|
84
87
|
*/
|
|
85
|
-
export const useForm = <T extends
|
|
88
|
+
export const useForm = <T extends AnyProperties>({
|
|
86
89
|
schema,
|
|
87
90
|
initialValues,
|
|
88
91
|
onValuesChanged,
|
|
@@ -201,6 +204,7 @@ export const useForm = <T extends BaseObject>({
|
|
|
201
204
|
[values],
|
|
202
205
|
);
|
|
203
206
|
|
|
207
|
+
// TODO(wittjosiah): This is causing the entire form to re-render on every change.
|
|
204
208
|
const onValueChange = useCallback<FormHandler<T>['onValueChange']>(
|
|
205
209
|
(path: (string | number)[], type: SimpleType, value: any) => {
|
|
206
210
|
const jsonPath = createJsonPath(path);
|
|
@@ -216,12 +220,13 @@ export const useForm = <T extends BaseObject>({
|
|
|
216
220
|
|
|
217
221
|
const newValues = { ...setValue(values, jsonPath, parsedValue) };
|
|
218
222
|
setValues(newValues);
|
|
219
|
-
|
|
223
|
+
const newChanged = { ...changed, [jsonPath]: true };
|
|
224
|
+
setChanged(newChanged);
|
|
220
225
|
onValuesChanged?.(newValues);
|
|
221
226
|
|
|
222
227
|
const isValid = validate(newValues);
|
|
223
228
|
if (isValid && onValid) {
|
|
224
|
-
onValid(newValues, { changed });
|
|
229
|
+
onValid(newValues, { changed: newChanged });
|
|
225
230
|
}
|
|
226
231
|
},
|
|
227
232
|
[values, onValuesChanged, validate, onValid, changed],
|
|
@@ -271,7 +276,7 @@ export const useForm = <T extends BaseObject>({
|
|
|
271
276
|
);
|
|
272
277
|
};
|
|
273
278
|
|
|
274
|
-
const createKeySet = <T extends
|
|
279
|
+
const createKeySet = <T extends AnyProperties, V>(obj: T, value: V): Record<JsonPath, V> => {
|
|
275
280
|
invariant(obj);
|
|
276
281
|
return Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: value }), {} as Record<JsonPath, V>);
|
|
277
282
|
};
|
|
@@ -2,26 +2,35 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type TypeAnnotation } from '@dxos/echo
|
|
7
|
+
import { type TypeAnnotation } from '@dxos/echo/internal';
|
|
8
8
|
import { type DXN } from '@dxos/keys';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import { type
|
|
10
|
+
import { type Palette } from '@dxos/react-ui-types';
|
|
11
11
|
import { type MaybePromise } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
export type RefOption = { dxn: DXN; label?: string };
|
|
14
14
|
export type QueryRefOptions = (type: TypeAnnotation) => MaybePromise<RefOption[]>;
|
|
15
15
|
|
|
16
|
+
export type QueryTag = {
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
hue?: Palette;
|
|
20
|
+
};
|
|
21
|
+
|
|
16
22
|
type UseQueryRefOptionsProps = { refTypeInfo: TypeAnnotation | undefined; onQueryRefOptions?: QueryRefOptions };
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Hook to query reference options based on type information.
|
|
20
26
|
* Used internally within forms to fetch and format reference options for reference fields.
|
|
21
27
|
*/
|
|
28
|
+
// TODO(wittjosiah): This should be a reactive query so that the options are always up to date.
|
|
22
29
|
export const useQueryRefOptions = ({ refTypeInfo, onQueryRefOptions }: UseQueryRefOptionsProps) => {
|
|
23
|
-
const [options, setOptions] = useState<
|
|
30
|
+
const [options, setOptions] = useState<QueryTag[]>([]);
|
|
24
31
|
const [loading, setLoading] = useState(false);
|
|
32
|
+
const [state, setState] = useState({});
|
|
33
|
+
const update = useCallback(() => setState({}), []);
|
|
25
34
|
|
|
26
35
|
useEffect(() => {
|
|
27
36
|
if (!refTypeInfo || !onQueryRefOptions) {
|
|
@@ -47,7 +56,7 @@ export const useQueryRefOptions = ({ refTypeInfo, onQueryRefOptions }: UseQueryR
|
|
|
47
56
|
};
|
|
48
57
|
|
|
49
58
|
void fetchOptions();
|
|
50
|
-
}, [refTypeInfo, onQueryRefOptions]);
|
|
59
|
+
}, [refTypeInfo, onQueryRefOptions, state]);
|
|
51
60
|
|
|
52
|
-
return { options, loading };
|
|
61
|
+
return { options, update, loading };
|
|
53
62
|
};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Obj } from '@dxos/echo';
|
|
8
|
+
import { type TypeAnnotation } from '@dxos/echo/internal';
|
|
8
9
|
import { Filter, type Space } from '@dxos/react-client/echo';
|
|
9
10
|
import { isNonNullable } from '@dxos/util';
|
|
10
11
|
|
|
@@ -18,18 +19,17 @@ export const useRefQueryLookupHandler = ({ space }: UseRefQueryLookupProps): Que
|
|
|
18
19
|
if (!space) {
|
|
19
20
|
return [];
|
|
20
21
|
}
|
|
22
|
+
|
|
21
23
|
const query = space.db.query(Filter.typename(typeInfo.typename));
|
|
22
24
|
const { objects } = await query.run();
|
|
23
|
-
|
|
24
25
|
return objects
|
|
25
26
|
.map((object) => {
|
|
26
|
-
const dxn =
|
|
27
|
+
const dxn = Obj.getDXN(object);
|
|
27
28
|
if (!dxn) {
|
|
28
29
|
return undefined;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
const item = { dxn, label: object?.name ?? object?.id ?? '' };
|
|
32
|
+
const item = { dxn, label: Obj.getLabel(object) ?? object?.id ?? '' };
|
|
33
33
|
return item;
|
|
34
34
|
})
|
|
35
35
|
.filter(isNonNullable);
|
package/src/translations.ts
CHANGED
|
@@ -15,7 +15,7 @@ export const translations = [
|
|
|
15
15
|
'empty readonly ref field label': '(none)',
|
|
16
16
|
|
|
17
17
|
// TODO(burdon): Standardize field/property.
|
|
18
|
-
'
|
|
18
|
+
'add property button label': 'Add property',
|
|
19
19
|
'field property label': 'Property',
|
|
20
20
|
'field property placeholder': 'Property name',
|
|
21
21
|
'field format label': 'Type',
|
|
@@ -25,10 +25,16 @@ export const translations = [
|
|
|
25
25
|
'field limit reached': 'Maximum number of fields reached',
|
|
26
26
|
'boolean input true value': 'Yes',
|
|
27
27
|
'boolean input false value': 'No',
|
|
28
|
+
'show field label': 'Show field',
|
|
29
|
+
'hide field label': 'Hide field',
|
|
30
|
+
'delete field label': 'Delete field',
|
|
31
|
+
'ref field combobox input placeholder': 'Search…',
|
|
32
|
+
'ref field placeholder_one': 'Select…',
|
|
33
|
+
'ref field placeholder_other': 'Select items…',
|
|
28
34
|
|
|
29
35
|
// TODO(burdon): Factor out?
|
|
30
|
-
'button
|
|
31
|
-
'button
|
|
36
|
+
'cancel button label': 'Cancel',
|
|
37
|
+
'save button label': 'Save',
|
|
32
38
|
|
|
33
39
|
// FormatEnum
|
|
34
40
|
'format boolean': 'Boolean',
|
|
@@ -43,7 +49,7 @@ export const translations = [
|
|
|
43
49
|
'format hostname': 'Hostname',
|
|
44
50
|
'format integer': 'Integer',
|
|
45
51
|
'format json': 'JSON',
|
|
46
|
-
'format
|
|
52
|
+
'format lnglat': 'Geopoint',
|
|
47
53
|
'format markdown': 'Markdown',
|
|
48
54
|
'format percent': 'Percent',
|
|
49
55
|
'format ref': 'Reference',
|
|
@@ -60,9 +66,9 @@ export const translations = [
|
|
|
60
66
|
'format url': 'URL',
|
|
61
67
|
'format uuid': 'UUID',
|
|
62
68
|
|
|
63
|
-
//
|
|
64
|
-
'
|
|
65
|
-
'
|
|
69
|
+
// GeoPointInput.
|
|
70
|
+
'latitude label': 'Latitude',
|
|
71
|
+
'longitude label': 'Longitude',
|
|
66
72
|
|
|
67
73
|
// SelectOptionsInput.
|
|
68
74
|
'select option label': 'Label',
|
|
@@ -72,8 +78,9 @@ export const translations = [
|
|
|
72
78
|
'select option add': 'Add option',
|
|
73
79
|
|
|
74
80
|
// System schema message.
|
|
75
|
-
'system schema title': 'System
|
|
76
|
-
'system schema description':
|
|
81
|
+
'system schema title': 'System type',
|
|
82
|
+
'system schema description':
|
|
83
|
+
'This is a system type and cannot be modified, though the view can still be configured.',
|
|
77
84
|
},
|
|
78
85
|
},
|
|
79
86
|
},
|
package/src/util/field.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Format, GeoLocation, TypeEnum } from '@dxos/echo/internal';
|
|
6
6
|
import { type ValidationError } from '@dxos/schema';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -13,7 +13,7 @@ import { type ValidationError } from '@dxos/schema';
|
|
|
13
13
|
*/
|
|
14
14
|
export type ParseProps = {
|
|
15
15
|
type?: TypeEnum;
|
|
16
|
-
format?:
|
|
16
|
+
format?: Format.TypeFormat;
|
|
17
17
|
value: any;
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -57,31 +57,31 @@ export const parseValue = ({ type, format, value }: ParseProps) => {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
switch (format) {
|
|
60
|
-
case
|
|
60
|
+
case Format.TypeFormat.Boolean:
|
|
61
61
|
return parseScalar(TypeEnum.Boolean);
|
|
62
62
|
|
|
63
|
-
case
|
|
64
|
-
case
|
|
65
|
-
case
|
|
63
|
+
case Format.TypeFormat.Number:
|
|
64
|
+
case Format.TypeFormat.Percent:
|
|
65
|
+
case Format.TypeFormat.Currency:
|
|
66
66
|
return parseScalar(TypeEnum.Number);
|
|
67
67
|
|
|
68
|
-
case
|
|
69
|
-
case
|
|
68
|
+
case Format.TypeFormat.String:
|
|
69
|
+
case Format.TypeFormat.Markdown: {
|
|
70
70
|
return parseScalar(TypeEnum.String);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
case
|
|
74
|
-
throw new Error(`unexpected format: ${
|
|
73
|
+
case Format.TypeFormat.Ref:
|
|
74
|
+
throw new Error(`unexpected format: ${Format.TypeFormat.Ref}`);
|
|
75
75
|
|
|
76
|
-
case
|
|
77
|
-
case
|
|
78
|
-
case
|
|
79
|
-
case
|
|
76
|
+
case Format.TypeFormat.DateTime:
|
|
77
|
+
case Format.TypeFormat.Date:
|
|
78
|
+
case Format.TypeFormat.Time:
|
|
79
|
+
case Format.TypeFormat.Timestamp: {
|
|
80
80
|
const date = new Date(value as string | number);
|
|
81
81
|
return isNaN(date.getTime()) ? null : date;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
case
|
|
84
|
+
case Format.TypeFormat.GeoPoint: {
|
|
85
85
|
// Parse string in format "lat,long" to GeoPoint [longitude, latitude].
|
|
86
86
|
if (typeof value === 'string') {
|
|
87
87
|
// Only keep digits, decimal points, minus signs, and commas.
|
|
@@ -108,41 +108,31 @@ export const parseValue = ({ type, format, value }: ParseProps) => {
|
|
|
108
108
|
// TODO(burdon): Type and format.
|
|
109
109
|
export type CellClassesForFieldTypeProps = {
|
|
110
110
|
type?: TypeEnum;
|
|
111
|
-
format?:
|
|
111
|
+
format?: Format.TypeFormat;
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
export const cellClassesForFieldType = ({ type, format }: CellClassesForFieldTypeProps): string[] | undefined => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
case TypeEnum.Number:
|
|
118
|
-
return ['text-right', 'font-mono'];
|
|
119
|
-
case TypeEnum.Boolean:
|
|
120
|
-
return ['text-right', 'font-mono'];
|
|
121
|
-
case TypeEnum.String:
|
|
122
|
-
return undefined;
|
|
123
|
-
case TypeEnum.Ref:
|
|
124
|
-
return undefined;
|
|
125
|
-
|
|
126
|
-
default: {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
switch (format) {
|
|
133
|
-
case FormatEnum.Markdown:
|
|
115
|
+
switch (format || type) {
|
|
116
|
+
case Format.TypeFormat.Markdown:
|
|
134
117
|
return undefined;
|
|
135
|
-
case
|
|
136
|
-
case
|
|
137
|
-
case
|
|
138
|
-
case
|
|
139
|
-
case
|
|
118
|
+
case Format.TypeFormat.Time:
|
|
119
|
+
case Format.TypeFormat.Timestamp:
|
|
120
|
+
case Format.TypeFormat.DateTime:
|
|
121
|
+
case Format.TypeFormat.Date:
|
|
122
|
+
case Format.TypeFormat.Duration:
|
|
140
123
|
return ['font-mono', 'text-right'];
|
|
141
|
-
case
|
|
124
|
+
case Format.TypeFormat.Currency:
|
|
142
125
|
return ['text-right'];
|
|
143
|
-
case
|
|
144
|
-
case
|
|
126
|
+
case Format.TypeFormat.JSON:
|
|
127
|
+
case Format.TypeFormat.DID:
|
|
145
128
|
return ['font-mono'];
|
|
129
|
+
case TypeEnum.Number:
|
|
130
|
+
case TypeEnum.Boolean:
|
|
131
|
+
return ['text-right', 'font-mono'];
|
|
132
|
+
case TypeEnum.String:
|
|
133
|
+
return undefined;
|
|
134
|
+
case TypeEnum.Ref:
|
|
135
|
+
return undefined;
|
|
146
136
|
default:
|
|
147
137
|
return undefined;
|
|
148
138
|
}
|
package/src/util/formatting.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { format as formatDate } from 'date-fns/format';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Format, GeoLocation, TypeEnum } from '@dxos/echo/internal';
|
|
8
8
|
|
|
9
9
|
type ValueFormatProps = {
|
|
10
10
|
type: TypeEnum;
|
|
11
|
-
format?:
|
|
11
|
+
format?: Format.TypeFormat | undefined;
|
|
12
12
|
value: any;
|
|
13
13
|
locale?: string | undefined;
|
|
14
14
|
};
|
|
@@ -41,20 +41,20 @@ export const formatForDisplay = ({ type, format, value, locale = undefined }: Va
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
switch (format) {
|
|
44
|
-
case
|
|
44
|
+
case Format.TypeFormat.Boolean: {
|
|
45
45
|
return formatScalar(TypeEnum.Boolean);
|
|
46
46
|
}
|
|
47
|
-
case
|
|
47
|
+
case Format.TypeFormat.Number: {
|
|
48
48
|
return formatScalar(TypeEnum.Number);
|
|
49
49
|
}
|
|
50
|
-
case
|
|
51
|
-
case
|
|
50
|
+
case Format.TypeFormat.String:
|
|
51
|
+
case Format.TypeFormat.Ref: {
|
|
52
52
|
return formatScalar(TypeEnum.String);
|
|
53
53
|
}
|
|
54
|
-
case
|
|
54
|
+
case Format.TypeFormat.Percent: {
|
|
55
55
|
return `${(value as number) * 100}%`;
|
|
56
56
|
}
|
|
57
|
-
case
|
|
57
|
+
case Format.TypeFormat.Currency: {
|
|
58
58
|
// TODO(burdon): Get from property annotation.
|
|
59
59
|
return (value as number).toLocaleString(locale, {
|
|
60
60
|
style: 'currency',
|
|
@@ -63,28 +63,28 @@ export const formatForDisplay = ({ type, format, value, locale = undefined }: Va
|
|
|
63
63
|
maximumFractionDigits: 2,
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
-
case
|
|
66
|
+
case Format.TypeFormat.Date: {
|
|
67
67
|
try {
|
|
68
68
|
return formatDate(new Date(value as number), 'yyyy-MM-dd');
|
|
69
69
|
} catch (error) {
|
|
70
70
|
return 'Invalid Date';
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
case
|
|
73
|
+
case Format.TypeFormat.Time: {
|
|
74
74
|
try {
|
|
75
75
|
return formatDate(new Date(value as number), 'HH:mm:ss');
|
|
76
76
|
} catch (error) {
|
|
77
77
|
return 'Invalid Time';
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
case
|
|
80
|
+
case Format.TypeFormat.DateTime: {
|
|
81
81
|
try {
|
|
82
82
|
return formatDate(new Date(value as number), 'yyyy-MM-dd HH:mm:ss');
|
|
83
83
|
} catch (error) {
|
|
84
84
|
return 'Invalid DateTime';
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
case
|
|
87
|
+
case Format.TypeFormat.GeoPoint: {
|
|
88
88
|
if (value === null || value === undefined) {
|
|
89
89
|
return '';
|
|
90
90
|
}
|
|
@@ -125,31 +125,31 @@ export const formatForEditing = ({ type, format, value, locale = undefined }: Va
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
switch (format) {
|
|
128
|
-
case
|
|
129
|
-
case
|
|
130
|
-
case
|
|
131
|
-
case
|
|
128
|
+
case Format.TypeFormat.Boolean:
|
|
129
|
+
case Format.TypeFormat.Number:
|
|
130
|
+
case Format.TypeFormat.String:
|
|
131
|
+
case Format.TypeFormat.Ref: {
|
|
132
132
|
return formatScalar(type);
|
|
133
133
|
}
|
|
134
|
-
case
|
|
134
|
+
case Format.TypeFormat.Percent: {
|
|
135
135
|
return String(value * 100);
|
|
136
136
|
}
|
|
137
|
-
case
|
|
137
|
+
case Format.TypeFormat.Currency: {
|
|
138
138
|
return String(value);
|
|
139
139
|
}
|
|
140
|
-
case
|
|
140
|
+
case Format.TypeFormat.DateTime: {
|
|
141
141
|
const date = new Date(value as number);
|
|
142
142
|
return date.toISOString();
|
|
143
143
|
}
|
|
144
|
-
case
|
|
144
|
+
case Format.TypeFormat.Date: {
|
|
145
145
|
const date = new Date(value as number);
|
|
146
146
|
return date.toISOString().split('T')[0];
|
|
147
147
|
}
|
|
148
|
-
case
|
|
148
|
+
case Format.TypeFormat.Time: {
|
|
149
149
|
const date = new Date(value as number);
|
|
150
150
|
return date.toISOString().split('T')[1].split('.')[0];
|
|
151
151
|
}
|
|
152
|
-
case
|
|
152
|
+
case Format.TypeFormat.GeoPoint: {
|
|
153
153
|
// Handle null or undefined
|
|
154
154
|
if (value === null || value === undefined) {
|
|
155
155
|
return '';
|
package/src/util/refs.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as SchemaAST from 'effect/SchemaAST';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Format, ReferenceAnnotationId } from '@dxos/echo/internal';
|
|
8
8
|
import { findAnnotation } from '@dxos/effect';
|
|
9
9
|
import { type SchemaProperty } from '@dxos/schema';
|
|
10
10
|
|
|
@@ -18,11 +18,6 @@ type RefProps = {
|
|
|
18
18
|
export const getRefProps = (property: SchemaProperty<any>): RefProps | undefined => {
|
|
19
19
|
const { ast, format, array } = property;
|
|
20
20
|
|
|
21
|
-
// Direct reference.
|
|
22
|
-
if (format === FormatEnum.Ref) {
|
|
23
|
-
return { ast, isArray: false };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
21
|
// Array of references.
|
|
27
22
|
if (array) {
|
|
28
23
|
const elementType = findArrayElementType(ast);
|
|
@@ -34,5 +29,10 @@ export const getRefProps = (property: SchemaProperty<any>): RefProps | undefined
|
|
|
34
29
|
}
|
|
35
30
|
}
|
|
36
31
|
|
|
32
|
+
// Direct reference.
|
|
33
|
+
if (format === Format.TypeFormat.Ref) {
|
|
34
|
+
return { ast, isArray: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
37
|
return undefined;
|
|
38
38
|
};
|
package/src/util/schema.ts
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
6
6
|
|
|
7
7
|
import { findNode } from '@dxos/effect';
|
|
8
8
|
|
|
9
9
|
export const findArrayElementType = (ast: SchemaAST.AST) => {
|
|
10
10
|
const tupleType = findNode(ast, SchemaAST.isTupleType);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return elementType;
|
|
11
|
+
return (tupleType as SchemaAST.TupleType | undefined)?.rest[0]?.type;
|
|
14
12
|
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type Schema } from 'effect';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
4
|
-
import { type Projection } from '@dxos/schema';
|
|
5
|
-
export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
|
|
6
|
-
projection: Projection;
|
|
7
|
-
object: T;
|
|
8
|
-
schema?: Schema.Schema<T>;
|
|
9
|
-
readonly?: boolean;
|
|
10
|
-
}>;
|
|
11
|
-
/**
|
|
12
|
-
* Schema-based object form.
|
|
13
|
-
*/
|
|
14
|
-
export declare const DeprecatedForm: <T extends {} = {}>({ classNames, projection, object, schema, readonly, }: DeprecatedFormProps<T>) => React.JSX.Element;
|
|
15
|
-
//# sourceMappingURL=Form.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,EAAS,KAAK,eAAe,EAA+C,MAAM,gBAAgB,CAAC;AAE1G,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,eAAe,CAAC;IACnE,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,uDAM/C,mBAAmB,CAAC,CAAC,CAAC,sBA0DxB,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import '@dxos-theme';
|
|
2
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { type TestType } from '@dxos/schema/testing';
|
|
5
|
-
import { type DeprecatedFormProps } from './Form';
|
|
6
|
-
declare const DefaultStory: (props: DeprecatedFormProps) => React.JSX.Element;
|
|
7
|
-
declare const meta: Meta<typeof DefaultStory>;
|
|
8
|
-
export default meta;
|
|
9
|
-
type Story = StoryObj<DeprecatedFormProps<TestType>>;
|
|
10
|
-
export declare const Default: Story;
|
|
11
|
-
export declare const Empty: Story;
|
|
12
|
-
export declare const Readonly: Story;
|
|
13
|
-
//# sourceMappingURL=Form.stories.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAIlE,QAAA,MAAM,YAAY,GAAI,OAAO,mBAAmB,sBAM/C,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAQnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAErD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type JSX, type PropsWithChildren } from 'react';
|
|
2
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
/**
|
|
4
|
-
* @deprecated
|
|
5
|
-
*/
|
|
6
|
-
export declare const DeprecatedFormContainer: ({ children, classNames }: ThemedClassName<PropsWithChildren>) => JSX.Element;
|
|
7
|
-
export type DeprecatedFormInputProps = {
|
|
8
|
-
label: string;
|
|
9
|
-
description?: JSX.Element;
|
|
10
|
-
secondary?: JSX.Element;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated
|
|
14
|
-
*/
|
|
15
|
-
export declare const DeprecatedFormInput: ({ label, description, secondary, children, }: PropsWithChildren<DeprecatedFormInputProps>) => JSX.Element;
|
|
16
|
-
//# sourceMappingURL=FormInput.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/FormInput.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,GAAG,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG7D;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,gBAQnG,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,8CAKjC,iBAAiB,CAAC,wBAAwB,CAAC,gBAkC7C,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import React from 'react';
|
|
9
|
-
|
|
10
|
-
import { live } from '@dxos/live-object';
|
|
11
|
-
import { testData, TestSchema, testView, type TestType } from '@dxos/schema/testing';
|
|
12
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
|
-
|
|
14
|
-
import { DeprecatedForm, type DeprecatedFormProps } from './Form';
|
|
15
|
-
import { translations } from '../../translations';
|
|
16
|
-
import { TestLayout, TestPanel } from '../testing';
|
|
17
|
-
|
|
18
|
-
const DefaultStory = (props: DeprecatedFormProps) => (
|
|
19
|
-
<TestLayout json={{ props }}>
|
|
20
|
-
<TestPanel>
|
|
21
|
-
<DeprecatedForm {...props} />
|
|
22
|
-
</TestPanel>
|
|
23
|
-
</TestLayout>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const meta: Meta<typeof DefaultStory> = {
|
|
27
|
-
title: 'ui/react-ui-form/DeprecatedForm',
|
|
28
|
-
component: DeprecatedForm,
|
|
29
|
-
render: DefaultStory,
|
|
30
|
-
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
31
|
-
parameters: {
|
|
32
|
-
translations,
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default meta;
|
|
37
|
-
|
|
38
|
-
type Story = StoryObj<DeprecatedFormProps<TestType>>;
|
|
39
|
-
|
|
40
|
-
export const Default: Story = {
|
|
41
|
-
args: {
|
|
42
|
-
object: live(testData),
|
|
43
|
-
projection: live(testView).projection,
|
|
44
|
-
schema: TestSchema,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const Empty: Story = {
|
|
49
|
-
args: {
|
|
50
|
-
projection: live(testView).projection,
|
|
51
|
-
schema: TestSchema,
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const Readonly: Story = {
|
|
56
|
-
args: {
|
|
57
|
-
object: live(testData),
|
|
58
|
-
projection: live(testView).projection,
|
|
59
|
-
schema: TestSchema,
|
|
60
|
-
readonly: true,
|
|
61
|
-
},
|
|
62
|
-
};
|