@dxos/react-ui-form 0.8.2-main.f11618f → 0.8.2-main.fbd8ed0
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 +731 -573
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +748 -598
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +731 -573
- 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.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +4 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +33 -32
- 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 +3 -3
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +7 -18
- package/dist/types/src/components/Form/RefField.d.ts.map +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.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
- 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/Form.d.ts +2 -2
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +2 -2
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +23 -0
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -0
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +8 -0
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -0
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/refs.d.ts +9 -0
- package/dist/types/src/util/refs.d.ts.map +1 -0
- package/dist/types/src/util/schema.d.ts +3 -0
- package/dist/types/src/util/schema.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +26 -25
- package/src/components/ControlSection/ControlSection.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/Form/ArrayField.tsx +10 -16
- package/src/components/Form/Defaults.tsx +4 -1
- package/src/components/Form/Form.stories.tsx +48 -42
- package/src/components/Form/Form.tsx +4 -4
- package/src/components/Form/FormContent.tsx +49 -22
- package/src/components/Form/FormContext.tsx +3 -1
- package/src/components/Form/Input.tsx +3 -9
- package/src/components/Form/RefField.tsx +147 -101
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +6 -5
- package/src/components/Form/factory.tsx +1 -1
- package/src/components/Form/index.ts +0 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +5 -4
- package/src/components/ViewEditor/ViewEditor.tsx +23 -20
- package/src/components/deprecated/Form.tsx +6 -6
- package/src/hooks/index.ts +3 -1
- package/src/hooks/useForm.ts +4 -3
- package/src/hooks/useQueryRefOptions.ts +53 -0
- package/src/hooks/useRefQueryLookupHandler.ts +41 -0
- package/src/util/index.ts +3 -1
- package/src/util/refs.ts +38 -0
- package/src/util/schema.ts +14 -0
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { pipe } from 'effect';
|
|
5
|
+
import { Schema, SchemaAST, pipe } from 'effect';
|
|
6
6
|
import { capitalize } from 'effect/String';
|
|
7
7
|
import React, { useMemo } from 'react';
|
|
8
8
|
|
|
9
|
-
import { AST, S } from '@dxos/echo-schema';
|
|
10
9
|
import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
|
|
11
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
12
11
|
import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
|
|
@@ -17,8 +16,10 @@ import { SelectInput } from './Defaults';
|
|
|
17
16
|
import { type ComponentLookup } from './Form';
|
|
18
17
|
import { useInputProps, useFormValues } from './FormContext';
|
|
19
18
|
import { type InputComponent } from './Input';
|
|
20
|
-
import { RefField
|
|
19
|
+
import { RefField } from './RefField';
|
|
21
20
|
import { getInputComponent } from './factory';
|
|
21
|
+
import { type QueryRefOptions } from '../../hooks';
|
|
22
|
+
import { getRefProps } from '../../util';
|
|
22
23
|
|
|
23
24
|
export type FormFieldProps = {
|
|
24
25
|
property: SchemaProperty<any>;
|
|
@@ -49,9 +50,13 @@ export const FormField = ({
|
|
|
49
50
|
[examples, description],
|
|
50
51
|
);
|
|
51
52
|
|
|
53
|
+
//
|
|
54
|
+
// Registry and Custom.
|
|
55
|
+
//
|
|
56
|
+
|
|
52
57
|
const FoundComponent = lookupComponent?.({
|
|
53
58
|
prop: name,
|
|
54
|
-
schema:
|
|
59
|
+
schema: Schema.make(ast),
|
|
55
60
|
inputProps: {
|
|
56
61
|
type,
|
|
57
62
|
format,
|
|
@@ -82,10 +87,40 @@ export const FormField = ({
|
|
|
82
87
|
);
|
|
83
88
|
}
|
|
84
89
|
|
|
90
|
+
//
|
|
91
|
+
// Refs.
|
|
92
|
+
//
|
|
93
|
+
|
|
94
|
+
const refProps = getRefProps(property);
|
|
95
|
+
if (refProps) {
|
|
96
|
+
return (
|
|
97
|
+
<RefField
|
|
98
|
+
ast={refProps.ast}
|
|
99
|
+
array={refProps.isArray}
|
|
100
|
+
type={type}
|
|
101
|
+
format={format}
|
|
102
|
+
label={label}
|
|
103
|
+
placeholder={placeholder}
|
|
104
|
+
disabled={readonly}
|
|
105
|
+
inputOnly={inline}
|
|
106
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
107
|
+
{...inputProps}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//
|
|
113
|
+
// Arrays.
|
|
114
|
+
//
|
|
115
|
+
|
|
85
116
|
if (array) {
|
|
86
117
|
return <ArrayField property={property} path={path} inputProps={inputProps} readonly={readonly} Custom={Custom} />;
|
|
87
118
|
}
|
|
88
119
|
|
|
120
|
+
//
|
|
121
|
+
// Standard Inputs.
|
|
122
|
+
//
|
|
123
|
+
|
|
89
124
|
const InputComponent = getInputComponent(type, format);
|
|
90
125
|
if (InputComponent) {
|
|
91
126
|
return (
|
|
@@ -103,6 +138,10 @@ export const FormField = ({
|
|
|
103
138
|
);
|
|
104
139
|
}
|
|
105
140
|
|
|
141
|
+
//
|
|
142
|
+
// Select.
|
|
143
|
+
//
|
|
144
|
+
|
|
106
145
|
if (options) {
|
|
107
146
|
return (
|
|
108
147
|
<div role='none'>
|
|
@@ -120,34 +159,22 @@ export const FormField = ({
|
|
|
120
159
|
);
|
|
121
160
|
}
|
|
122
161
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<RefField
|
|
127
|
-
ast={ast}
|
|
128
|
-
type={type}
|
|
129
|
-
label={label}
|
|
130
|
-
readonly={readonly}
|
|
131
|
-
placeholder={placeholder}
|
|
132
|
-
inline={inline}
|
|
133
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
134
|
-
inputProps={inputProps}
|
|
135
|
-
/>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
162
|
+
//
|
|
163
|
+
// Nested Objects.
|
|
164
|
+
//
|
|
138
165
|
|
|
139
166
|
if (type === 'object') {
|
|
140
167
|
const baseNode = findNode(ast, isDiscriminatedUnion);
|
|
141
168
|
const typeLiteral = baseNode
|
|
142
169
|
? getDiscriminatedType(baseNode, inputProps.getValue() as any)
|
|
143
|
-
: findNode(ast,
|
|
170
|
+
: findNode(ast, SchemaAST.isTypeLiteral);
|
|
144
171
|
|
|
145
172
|
if (typeLiteral) {
|
|
146
173
|
return (
|
|
147
174
|
<div role='none'>
|
|
148
175
|
{!inline && <h3 className='text-lg mbs-2 mbe-1'>{label}</h3>}
|
|
149
176
|
<FormFields
|
|
150
|
-
schema={
|
|
177
|
+
schema={Schema.make(typeLiteral)}
|
|
151
178
|
path={path}
|
|
152
179
|
readonly={readonly}
|
|
153
180
|
onQueryRefOptions={onQueryRefOptions}
|
|
@@ -163,7 +190,7 @@ export const FormField = ({
|
|
|
163
190
|
};
|
|
164
191
|
|
|
165
192
|
export type FormContentProps = {
|
|
166
|
-
schema:
|
|
193
|
+
schema: Schema.Schema.All;
|
|
167
194
|
path?: (string | number)[];
|
|
168
195
|
filter?: (props: SchemaProperty<any>[]) => SchemaProperty<any>[];
|
|
169
196
|
sort?: string[];
|
|
@@ -76,7 +76,9 @@ export const FormProvider = ({
|
|
|
76
76
|
const keyIsEnter = event.key === 'Enter';
|
|
77
77
|
const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
|
|
78
78
|
const inputIsTextarea = (event.target as HTMLElement).tagName.toLowerCase() === 'textarea';
|
|
79
|
-
const inputOptOut =
|
|
79
|
+
const inputOptOut =
|
|
80
|
+
(event.target as HTMLElement).hasAttribute('data-no-submit') ||
|
|
81
|
+
(event.target as HTMLElement).closest('[data-no-submit]') !== null;
|
|
80
82
|
|
|
81
83
|
// Regular inputs: Submit on Enter (no modifiers).
|
|
82
84
|
const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
|
|
@@ -37,15 +37,9 @@ export const InputHeader = ({ children, error }: InputHeaderProps) => {
|
|
|
37
37
|
<div role='none' className='flex justify-between items-center mb-1'>
|
|
38
38
|
{children}
|
|
39
39
|
{error && (
|
|
40
|
-
<Tooltip.
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
</Tooltip.Trigger>
|
|
44
|
-
<Tooltip.Content side='bottom'>
|
|
45
|
-
<Tooltip.Arrow />
|
|
46
|
-
{error}
|
|
47
|
-
</Tooltip.Content>
|
|
48
|
-
</Tooltip.Root>
|
|
40
|
+
<Tooltip.Trigger asChild content={error} side='bottom'>
|
|
41
|
+
<Icon icon='ph--warning--regular' size={4} classNames={errorText} />
|
|
42
|
+
</Tooltip.Trigger>
|
|
49
43
|
)}
|
|
50
44
|
</div>
|
|
51
45
|
);
|
|
@@ -2,149 +2,195 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { type SchemaAST } from 'effect';
|
|
6
|
+
import React, { useCallback, useMemo, type FocusEvent } from 'react';
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
|
-
type AST,
|
|
9
9
|
Expando,
|
|
10
10
|
getTypeAnnotation,
|
|
11
|
+
Ref,
|
|
11
12
|
ReferenceAnnotationId,
|
|
12
13
|
type ReferenceAnnotationValue,
|
|
13
|
-
type TypeAnnotation,
|
|
14
14
|
} from '@dxos/echo-schema';
|
|
15
|
-
import { findAnnotation
|
|
15
|
+
import { findAnnotation } from '@dxos/effect';
|
|
16
16
|
import { DXN } from '@dxos/keys';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
import { type
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
type: SimpleType;
|
|
29
|
-
label: string;
|
|
30
|
-
readonly?: boolean;
|
|
31
|
-
placeholder?: string;
|
|
32
|
-
inline?: boolean;
|
|
17
|
+
import { Input } from '@dxos/react-ui';
|
|
18
|
+
import { TagPicker, type TagPickerMode, type TagPickerItemData } from '@dxos/react-ui-tag-picker';
|
|
19
|
+
import { isNonNullable } from '@dxos/util';
|
|
20
|
+
|
|
21
|
+
import { TextInput } from './Defaults';
|
|
22
|
+
import { InputHeader, type InputProps } from './Input';
|
|
23
|
+
import { type QueryRefOptions, useQueryRefOptions } from '../../hooks';
|
|
24
|
+
|
|
25
|
+
type RefFieldProps = InputProps & {
|
|
26
|
+
ast?: SchemaAST.AST;
|
|
27
|
+
array?: boolean;
|
|
33
28
|
onQueryRefOptions?: QueryRefOptions;
|
|
34
|
-
inputProps: FormInputStateProps;
|
|
35
29
|
};
|
|
36
30
|
|
|
37
31
|
export const RefField = ({
|
|
38
|
-
ast,
|
|
39
32
|
type,
|
|
40
33
|
label,
|
|
41
|
-
|
|
34
|
+
disabled,
|
|
42
35
|
placeholder,
|
|
43
|
-
|
|
36
|
+
inputOnly,
|
|
37
|
+
array,
|
|
38
|
+
ast,
|
|
39
|
+
getValue,
|
|
40
|
+
onBlur,
|
|
44
41
|
onQueryRefOptions,
|
|
45
|
-
|
|
42
|
+
onValueChange,
|
|
43
|
+
...restInputProps
|
|
46
44
|
}: RefFieldProps) => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
const refTypeInfo = useMemo(
|
|
46
|
+
() => (ast ? findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId) : undefined),
|
|
47
|
+
[ast],
|
|
48
|
+
);
|
|
49
|
+
const { options: availableOptions, loading: _loading } = useQueryRefOptions({ refTypeInfo, onQueryRefOptions });
|
|
50
|
+
|
|
51
|
+
if ((refTypeInfo && refTypeInfo?.typename === getTypeAnnotation(Expando)?.typename) || !onQueryRefOptions) {
|
|
52
|
+
// If ref type is expando, fall back to taking a DXN in string format.
|
|
53
|
+
return (
|
|
54
|
+
<RefFieldFallback
|
|
55
|
+
{...{ type, label, placeholder, disabled, inputOnly, getValue, onBlur, onValueChange, ...restInputProps }}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const handleGetValue = useCallback((): TagPickerItemData[] => {
|
|
61
|
+
const formValue = getValue();
|
|
56
62
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
value: option.dxn.toString(),
|
|
65
|
-
})),
|
|
66
|
-
);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
log.error('Failed to fetch ref options:', error);
|
|
69
|
-
setRefOptions([]);
|
|
70
|
-
} finally {
|
|
71
|
-
setLoading(false);
|
|
63
|
+
const unknownToRefOption = (val: unknown) => {
|
|
64
|
+
if (Ref.isRef(val)) {
|
|
65
|
+
const dxnString = val.dxn.toString();
|
|
66
|
+
const matchingOption = availableOptions.find((option) => option.id === dxnString);
|
|
67
|
+
if (matchingOption) {
|
|
68
|
+
return matchingOption;
|
|
69
|
+
}
|
|
72
70
|
}
|
|
71
|
+
return undefined;
|
|
73
72
|
};
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if (array && Array.isArray(formValue)) {
|
|
75
|
+
return formValue.map(unknownToRefOption).filter(isNonNullable) ?? [];
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const option = unknownToRefOption(formValue);
|
|
79
|
+
if (option) {
|
|
80
|
+
return [option];
|
|
81
|
+
}
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const handleOnValueChange = (_type: any, dxnString: string) => {
|
|
85
|
-
const dxn = DXN.tryParse(dxnString);
|
|
86
|
-
if (dxn) {
|
|
87
|
-
onValueChange?.('object', refFromDXN(dxn));
|
|
88
|
-
} else if (dxnString === '') {
|
|
89
|
-
onValueChange?.('object', undefined);
|
|
90
|
-
} else {
|
|
91
|
-
onValueChange?.('string', dxnString);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
83
|
+
return [];
|
|
84
|
+
}, [getValue, availableOptions, array]);
|
|
94
85
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
86
|
+
const handleSearch = useCallback(
|
|
87
|
+
(text: string, ids: string[]): TagPickerItemData[] =>
|
|
88
|
+
availableOptions
|
|
89
|
+
.filter((option) => !ids.includes(option.id))
|
|
90
|
+
.filter((option) => option.label.toLowerCase().includes(text.toLowerCase()))
|
|
91
|
+
.map((option) => ({ id: option.id, label: option.label, hue: option.hue as any })),
|
|
92
|
+
[availableOptions],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const handleUpdate = useCallback(
|
|
96
|
+
(ids: string[]) => {
|
|
97
|
+
if (ids.length === 0) {
|
|
98
|
+
onValueChange('object', undefined);
|
|
99
|
+
return;
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
|
|
102
|
+
const refs = ids
|
|
103
|
+
.map((id) => {
|
|
104
|
+
const item = availableOptions.find((option) => option.id === id);
|
|
105
|
+
if (item) {
|
|
106
|
+
const dxn = DXN.parse(item.id);
|
|
107
|
+
return Ref.fromDXN(dxn);
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
})
|
|
111
|
+
.filter(isNonNullable);
|
|
112
|
+
|
|
113
|
+
if (array) {
|
|
114
|
+
onValueChange('object', refs);
|
|
115
|
+
} else {
|
|
116
|
+
onValueChange('object', refs[0]);
|
|
102
117
|
}
|
|
118
|
+
},
|
|
119
|
+
[availableOptions, array, onValueChange],
|
|
120
|
+
);
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
};
|
|
122
|
+
const tagPickerMode: TagPickerMode = useMemo(() => (array ? 'multi-select' : 'single-select'), [array]);
|
|
106
123
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
type={type}
|
|
110
|
-
label={label}
|
|
111
|
-
disabled={readonly}
|
|
112
|
-
placeholder={placeholder}
|
|
113
|
-
inputOnly={inline}
|
|
114
|
-
getValue={handleGetValue as <V>() => V | undefined}
|
|
115
|
-
onValueChange={handleOnValueChange}
|
|
116
|
-
{...restInputProps}
|
|
117
|
-
/>
|
|
118
|
-
);
|
|
124
|
+
if (!refTypeInfo) {
|
|
125
|
+
return null;
|
|
119
126
|
}
|
|
120
127
|
|
|
128
|
+
const { status, error } = restInputProps.getStatus();
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Input.Root validationValence={status}>
|
|
132
|
+
{!inputOnly && (
|
|
133
|
+
<InputHeader error={error}>
|
|
134
|
+
<Input.Label>{label}</Input.Label>
|
|
135
|
+
</InputHeader>
|
|
136
|
+
)}
|
|
137
|
+
<div data-no-submit>
|
|
138
|
+
<TagPicker
|
|
139
|
+
items={handleGetValue()}
|
|
140
|
+
mode={tagPickerMode}
|
|
141
|
+
onBlur={(event) => onBlur(event as unknown as FocusEvent<HTMLElement>)}
|
|
142
|
+
onUpdate={handleUpdate}
|
|
143
|
+
onSearch={handleSearch}
|
|
144
|
+
classNames='rounded-sm bg-input p-1.5'
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
{inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
|
|
148
|
+
</Input.Root>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const RefFieldFallback = ({
|
|
153
|
+
type,
|
|
154
|
+
label,
|
|
155
|
+
disabled,
|
|
156
|
+
placeholder,
|
|
157
|
+
inputOnly,
|
|
158
|
+
getValue,
|
|
159
|
+
onValueChange,
|
|
160
|
+
...restInputProps
|
|
161
|
+
}: InputProps) => {
|
|
162
|
+
const handleOnValueChange = (_type: any, dxnString: string) => {
|
|
163
|
+
const dxn = DXN.tryParse(dxnString);
|
|
164
|
+
if (dxn) {
|
|
165
|
+
onValueChange?.('object', Ref.fromDXN(dxn));
|
|
166
|
+
} else if (dxnString === '') {
|
|
167
|
+
onValueChange?.('object', undefined);
|
|
168
|
+
} else {
|
|
169
|
+
onValueChange?.('string', dxnString);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
121
173
|
const handleGetValue = () => {
|
|
122
174
|
const formValue = getValue();
|
|
123
|
-
|
|
124
|
-
|
|
175
|
+
if (typeof formValue === 'string') {
|
|
176
|
+
return formValue;
|
|
177
|
+
}
|
|
178
|
+
if (Ref.isRef(formValue)) {
|
|
125
179
|
return formValue.dxn.toString();
|
|
126
180
|
}
|
|
127
181
|
|
|
128
182
|
return undefined;
|
|
129
183
|
};
|
|
130
184
|
|
|
131
|
-
const handleValueChanged = (_type: any, dxnString: string) => {
|
|
132
|
-
const dxn = DXN.parse(dxnString);
|
|
133
|
-
const ref = refFromDXN(dxn);
|
|
134
|
-
|
|
135
|
-
onValueChange('object', ref);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
185
|
return (
|
|
139
|
-
<
|
|
186
|
+
<TextInput
|
|
140
187
|
type={type}
|
|
141
188
|
label={label}
|
|
142
|
-
disabled={
|
|
143
|
-
placeholder={
|
|
144
|
-
inputOnly={
|
|
189
|
+
disabled={disabled}
|
|
190
|
+
placeholder={placeholder}
|
|
191
|
+
inputOnly={inputOnly}
|
|
145
192
|
getValue={handleGetValue as <V>() => V | undefined}
|
|
146
|
-
onValueChange={
|
|
147
|
-
options={refOptions}
|
|
193
|
+
onValueChange={handleOnValueChange}
|
|
148
194
|
{...restInputProps}
|
|
149
195
|
/>
|
|
150
196
|
);
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import { Schema } from 'effect';
|
|
8
9
|
import React, { useState } from 'react';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
|
|
11
12
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
12
13
|
|
|
13
14
|
import { SelectOptionInput } from './SelectOptionsInput';
|
|
@@ -15,9 +16,9 @@ import translations from '../../../translations';
|
|
|
15
16
|
import { TestLayout, TestPanel } from '../../testing';
|
|
16
17
|
import { Form } from '../Form';
|
|
17
18
|
|
|
18
|
-
const schema =
|
|
19
|
-
options:
|
|
20
|
-
}).pipe(
|
|
19
|
+
const schema = Schema.Struct({
|
|
20
|
+
options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
|
|
21
|
+
}).pipe(Schema.mutable);
|
|
21
22
|
|
|
22
23
|
const DefaultStory = () => {
|
|
23
24
|
const [values, setValues] = useState<{ options: SelectOption[] }>({
|
|
@@ -46,7 +47,7 @@ const DefaultStory = () => {
|
|
|
46
47
|
const meta: Meta = {
|
|
47
48
|
title: 'ui/react-ui-form/SelectOptionsInput',
|
|
48
49
|
component: SelectOptionInput,
|
|
49
|
-
decorators: [withTheme, withLayout(
|
|
50
|
+
decorators: [withTheme, withLayout()],
|
|
50
51
|
parameters: { translations },
|
|
51
52
|
};
|
|
52
53
|
|
|
@@ -12,7 +12,7 @@ import { GeoPointInput } from './custom';
|
|
|
12
12
|
/**
|
|
13
13
|
* Get property input component.
|
|
14
14
|
*/
|
|
15
|
-
export const getInputComponent = (type: SimpleType, format?: FormatEnum): InputComponent | undefined => {
|
|
15
|
+
export const getInputComponent = (type: SimpleType, format?: FormatEnum | string): InputComponent | undefined => {
|
|
16
16
|
switch (format) {
|
|
17
17
|
case FormatEnum.GeoPoint:
|
|
18
18
|
return GeoPointInput;
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import { Schema } from 'effect';
|
|
8
9
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
10
|
|
|
10
|
-
import { Format, type EchoSchema,
|
|
11
|
+
import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
|
|
11
12
|
import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
12
13
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
13
14
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
@@ -26,7 +27,7 @@ const DefaultStory = () => {
|
|
|
26
27
|
useAsyncEffect(async () => {
|
|
27
28
|
if (space) {
|
|
28
29
|
class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
29
|
-
name:
|
|
30
|
+
name: Schema.String,
|
|
30
31
|
email: Format.Email,
|
|
31
32
|
salary: Format.Currency(),
|
|
32
33
|
}) {}
|
|
@@ -41,7 +42,7 @@ const DefaultStory = () => {
|
|
|
41
42
|
}
|
|
42
43
|
}, [space]);
|
|
43
44
|
|
|
44
|
-
const views = useQuery(space, Filter.
|
|
45
|
+
const views = useQuery(space, Filter.type(ViewType));
|
|
45
46
|
const currentTypename = useMemo(() => view?.query?.typename, [view]);
|
|
46
47
|
const updateViewTypename = useCallback(
|
|
47
48
|
(newTypename: string) => {
|
|
@@ -81,7 +82,7 @@ const meta: Meta<typeof ViewEditor> = {
|
|
|
81
82
|
title: 'ui/react-ui-form/ViewEditor',
|
|
82
83
|
component: ViewEditor,
|
|
83
84
|
render: DefaultStory,
|
|
84
|
-
decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true
|
|
85
|
+
decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true }), withTheme],
|
|
85
86
|
parameters: {
|
|
86
87
|
translations,
|
|
87
88
|
},
|