@dxos/react-ui-form 0.8.4-main.c1de068 → 0.8.4-main.c85a9c8dae
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/README.md +0 -1
- package/dist/lib/browser/index.mjs +2314 -2078
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +2314 -2078
- 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 +8 -7
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +82 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +81 -25
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +127 -55
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +37 -0
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +78 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts +19 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts +14 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +82 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/index.d.ts +12 -0
- package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +2 -4
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +46 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +85 -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/Settings/Settings.d.ts +56 -0
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts +81 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +2 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +22 -10
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +84 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +4 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -3
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +19 -10
- 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/index.d.ts +1 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +7 -0
- package/dist/types/src/util/properties.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +2 -3
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +51 -42
- package/src/components/FieldEditor/FieldEditor.stories.tsx +51 -32
- package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
- package/src/components/FieldEditor/FieldEditor.tsx +87 -83
- package/src/components/Form/Form.stories.tsx +156 -219
- package/src/components/Form/Form.tsx +347 -96
- package/src/components/Form/FormField.tsx +294 -0
- package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
- package/src/components/Form/FormFieldComponent.tsx +172 -0
- package/src/components/Form/FormFieldSet.tsx +123 -0
- package/src/components/Form/README.md +3 -0
- package/src/components/Form/fields/ArrayField.tsx +149 -0
- package/src/components/Form/fields/BooleanField.tsx +22 -0
- package/src/components/Form/fields/GeoPointField.tsx +120 -0
- package/src/components/Form/fields/MarkdownField.tsx +78 -0
- package/src/components/Form/fields/NumberField.tsx +39 -0
- package/src/components/Form/fields/RefField.tsx +189 -0
- package/src/components/Form/fields/SelectField.tsx +65 -0
- package/src/components/Form/fields/SelectOptionField.stories.tsx +64 -0
- package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +46 -41
- package/src/components/Form/fields/TextAreaField.tsx +38 -0
- package/src/components/Form/fields/TextField.tsx +38 -0
- package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +16 -9
- package/src/components/Form/fields/index.ts +15 -0
- package/src/components/Form/index.ts +3 -4
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +210 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +175 -0
- package/src/components/{ControlSection → ObjectPicker}/index.ts +1 -1
- package/src/components/Settings/Settings.stories.tsx +56 -0
- package/src/components/Settings/Settings.tsx +230 -0
- package/src/components/Settings/index.ts +5 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +140 -62
- package/src/components/ViewEditor/ViewEditor.test.tsx +17 -18
- package/src/components/ViewEditor/ViewEditor.tsx +341 -202
- package/src/components/index.ts +2 -3
- package/src/components/testing.tsx +22 -11
- package/src/hooks/index.ts +2 -3
- package/src/hooks/useFormHandler.ts +380 -0
- package/src/hooks/useKeyHandler.tsx +37 -0
- package/src/translations.ts +21 -12
- package/src/util/field.ts +33 -43
- package/src/util/formatting.ts +22 -22
- package/src/util/index.ts +1 -1
- package/src/util/properties.ts +18 -0
- package/src/util/refs.ts +12 -18
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/index.d.ts +0 -2
- package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
- package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
- package/dist/types/src/components/Form/Defaults.d.ts +0 -14
- package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormActions.d.ts +0 -9
- package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormContent.d.ts +0 -57
- package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormContext.d.ts +0 -23
- package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
- package/dist/types/src/components/Form/Input.d.ts +0 -26
- package/dist/types/src/components/Form/Input.d.ts.map +0 -1
- package/dist/types/src/components/Form/RefField.d.ts +0 -12
- package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/index.d.ts +0 -4
- package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
- package/dist/types/src/components/Form/factory.d.ts +0 -8
- package/dist/types/src/components/Form/factory.d.ts.map +0 -1
- 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/dist/types/src/hooks/useForm.d.ts +0 -67
- package/dist/types/src/hooks/useForm.d.ts.map +0 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
- package/dist/types/src/util/schema.d.ts +0 -3
- package/dist/types/src/util/schema.d.ts.map +0 -1
- package/src/components/ControlSection/ControlSection.stories.tsx +0 -41
- package/src/components/ControlSection/ControlSection.tsx +0 -139
- package/src/components/Form/ArrayField.tsx +0 -124
- package/src/components/Form/Defaults.tsx +0 -203
- package/src/components/Form/FormActions.tsx +0 -53
- package/src/components/Form/FormContent.tsx +0 -240
- package/src/components/Form/FormContext.tsx +0 -110
- package/src/components/Form/Input.tsx +0 -51
- package/src/components/Form/RefField.tsx +0 -213
- package/src/components/Form/custom/GeoPointInput.tsx +0 -72
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +0 -61
- package/src/components/Form/custom/index.ts +0 -7
- package/src/components/Form/factory.tsx +0 -31
- 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 +0 -291
- package/src/hooks/useQueryRefOptions.ts +0 -53
- package/src/hooks/useRefQueryLookupHandler.ts +0 -39
- package/src/util/schema.ts +0 -14
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import { Schema } from 'effect';
|
|
9
|
-
import React, { useState } from 'react';
|
|
10
|
-
|
|
11
|
-
import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
|
|
12
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
|
-
|
|
14
|
-
import { SelectOptionInput } from './SelectOptionsInput';
|
|
15
|
-
import { translations } from '../../../translations';
|
|
16
|
-
import { TestLayout, TestPanel } from '../../testing';
|
|
17
|
-
import { Form } from '../Form';
|
|
18
|
-
|
|
19
|
-
const schema = Schema.Struct({
|
|
20
|
-
options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
|
|
21
|
-
}).pipe(Schema.mutable);
|
|
22
|
-
|
|
23
|
-
const DefaultStory = () => {
|
|
24
|
-
const [values, setValues] = useState<{ options: SelectOption[] }>({
|
|
25
|
-
// TODO(Zaymon): Pick colors from our limited set.
|
|
26
|
-
options: [
|
|
27
|
-
{ id: 'opt_1', title: 'First option', color: 'amber' },
|
|
28
|
-
{ id: 'opt_2', title: 'Second option', color: 'emerald' },
|
|
29
|
-
{ id: 'opt_3', title: 'Third option', color: 'rose' },
|
|
30
|
-
],
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<TestLayout>
|
|
35
|
-
<TestPanel>
|
|
36
|
-
<Form
|
|
37
|
-
schema={schema}
|
|
38
|
-
values={values}
|
|
39
|
-
onSave={(vals) => setValues(vals)}
|
|
40
|
-
Custom={{ options: SelectOptionInput }}
|
|
41
|
-
/>
|
|
42
|
-
</TestPanel>
|
|
43
|
-
</TestLayout>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const meta: Meta = {
|
|
48
|
-
title: 'ui/react-ui-form/SelectOptionsInput',
|
|
49
|
-
component: SelectOptionInput,
|
|
50
|
-
render: DefaultStory,
|
|
51
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
52
|
-
parameters: {
|
|
53
|
-
translations,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export default meta;
|
|
58
|
-
|
|
59
|
-
type Story = StoryObj;
|
|
60
|
-
|
|
61
|
-
export const Default: Story = {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { FormatEnum } from '@dxos/echo-schema';
|
|
6
|
-
import { type SimpleType } from '@dxos/effect';
|
|
7
|
-
|
|
8
|
-
import { BooleanInput, MarkdownInput, NumberInput, TextInput } from './Defaults';
|
|
9
|
-
import { type InputComponent } from './Input';
|
|
10
|
-
import { GeoPointInput } from './custom';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get property input component.
|
|
14
|
-
*/
|
|
15
|
-
export const getInputComponent = (type: SimpleType, format?: FormatEnum | string): InputComponent | undefined => {
|
|
16
|
-
switch (format) {
|
|
17
|
-
case FormatEnum.GeoPoint:
|
|
18
|
-
return GeoPointInput;
|
|
19
|
-
case FormatEnum.Markdown:
|
|
20
|
-
return MarkdownInput;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
switch (type) {
|
|
24
|
-
case 'string':
|
|
25
|
-
return TextInput;
|
|
26
|
-
case 'number':
|
|
27
|
-
return NumberInput;
|
|
28
|
-
case 'boolean':
|
|
29
|
-
return BooleanInput;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
@@ -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
|
-
};
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { SchemaAST, type Schema, Option, pipe } from 'effect';
|
|
6
|
-
import React, { useState } from 'react';
|
|
7
|
-
|
|
8
|
-
import { findProperty } from '@dxos/effect';
|
|
9
|
-
import { Input, type ThemedClassName, type TextInputProps as NativeTextInputProps } from '@dxos/react-ui';
|
|
10
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
11
|
-
import { getFieldValue, setFieldValue, type Projection } from '@dxos/schema';
|
|
12
|
-
|
|
13
|
-
export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
|
|
14
|
-
projection: Projection;
|
|
15
|
-
object: T;
|
|
16
|
-
schema?: Schema.Schema<T>;
|
|
17
|
-
readonly?: boolean;
|
|
18
|
-
}>;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Schema-based object form.
|
|
22
|
-
*/
|
|
23
|
-
export const DeprecatedForm = <T extends {} = {}>({
|
|
24
|
-
classNames,
|
|
25
|
-
projection,
|
|
26
|
-
object,
|
|
27
|
-
schema,
|
|
28
|
-
readonly,
|
|
29
|
-
}: DeprecatedFormProps<T>) => {
|
|
30
|
-
return (
|
|
31
|
-
<div role='none' className={mx('flex flex-col w-full gap-2 p-2', classNames)}>
|
|
32
|
-
{projection.fields.map((field) => {
|
|
33
|
-
const prop = schema && findProperty(schema, field.path);
|
|
34
|
-
const title = (prop && pipe(SchemaAST.getTitleAnnotation(prop), Option.getOrUndefined)) ?? '';
|
|
35
|
-
const description = (prop && pipe(SchemaAST.getDescriptionAnnotation(prop), Option.getOrUndefined)) ?? title;
|
|
36
|
-
// const format =
|
|
37
|
-
// (prop && (getPatternAnnotation(prop) ?? (SchemaAST.isNumberKeyword(prop) && RealNumberFormat))) || undefined;
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
// Boolean
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
if (prop && SchemaAST.isBooleanKeyword(prop)) {
|
|
44
|
-
const value = object && getFieldValue(object, field);
|
|
45
|
-
const onChange = (checked: boolean) => {
|
|
46
|
-
setFieldValue(object, field, checked);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div key={field.path} className='flex flex-col w-full gap-1'>
|
|
51
|
-
<Input.Root>
|
|
52
|
-
<Input.Label classNames='px-1'>{title}</Input.Label>
|
|
53
|
-
<Input.Switch disabled={readonly} checked={!!value} onCheckedChange={(checked) => onChange(checked)} />
|
|
54
|
-
</Input.Root>
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
// Strings
|
|
61
|
-
// TODO(burdon): Other types:
|
|
62
|
-
// - RichText editor
|
|
63
|
-
// - Numbers (with formatting: commas, currency, etc.)
|
|
64
|
-
// - Date (https://ark-ui.com/react/docs/components/date-picker)
|
|
65
|
-
// - Enum (single/multi-select)
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
const value = object ? getFieldValue(object, field) : undefined;
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div key={field.path} className='flex flex-col w-full gap-1'>
|
|
72
|
-
<Input.Root>
|
|
73
|
-
<Input.Label classNames='px-1'>{title}</Input.Label>
|
|
74
|
-
<TextInput
|
|
75
|
-
disabled={readonly}
|
|
76
|
-
placeholder={description}
|
|
77
|
-
// format={format}
|
|
78
|
-
value={value ? String(value) : ''}
|
|
79
|
-
onChange={(event) => setFieldValue(object, field, event.target.value)}
|
|
80
|
-
/>
|
|
81
|
-
</Input.Root>
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
})}
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
type TextInputProps = {
|
|
90
|
-
// format?: PatternAnnotation;
|
|
91
|
-
} & Omit<NativeTextInputProps, 'style'>;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* @deprecated
|
|
95
|
-
*/
|
|
96
|
-
// TODO(burdon): Remove.
|
|
97
|
-
// TODO(burdon): Use schema validation directly.
|
|
98
|
-
const TextInput = ({ onChange, ...props }: TextInputProps) => {
|
|
99
|
-
const [valid, _setValid] = useState(true);
|
|
100
|
-
|
|
101
|
-
const handleChange: NativeTextInputProps['onChange'] = (ev) => {
|
|
102
|
-
// const text = ev.target.value;
|
|
103
|
-
// if (!format?.filter || text.match(format.filter)) {
|
|
104
|
-
onChange?.(ev);
|
|
105
|
-
// if (format?.valid) {
|
|
106
|
-
// setValid(format.valid.test(text));
|
|
107
|
-
// }
|
|
108
|
-
// }
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<Input.TextInput
|
|
113
|
-
{...props}
|
|
114
|
-
// TODO(burdon): validationValence on Input.
|
|
115
|
-
style={valid ? {} : ({ '--tw-ring-color': 'red' } as any)}
|
|
116
|
-
onChange={handleChange}
|
|
117
|
-
/>
|
|
118
|
-
);
|
|
119
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { type JSX, type PropsWithChildren } from 'react';
|
|
6
|
-
|
|
7
|
-
import { Input, type ThemedClassName } from '@dxos/react-ui';
|
|
8
|
-
import { mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @deprecated
|
|
12
|
-
*/
|
|
13
|
-
export const DeprecatedFormContainer = ({ children, classNames }: ThemedClassName<PropsWithChildren>) => {
|
|
14
|
-
return (
|
|
15
|
-
<div role='none' className='container-max-width p-4 justify-center overflow-x-hidden overflow-y-auto'>
|
|
16
|
-
<div role='form' className={mx('flex flex-col', textBlockWidth, classNames)}>
|
|
17
|
-
{children}
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type DeprecatedFormInputProps = {
|
|
24
|
-
label: string;
|
|
25
|
-
description?: JSX.Element;
|
|
26
|
-
secondary?: JSX.Element;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @deprecated
|
|
31
|
-
*/
|
|
32
|
-
export const DeprecatedFormInput = ({
|
|
33
|
-
label,
|
|
34
|
-
description,
|
|
35
|
-
secondary,
|
|
36
|
-
children,
|
|
37
|
-
}: PropsWithChildren<DeprecatedFormInputProps>) => {
|
|
38
|
-
const primary = (
|
|
39
|
-
<div role='none' className='flex w-full gap-4 py-1'>
|
|
40
|
-
<Input.Root>
|
|
41
|
-
<div role='none' className='flex flex-col w-full'>
|
|
42
|
-
<Input.Label classNames='flex min-bs-[--rail-action] items-center' style={{ fontSize: 'unset' }}>
|
|
43
|
-
{label}
|
|
44
|
-
</Input.Label>
|
|
45
|
-
{description && (
|
|
46
|
-
<Input.DescriptionAndValidation classNames='mbs-0.5'>
|
|
47
|
-
<Input.Description>{description}</Input.Description>
|
|
48
|
-
</Input.DescriptionAndValidation>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<div role='none'>
|
|
53
|
-
<div role='none' className='flex min-bs-[--rail-action] items-center'>
|
|
54
|
-
{children}
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
</Input.Root>
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (secondary) {
|
|
62
|
-
return (
|
|
63
|
-
<div role='none'>
|
|
64
|
-
{primary}
|
|
65
|
-
{secondary}
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return primary;
|
|
71
|
-
};
|
package/src/hooks/useForm.ts
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { SchemaAST, type Schema } from 'effect';
|
|
6
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
-
|
|
8
|
-
import { type BaseObject, getValue, setValue } from '@dxos/echo-schema';
|
|
9
|
-
import { type SimpleType, type JsonPath, createJsonPath, fromEffectValidationPath } from '@dxos/effect';
|
|
10
|
-
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import { log } from '@dxos/log';
|
|
12
|
-
import { validateSchema, type ValidationError } from '@dxos/schema';
|
|
13
|
-
import { type MaybePromise } from '@dxos/util';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Return type from `useForm` hook.
|
|
17
|
-
*/
|
|
18
|
-
export type FormHandler<T extends BaseObject> = {
|
|
19
|
-
//
|
|
20
|
-
// Form state management.
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
values: Partial<T>;
|
|
24
|
-
errors: Record<JsonPath, string>;
|
|
25
|
-
touched: Record<JsonPath, boolean>;
|
|
26
|
-
changed: Record<JsonPath, boolean>;
|
|
27
|
-
canSave: boolean;
|
|
28
|
-
formIsValid: boolean;
|
|
29
|
-
handleSave: () => void;
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
// Form input component helpers.
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
getStatus: (path: string | (string | number)[]) => { status?: 'error'; error?: string };
|
|
36
|
-
getValue: <V>(path: (string | number)[]) => V | undefined;
|
|
37
|
-
onValueChange: <V>(path: (string | number)[], type: SimpleType, value: V) => void;
|
|
38
|
-
onTouched: (path: (string | number)[]) => void;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Hook options.
|
|
43
|
-
*/
|
|
44
|
-
export interface FormOptions<T extends BaseObject> {
|
|
45
|
-
/**
|
|
46
|
-
* Effect schema (Type literal).
|
|
47
|
-
*/
|
|
48
|
-
schema: Schema.Schema<T, any>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Initial values (which may not pass validation).
|
|
52
|
-
*/
|
|
53
|
-
initialValues: Partial<T>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Callback for value changes. Note: This is called even when values are invalid.
|
|
57
|
-
* Sometimes the parent component may want to know about changes even if the form is
|
|
58
|
-
* in an invalid state.
|
|
59
|
-
*/
|
|
60
|
-
onValuesChanged?: (values: Partial<T>) => void;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Custom validation function that runs only after schema validation passes.
|
|
64
|
-
* Use this for complex validation logic that can't be expressed in the schema.
|
|
65
|
-
* @returns Array of validation errors, or undefined if validation passes
|
|
66
|
-
*/
|
|
67
|
-
// TODO(burdon): Change to key x value?
|
|
68
|
-
onValidate?: (values: T) => ValidationError[] | undefined;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Called when the form is valid.
|
|
72
|
-
*/
|
|
73
|
-
onValid?: (values: T, meta: { changed: FormHandler<T>['changed'] }) => void;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Called when the form is submitted and passes validation.
|
|
77
|
-
*/
|
|
78
|
-
onSave?: (values: T, meta: { changed: FormHandler<T>['changed'] }) => MaybePromise<void>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Creates a hook for managing form state, including values, validation, and submission.
|
|
83
|
-
* Deeply integrated with `@dxos/schema` for schema-based validation.
|
|
84
|
-
*/
|
|
85
|
-
export const useForm = <T extends BaseObject>({
|
|
86
|
-
schema,
|
|
87
|
-
initialValues,
|
|
88
|
-
onValuesChanged,
|
|
89
|
-
onValidate,
|
|
90
|
-
onValid,
|
|
91
|
-
onSave,
|
|
92
|
-
}: FormOptions<T>): FormHandler<T> => {
|
|
93
|
-
invariant(SchemaAST.isTypeLiteral(schema.ast));
|
|
94
|
-
|
|
95
|
-
const [values, setValues] = useState<Partial<T>>(initialValues);
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
setValues(initialValues);
|
|
99
|
-
}, [initialValues]);
|
|
100
|
-
|
|
101
|
-
const [touched, setTouched] = useState<Record<JsonPath, boolean>>(createKeySet(initialValues, false));
|
|
102
|
-
const [changed, setChanged] = useState<Record<JsonPath, boolean>>(createKeySet(initialValues, false));
|
|
103
|
-
const [errors, setErrors] = useState<Record<JsonPath, string>>({});
|
|
104
|
-
const [saving, setSaving] = useState(false);
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
// Validation.
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
const validate = useCallback(
|
|
111
|
-
(values: Partial<T>): values is T => {
|
|
112
|
-
let errors: ValidationError[] = validateSchema(schema, values) ?? [];
|
|
113
|
-
if (errors.length === 0 && onValidate) {
|
|
114
|
-
const validatedValues = values as T;
|
|
115
|
-
errors = onValidate(validatedValues) ?? [];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
setErrors(flatMap(errors));
|
|
119
|
-
const valid = errors.length === 0;
|
|
120
|
-
return valid;
|
|
121
|
-
},
|
|
122
|
-
[schema, onValidate],
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Validate on schema change.
|
|
126
|
-
const [schemaDirty, setSchemaDirty] = useState(false);
|
|
127
|
-
useEffect(() => setSchemaDirty(true), [schema]);
|
|
128
|
-
useEffect(() => {
|
|
129
|
-
if (schemaDirty) {
|
|
130
|
-
validate(values);
|
|
131
|
-
setSchemaDirty(false);
|
|
132
|
-
}
|
|
133
|
-
}, [values, schemaDirty]);
|
|
134
|
-
|
|
135
|
-
const formIsValid = useMemo(() => {
|
|
136
|
-
return Object.keys(errors).length === 0;
|
|
137
|
-
}, [errors]);
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* NOTE: We can submit if there is no touched field that has an error.
|
|
141
|
-
* Basically, if there's a validation message visible in the form, submit should be disabled.
|
|
142
|
-
*/
|
|
143
|
-
const canSave = useMemo(
|
|
144
|
-
() =>
|
|
145
|
-
!saving &&
|
|
146
|
-
// Check if any error paths that are touched have errors.
|
|
147
|
-
!Object.entries(touched).some(
|
|
148
|
-
([path, isTouched]) =>
|
|
149
|
-
isTouched &&
|
|
150
|
-
Object.keys(errors).some(
|
|
151
|
-
(errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`),
|
|
152
|
-
),
|
|
153
|
-
),
|
|
154
|
-
[touched, errors, saving],
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const handleSave = useCallback(async () => {
|
|
158
|
-
if (validate(values)) {
|
|
159
|
-
setSaving(true);
|
|
160
|
-
try {
|
|
161
|
-
await onSave?.(values, { changed });
|
|
162
|
-
} finally {
|
|
163
|
-
setSaving(false);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}, [values, validate, onSave, changed]);
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
// Fields.
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
const getStatus = useCallback<FormHandler<T>['getStatus']>(
|
|
173
|
-
(path) => {
|
|
174
|
-
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
175
|
-
const matchingError = Object.entries(errors).find(
|
|
176
|
-
([errorPath]) =>
|
|
177
|
-
errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`),
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Only show errors for touched fields.
|
|
181
|
-
const isTouched = touched[jsonPath as JsonPath];
|
|
182
|
-
if (!isTouched) {
|
|
183
|
-
return {
|
|
184
|
-
status: undefined,
|
|
185
|
-
error: undefined,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
status: matchingError ? 'error' : undefined,
|
|
191
|
-
error: matchingError ? matchingError[1] : undefined,
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
[errors, touched],
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
const getFormValue = useCallback<FormHandler<T>['getValue']>(
|
|
198
|
-
<V>(path: (string | number)[]): V | undefined => {
|
|
199
|
-
return getValue(values, createJsonPath(path));
|
|
200
|
-
},
|
|
201
|
-
[values],
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const onValueChange = useCallback<FormHandler<T>['onValueChange']>(
|
|
205
|
-
(path: (string | number)[], type: SimpleType, value: any) => {
|
|
206
|
-
const jsonPath = createJsonPath(path);
|
|
207
|
-
let parsedValue = value;
|
|
208
|
-
try {
|
|
209
|
-
if (type === 'number') {
|
|
210
|
-
parsedValue = parseFloat(value as string) || 0;
|
|
211
|
-
}
|
|
212
|
-
} catch (err) {
|
|
213
|
-
log.catch(err);
|
|
214
|
-
parsedValue = undefined;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const newValues = { ...setValue(values, jsonPath, parsedValue) };
|
|
218
|
-
setValues(newValues);
|
|
219
|
-
setChanged((prev) => ({ ...prev, [jsonPath]: true }));
|
|
220
|
-
onValuesChanged?.(newValues);
|
|
221
|
-
|
|
222
|
-
const isValid = validate(newValues);
|
|
223
|
-
if (isValid && onValid) {
|
|
224
|
-
onValid(newValues, { changed });
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
[values, onValuesChanged, validate, onValid, changed],
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
const onTouched = useCallback(
|
|
231
|
-
(path: (string | number)[]) => {
|
|
232
|
-
const jsonPath = createJsonPath(path);
|
|
233
|
-
setTouched((touched) => ({ ...touched, [jsonPath]: true }));
|
|
234
|
-
validate(values);
|
|
235
|
-
},
|
|
236
|
-
[validate, values],
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
return useMemo(
|
|
240
|
-
() => ({
|
|
241
|
-
// State.
|
|
242
|
-
values,
|
|
243
|
-
errors,
|
|
244
|
-
touched,
|
|
245
|
-
changed,
|
|
246
|
-
canSave,
|
|
247
|
-
formIsValid,
|
|
248
|
-
|
|
249
|
-
// Actions.
|
|
250
|
-
handleSave,
|
|
251
|
-
|
|
252
|
-
// Field utils.
|
|
253
|
-
getStatus,
|
|
254
|
-
getValue: getFormValue,
|
|
255
|
-
onValueChange,
|
|
256
|
-
onTouched,
|
|
257
|
-
}),
|
|
258
|
-
[
|
|
259
|
-
values,
|
|
260
|
-
errors,
|
|
261
|
-
touched,
|
|
262
|
-
changed,
|
|
263
|
-
canSave,
|
|
264
|
-
formIsValid,
|
|
265
|
-
handleSave,
|
|
266
|
-
getStatus,
|
|
267
|
-
getFormValue,
|
|
268
|
-
onValueChange,
|
|
269
|
-
onTouched,
|
|
270
|
-
],
|
|
271
|
-
);
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
const createKeySet = <T extends BaseObject, V>(obj: T, value: V): Record<JsonPath, V> => {
|
|
275
|
-
invariant(obj);
|
|
276
|
-
return Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: value }), {} as Record<JsonPath, V>);
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
const flatMap = (errors: ValidationError[]) => {
|
|
280
|
-
return errors.reduce(
|
|
281
|
-
(result, { path, message }) => {
|
|
282
|
-
// Convert the validation error path format to our JsonPath format.
|
|
283
|
-
const jsonPath = fromEffectValidationPath(path);
|
|
284
|
-
if (!(jsonPath in result)) {
|
|
285
|
-
result[jsonPath] = message;
|
|
286
|
-
}
|
|
287
|
-
return result;
|
|
288
|
-
},
|
|
289
|
-
{} as Record<JsonPath, string>,
|
|
290
|
-
);
|
|
291
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { type TypeAnnotation } from '@dxos/echo-schema';
|
|
8
|
-
import { type DXN } from '@dxos/keys';
|
|
9
|
-
import { log } from '@dxos/log';
|
|
10
|
-
import { type TagPickerItemData } from '@dxos/react-ui-tag-picker';
|
|
11
|
-
import { type MaybePromise } from '@dxos/util';
|
|
12
|
-
|
|
13
|
-
export type RefOption = { dxn: DXN; label?: string };
|
|
14
|
-
export type QueryRefOptions = (type: TypeAnnotation) => MaybePromise<RefOption[]>;
|
|
15
|
-
|
|
16
|
-
type UseQueryRefOptionsProps = { refTypeInfo: TypeAnnotation | undefined; onQueryRefOptions?: QueryRefOptions };
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Hook to query reference options based on type information.
|
|
20
|
-
* Used internally within forms to fetch and format reference options for reference fields.
|
|
21
|
-
*/
|
|
22
|
-
export const useQueryRefOptions = ({ refTypeInfo, onQueryRefOptions }: UseQueryRefOptionsProps) => {
|
|
23
|
-
const [options, setOptions] = useState<TagPickerItemData[]>([]);
|
|
24
|
-
const [loading, setLoading] = useState(false);
|
|
25
|
-
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
if (!refTypeInfo || !onQueryRefOptions) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const fetchOptions = async () => {
|
|
32
|
-
setLoading(true);
|
|
33
|
-
try {
|
|
34
|
-
const fetchedOptions = await onQueryRefOptions(refTypeInfo);
|
|
35
|
-
setOptions(
|
|
36
|
-
fetchedOptions.map((option) => {
|
|
37
|
-
const dxn = option.dxn.toString() as string;
|
|
38
|
-
return { id: dxn, label: option.label ?? dxn, hue: 'neutral' as any };
|
|
39
|
-
}),
|
|
40
|
-
);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
log.error('Failed to fetch ref options:', error);
|
|
43
|
-
setOptions([]);
|
|
44
|
-
} finally {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
void fetchOptions();
|
|
50
|
-
}, [refTypeInfo, onQueryRefOptions]);
|
|
51
|
-
|
|
52
|
-
return { options, loading };
|
|
53
|
-
};
|