@dxos/react-ui-form 0.8.4-main.5ea62a8 → 0.8.4-main.66e292d
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 +534 -348
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +534 -348
- 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.stories.d.ts +8 -7
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +8 -7
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +6 -5
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +52 -37
- 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 +5 -5
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +4 -4
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +2 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +10 -6
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +8 -7
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.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/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 +13 -11
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +5 -5
- 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 +7 -6
- 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 +40 -37
- package/src/components/ControlSection/ControlSection.stories.tsx +4 -5
- package/src/components/ControlSection/ControlSection.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.stories.tsx +8 -7
- package/src/components/FieldEditor/FieldEditor.tsx +7 -4
- package/src/components/Form/ArrayField.tsx +7 -4
- package/src/components/Form/Defaults.tsx +1 -1
- package/src/components/Form/Form.stories.tsx +52 -52
- package/src/components/Form/Form.tsx +19 -9
- package/src/components/Form/FormActions.tsx +7 -2
- package/src/components/Form/FormContent.tsx +83 -23
- package/src/components/Form/FormContext.tsx +6 -6
- package/src/components/Form/Input.tsx +2 -2
- package/src/components/Form/RefField.tsx +50 -66
- package/src/components/Form/custom/GeoPointInput.tsx +1 -1
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +5 -6
- package/src/components/Form/custom/SelectOptionsInput.tsx +2 -2
- package/src/components/Form/factory.tsx +7 -4
- 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 +112 -49
- package/src/components/ViewEditor/ViewEditor.test.tsx +6 -5
- package/src/components/ViewEditor/ViewEditor.tsx +277 -208
- package/src/components/index.ts +1 -0
- package/src/components/testing.tsx +2 -2
- package/src/hooks/useForm.ts +11 -8
- package/src/hooks/useQueryRefOptions.ts +15 -6
- package/src/hooks/useRefQueryLookupHandler.ts +5 -5
- package/src/translations.ts +7 -6
- package/src/util/field.ts +25 -25
- package/src/util/formatting.ts +22 -22
- package/src/util/refs.ts +7 -7
- package/src/util/schema.ts +2 -4
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import * as Function from 'effect/Function';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
8
|
+
import * as StringEffect from 'effect/String';
|
|
9
|
+
import React, { Component, type PropsWithChildren, type ReactNode, forwardRef, useMemo } from 'react';
|
|
8
10
|
|
|
9
11
|
import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
|
|
10
12
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
11
13
|
import { mx } from '@dxos/react-ui-theme';
|
|
12
14
|
import { type ProjectionModel, type SchemaProperty, getSchemaProperties } from '@dxos/schema';
|
|
13
|
-
import {
|
|
15
|
+
import { isTruthy } from '@dxos/util';
|
|
14
16
|
|
|
15
17
|
import { type QueryRefOptions } from '../../hooks';
|
|
16
18
|
import { getRefProps } from '../../util';
|
|
@@ -33,7 +35,13 @@ export type FormFieldProps = {
|
|
|
33
35
|
Custom?: Partial<Record<string, InputComponent>>;
|
|
34
36
|
} & Pick<
|
|
35
37
|
RefFieldProps,
|
|
36
|
-
|
|
38
|
+
| 'readonly'
|
|
39
|
+
| 'onQueryRefOptions'
|
|
40
|
+
| 'createOptionLabel'
|
|
41
|
+
| 'createOptionIcon'
|
|
42
|
+
| 'onCreate'
|
|
43
|
+
| 'createSchema'
|
|
44
|
+
| 'createInitialValuePath'
|
|
37
45
|
>;
|
|
38
46
|
|
|
39
47
|
export const FormField = ({
|
|
@@ -45,14 +53,16 @@ export const FormField = ({
|
|
|
45
53
|
onQueryRefOptions,
|
|
46
54
|
createOptionLabel,
|
|
47
55
|
createOptionIcon,
|
|
48
|
-
|
|
56
|
+
onCreate,
|
|
57
|
+
createSchema,
|
|
58
|
+
createInitialValuePath,
|
|
49
59
|
lookupComponent,
|
|
50
60
|
Custom,
|
|
51
61
|
}: FormFieldProps) => {
|
|
52
62
|
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
53
63
|
const inputProps = useInputProps(path);
|
|
54
64
|
|
|
55
|
-
const label = useMemo(() => title ?? pipe(name, capitalize), [title, name]);
|
|
65
|
+
const label = useMemo(() => title ?? Function.pipe(name, StringEffect.capitalize), [title, name]);
|
|
56
66
|
const placeholder = useMemo(
|
|
57
67
|
() => (examples?.length ? `Example: "${examples[0]}"` : description),
|
|
58
68
|
[examples, description],
|
|
@@ -114,7 +124,9 @@ export const FormField = ({
|
|
|
114
124
|
onQueryRefOptions={onQueryRefOptions}
|
|
115
125
|
createOptionLabel={createOptionLabel}
|
|
116
126
|
createOptionIcon={createOptionIcon}
|
|
117
|
-
|
|
127
|
+
onCreate={onCreate}
|
|
128
|
+
createSchema={createSchema}
|
|
129
|
+
createInitialValuePath={createInitialValuePath}
|
|
118
130
|
{...inputProps}
|
|
119
131
|
/>
|
|
120
132
|
);
|
|
@@ -188,7 +200,7 @@ export const FormField = ({
|
|
|
188
200
|
onQueryRefOptions={onQueryRefOptions}
|
|
189
201
|
createOptionLabel={createOptionLabel}
|
|
190
202
|
createOptionIcon={createOptionIcon}
|
|
191
|
-
|
|
203
|
+
onCreate={onCreate}
|
|
192
204
|
Custom={Custom}
|
|
193
205
|
lookupComponent={lookupComponent}
|
|
194
206
|
/>
|
|
@@ -222,7 +234,15 @@ export type FormFieldsProps = ThemedClassName<
|
|
|
222
234
|
Custom?: Partial<Record<string, InputComponent>>;
|
|
223
235
|
onQueryRefOptions?: QueryRefOptions;
|
|
224
236
|
} & Pick<FormFieldProps, 'readonly'> &
|
|
225
|
-
Pick<
|
|
237
|
+
Pick<
|
|
238
|
+
RefFieldProps,
|
|
239
|
+
| 'onQueryRefOptions'
|
|
240
|
+
| 'createOptionLabel'
|
|
241
|
+
| 'createOptionIcon'
|
|
242
|
+
| 'onCreate'
|
|
243
|
+
| 'createSchema'
|
|
244
|
+
| 'createInitialValuePath'
|
|
245
|
+
>
|
|
226
246
|
>;
|
|
227
247
|
|
|
228
248
|
export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
@@ -244,7 +264,7 @@ export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
|
244
264
|
) => {
|
|
245
265
|
const values = useFormValues(path);
|
|
246
266
|
const properties = useMemo(() => {
|
|
247
|
-
const props = getSchemaProperties(schema.ast, values);
|
|
267
|
+
const props = getSchemaProperties(schema.ast, values, { form: true });
|
|
248
268
|
|
|
249
269
|
// Use projection-based field management when view and projection are available
|
|
250
270
|
if (projection) {
|
|
@@ -282,21 +302,61 @@ export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
|
282
302
|
{properties
|
|
283
303
|
.map((property) => {
|
|
284
304
|
return (
|
|
285
|
-
<
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
305
|
+
<ErrorBoundary key={property.name} path={[...(path ?? []), property.name]}>
|
|
306
|
+
<FormField
|
|
307
|
+
property={property}
|
|
308
|
+
path={[...(path ?? []), property.name]}
|
|
309
|
+
readonly={readonly}
|
|
310
|
+
projection={projection}
|
|
311
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
312
|
+
lookupComponent={lookupComponent}
|
|
313
|
+
Custom={Custom}
|
|
314
|
+
{...props}
|
|
315
|
+
/>
|
|
316
|
+
</ErrorBoundary>
|
|
296
317
|
);
|
|
297
318
|
})
|
|
298
|
-
.filter(
|
|
319
|
+
.filter(isTruthy)}
|
|
299
320
|
</div>
|
|
300
321
|
);
|
|
301
322
|
},
|
|
302
323
|
);
|
|
324
|
+
|
|
325
|
+
type ErrorState = {
|
|
326
|
+
error: Error | undefined;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
type ErrorBoundaryProps = PropsWithChildren<{
|
|
330
|
+
path?: (string | number)[];
|
|
331
|
+
}>;
|
|
332
|
+
|
|
333
|
+
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorState> {
|
|
334
|
+
static getDerivedStateFromError(error: Error): { error: Error } {
|
|
335
|
+
return { error };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
override state = { error: undefined };
|
|
339
|
+
|
|
340
|
+
override componentDidUpdate(prevProps: ErrorBoundaryProps): void {
|
|
341
|
+
if (prevProps.path !== this.props.path) {
|
|
342
|
+
this.resetError();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
override render(): ReactNode {
|
|
347
|
+
if (this.state.error) {
|
|
348
|
+
return (
|
|
349
|
+
<div className='flex gap-2 border border-roseFill font-mono text-sm'>
|
|
350
|
+
<span className='bg-roseFill text-surfaceText pli-1 font-thin'>ERROR</span>
|
|
351
|
+
{String(this.props.path?.join('.'))}
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return this.props.children;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private resetError(): void {
|
|
360
|
+
this.setState({ error: undefined });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -14,23 +14,23 @@ import React, {
|
|
|
14
14
|
} from 'react';
|
|
15
15
|
|
|
16
16
|
import { raise } from '@dxos/debug';
|
|
17
|
-
import { type
|
|
17
|
+
import { type AnyProperties, getValue } from '@dxos/echo/internal';
|
|
18
18
|
import { type SimpleType, createJsonPath } from '@dxos/effect';
|
|
19
19
|
|
|
20
20
|
import { type FormHandler, type FormOptions, useForm } from '../../hooks';
|
|
21
21
|
|
|
22
|
-
type FormContextValue<T extends
|
|
22
|
+
type FormContextValue<T extends AnyProperties> = FormHandler<T>;
|
|
23
23
|
|
|
24
24
|
const FormContext = createContext<FormContextValue<any> | undefined>(undefined);
|
|
25
25
|
|
|
26
|
-
export const useFormContext = <T extends
|
|
26
|
+
export const useFormContext = <T extends AnyProperties>(): FormContextValue<T> => {
|
|
27
27
|
return useContext(FormContext) ?? raise(new Error('Missing FormContext'));
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export const useFormValues = (path: (string | number)[] = []): any => {
|
|
31
31
|
const { values: formValues } = useFormContext();
|
|
32
32
|
const jsonPath = createJsonPath(path);
|
|
33
|
-
return getValue(formValues, jsonPath) as
|
|
33
|
+
return getValue(formValues, jsonPath) as AnyProperties;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export type FormInputStateProps = {
|
|
@@ -62,7 +62,7 @@ export const FormProvider = ({
|
|
|
62
62
|
...formOptions
|
|
63
63
|
}: PropsWithChildren<
|
|
64
64
|
FormOptions<any> & {
|
|
65
|
-
formRef?: RefObject<HTMLDivElement>;
|
|
65
|
+
formRef?: RefObject<HTMLDivElement | null>;
|
|
66
66
|
autoSave?: boolean;
|
|
67
67
|
}
|
|
68
68
|
>) => {
|
|
@@ -92,7 +92,7 @@ export const FormProvider = ({
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
-
[form, autoSave],
|
|
95
|
+
[form.canSave, form.formIsValid, form.handleSave, autoSave],
|
|
96
96
|
);
|
|
97
97
|
|
|
98
98
|
useEffect(() => {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type FC } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
7
|
+
import { type Format } from '@dxos/echo/internal';
|
|
8
8
|
import { type SimpleType } from '@dxos/effect';
|
|
9
9
|
import { Icon, Input, Tooltip } from '@dxos/react-ui';
|
|
10
10
|
import { labelSpacing } from '@dxos/react-ui-stack';
|
|
@@ -17,7 +17,7 @@ import { type FormInputStateProps } from './FormContext';
|
|
|
17
17
|
*/
|
|
18
18
|
export type InputProps = {
|
|
19
19
|
type: SimpleType;
|
|
20
|
-
format?:
|
|
20
|
+
format?: Format.TypeFormat;
|
|
21
21
|
label: string;
|
|
22
22
|
placeholder?: string;
|
|
23
23
|
inputOnly?: boolean;
|
|
@@ -2,29 +2,31 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
5
|
+
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
6
|
+
|
|
7
|
+
import type * as Schema from 'effect/Schema';
|
|
8
|
+
import type * as SchemaAST from 'effect/SchemaAST';
|
|
9
|
+
import React, { useCallback, useMemo } from 'react';
|
|
7
10
|
|
|
8
11
|
import {
|
|
12
|
+
type EchoSchema,
|
|
9
13
|
Expando,
|
|
10
14
|
Ref,
|
|
11
15
|
ReferenceAnnotationId,
|
|
12
16
|
type ReferenceAnnotationValue,
|
|
13
|
-
type TypeAnnotation,
|
|
14
17
|
getTypeAnnotation,
|
|
15
|
-
} from '@dxos/echo
|
|
18
|
+
} from '@dxos/echo/internal';
|
|
16
19
|
import { findAnnotation } from '@dxos/effect';
|
|
17
20
|
import { DXN } from '@dxos/keys';
|
|
18
21
|
import { type DxTagPickerItemClick } from '@dxos/lit-ui';
|
|
19
|
-
import {
|
|
22
|
+
import { DxAnchor, DxTagPickerItem } from '@dxos/lit-ui/react';
|
|
20
23
|
import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
|
|
21
|
-
import { PopoverCombobox } from '@dxos/react-ui-searchlist';
|
|
22
|
-
import { TagPickerItem } from '@dxos/react-ui-tag-picker';
|
|
23
24
|
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
24
|
-
import {
|
|
25
|
+
import { isNonNullable } from '@dxos/util';
|
|
25
26
|
|
|
26
27
|
import { type QueryRefOptions, useQueryRefOptions } from '../../hooks';
|
|
27
28
|
import { translationKey } from '../../translations';
|
|
29
|
+
import { ObjectPicker } from '../ObjectPicker';
|
|
28
30
|
|
|
29
31
|
import { TextInput } from './Defaults';
|
|
30
32
|
import { InputHeader, type InputProps } from './Input';
|
|
@@ -32,10 +34,13 @@ import { InputHeader, type InputProps } from './Input';
|
|
|
32
34
|
export type RefFieldProps = InputProps & {
|
|
33
35
|
ast?: SchemaAST.AST;
|
|
34
36
|
array?: boolean;
|
|
35
|
-
onQueryRefOptions?: QueryRefOptions;
|
|
36
37
|
createOptionLabel?: [string, { ns: string }];
|
|
37
38
|
createOptionIcon?: string;
|
|
38
|
-
|
|
39
|
+
onCreate?: (values: any) => void;
|
|
40
|
+
createSchema?: Schema.Schema.AnyNoContext;
|
|
41
|
+
createInitialValuePath?: string;
|
|
42
|
+
onQueryRefOptions?: QueryRefOptions;
|
|
43
|
+
schema?: EchoSchema;
|
|
39
44
|
};
|
|
40
45
|
|
|
41
46
|
// TODO(thure): Is this a standard that should be better canonized?
|
|
@@ -52,11 +57,13 @@ export const RefField = ({
|
|
|
52
57
|
array,
|
|
53
58
|
ast,
|
|
54
59
|
getValue,
|
|
55
|
-
onBlur,
|
|
56
|
-
onQueryRefOptions,
|
|
57
60
|
createOptionLabel,
|
|
58
61
|
createOptionIcon,
|
|
59
|
-
|
|
62
|
+
onBlur,
|
|
63
|
+
onCreate,
|
|
64
|
+
createSchema,
|
|
65
|
+
createInitialValuePath,
|
|
66
|
+
onQueryRefOptions,
|
|
60
67
|
onValueChange,
|
|
61
68
|
...restInputProps
|
|
62
69
|
}: RefFieldProps) => {
|
|
@@ -65,7 +72,11 @@ export const RefField = ({
|
|
|
65
72
|
() => (ast ? findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId) : undefined),
|
|
66
73
|
[ast],
|
|
67
74
|
);
|
|
68
|
-
const {
|
|
75
|
+
const {
|
|
76
|
+
options: availableOptions,
|
|
77
|
+
update: updateOptions,
|
|
78
|
+
loading: _loading,
|
|
79
|
+
} = useQueryRefOptions({ refTypeInfo, onQueryRefOptions });
|
|
69
80
|
|
|
70
81
|
if ((refTypeInfo && refTypeInfo?.typename === getTypeAnnotation(Expando)?.typename) || !onQueryRefOptions) {
|
|
71
82
|
// If ref type is expando, fall back to taking a DXN in string format.
|
|
@@ -130,6 +141,14 @@ export const RefField = ({
|
|
|
130
141
|
[availableOptions, array, onValueChange],
|
|
131
142
|
);
|
|
132
143
|
|
|
144
|
+
const handleCreate = useCallback(
|
|
145
|
+
(values: any) => {
|
|
146
|
+
onCreate?.(values);
|
|
147
|
+
updateOptions();
|
|
148
|
+
},
|
|
149
|
+
[onCreate, updateOptions],
|
|
150
|
+
);
|
|
151
|
+
|
|
133
152
|
if (!refTypeInfo) {
|
|
134
153
|
return null;
|
|
135
154
|
}
|
|
@@ -138,15 +157,6 @@ export const RefField = ({
|
|
|
138
157
|
|
|
139
158
|
const items = handleGetValue();
|
|
140
159
|
const selectedIds = useMemo(() => items.map((i: any) => i.id), [items]);
|
|
141
|
-
const labelById = useMemo(
|
|
142
|
-
() =>
|
|
143
|
-
availableOptions.reduce((acc: Record<string, string>, option) => {
|
|
144
|
-
acc[option.id.toLowerCase()] = option.label.toLowerCase();
|
|
145
|
-
return acc;
|
|
146
|
-
}, {}),
|
|
147
|
-
[availableOptions],
|
|
148
|
-
);
|
|
149
|
-
const [query, setQuery] = useState('');
|
|
150
160
|
const toggleSelect = useCallback(
|
|
151
161
|
(id: string) => {
|
|
152
162
|
if (array) {
|
|
@@ -173,19 +183,19 @@ export const RefField = ({
|
|
|
173
183
|
<p className={mx(descriptionText, 'mbe-2')}>{t('empty readonly ref field label')}</p>
|
|
174
184
|
) : (
|
|
175
185
|
items.map((item) => (
|
|
176
|
-
<
|
|
186
|
+
<DxAnchor key={item.id} refid={item.id} rootclassname='mie-1'>
|
|
177
187
|
{item.label}
|
|
178
|
-
</
|
|
188
|
+
</DxAnchor>
|
|
179
189
|
))
|
|
180
190
|
)
|
|
181
191
|
) : (
|
|
182
|
-
<
|
|
183
|
-
<
|
|
192
|
+
<ObjectPicker.Root>
|
|
193
|
+
<ObjectPicker.Trigger asChild>
|
|
184
194
|
<Button variant='ghost' classNames='is-full text-start gap-2'>
|
|
185
195
|
<div role='none' className='grow'>
|
|
186
196
|
{items?.length ? (
|
|
187
197
|
items?.map((item) => (
|
|
188
|
-
<
|
|
198
|
+
<DxTagPickerItem
|
|
189
199
|
key={item.id}
|
|
190
200
|
itemId={item.id}
|
|
191
201
|
label={item.label}
|
|
@@ -206,44 +216,18 @@ export const RefField = ({
|
|
|
206
216
|
</div>
|
|
207
217
|
<Icon size={3} icon='ph--caret-down--bold' />
|
|
208
218
|
</Button>
|
|
209
|
-
</
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
<PopoverCombobox.List>
|
|
222
|
-
{availableOptions.map((option) => (
|
|
223
|
-
<PopoverCombobox.Item
|
|
224
|
-
key={option.id}
|
|
225
|
-
value={option.id}
|
|
226
|
-
onSelect={() => toggleSelect(option.id)}
|
|
227
|
-
classNames='flex items-center gap-2'
|
|
228
|
-
>
|
|
229
|
-
<span className='grow'>{option.label}</span>
|
|
230
|
-
{selectedIds.includes(option.id) && <Icon icon='ph--check--regular' />}
|
|
231
|
-
</PopoverCombobox.Item>
|
|
232
|
-
))}
|
|
233
|
-
{query.length > 0 && createOptionLabel && createOptionIcon && onCreateFromQuery && (
|
|
234
|
-
<PopoverCombobox.Item
|
|
235
|
-
value='__create__'
|
|
236
|
-
onSelect={() => onCreateFromQuery?.(refTypeInfo, query)}
|
|
237
|
-
classNames='flex items-center gap-2'
|
|
238
|
-
>
|
|
239
|
-
<Icon icon={createOptionIcon} />
|
|
240
|
-
{t(createOptionLabel[0], { ns: createOptionLabel[1].ns, text: query })}
|
|
241
|
-
</PopoverCombobox.Item>
|
|
242
|
-
)}
|
|
243
|
-
</PopoverCombobox.List>
|
|
244
|
-
<PopoverCombobox.Arrow />
|
|
245
|
-
</PopoverCombobox.Content>
|
|
246
|
-
</PopoverCombobox.Root>
|
|
219
|
+
</ObjectPicker.Trigger>
|
|
220
|
+
<ObjectPicker.Content
|
|
221
|
+
options={availableOptions}
|
|
222
|
+
selectedIds={selectedIds}
|
|
223
|
+
onSelect={toggleSelect}
|
|
224
|
+
createSchema={createSchema}
|
|
225
|
+
createOptionLabel={createOptionLabel}
|
|
226
|
+
createOptionIcon={createOptionIcon}
|
|
227
|
+
createInitialValuePath={createInitialValuePath}
|
|
228
|
+
onCreate={handleCreate}
|
|
229
|
+
/>
|
|
230
|
+
</ObjectPicker.Root>
|
|
247
231
|
)}
|
|
248
232
|
</div>
|
|
249
233
|
{inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { GeoLocation, type GeoPoint } from '@dxos/echo
|
|
7
|
+
import { GeoLocation, type GeoPoint } from '@dxos/echo/internal';
|
|
8
8
|
import { Input, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { safeParseFloat } from '@dxos/util';
|
|
10
10
|
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
9
7
|
import React, { useState } from 'react';
|
|
10
8
|
|
|
11
|
-
import { type SelectOption, SelectOptionSchema } from '@dxos/echo
|
|
12
|
-
import {
|
|
9
|
+
import { type SelectOption, SelectOptionSchema } from '@dxos/echo/internal';
|
|
10
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
13
11
|
|
|
14
12
|
import { translations } from '../../../translations';
|
|
15
13
|
import { TestLayout, TestPanel } from '../../testing';
|
|
@@ -49,8 +47,9 @@ const meta = {
|
|
|
49
47
|
title: 'ui/react-ui-form/SelectOptionsInput',
|
|
50
48
|
component: SelectOptionInput as any,
|
|
51
49
|
render: DefaultStory,
|
|
52
|
-
decorators: [withTheme
|
|
50
|
+
decorators: [withTheme],
|
|
53
51
|
parameters: {
|
|
52
|
+
layout: 'fullscreen',
|
|
54
53
|
translations,
|
|
55
54
|
},
|
|
56
55
|
} satisfies Meta<typeof DefaultStory>;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type ChangeEvent, type KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type SelectOption } from '@dxos/echo
|
|
7
|
+
import { type SelectOption } from '@dxos/echo/internal';
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
9
9
|
import { type ChromaticPalette, IconButton, Input, Tag, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
import { List } from '@dxos/react-ui-list';
|
|
@@ -121,7 +121,7 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
|
|
|
121
121
|
readonly={!!readonly}
|
|
122
122
|
>
|
|
123
123
|
{({ items }) => (
|
|
124
|
-
<div role='list' className='
|
|
124
|
+
<div role='list' className='is-full overflow-auto'>
|
|
125
125
|
{items?.map((item) => (
|
|
126
126
|
<div key={item.id}>
|
|
127
127
|
<List.Item
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Format } from '@dxos/echo/internal';
|
|
6
6
|
import { type SimpleType } from '@dxos/effect';
|
|
7
7
|
|
|
8
8
|
import { GeoPointInput } from './custom';
|
|
@@ -12,11 +12,14 @@ import { type InputComponent } from './Input';
|
|
|
12
12
|
/**
|
|
13
13
|
* Get property input component.
|
|
14
14
|
*/
|
|
15
|
-
export const getInputComponent = (
|
|
15
|
+
export const getInputComponent = (
|
|
16
|
+
type: SimpleType,
|
|
17
|
+
format?: Format.TypeFormat | string,
|
|
18
|
+
): InputComponent | undefined => {
|
|
16
19
|
switch (format) {
|
|
17
|
-
case
|
|
20
|
+
case Format.TypeFormat.GeoPoint:
|
|
18
21
|
return GeoPointInput;
|
|
19
|
-
case
|
|
22
|
+
case Format.TypeFormat.Markdown:
|
|
20
23
|
return MarkdownInput;
|
|
21
24
|
}
|
|
22
25
|
|