@dxos/react-ui-form 0.9.1-main.c7dcc2e112 → 0.9.1-staging.ee54ba693a
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 +1883 -1539
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1883 -1539
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/annotations.d.ts +47 -0
- package/dist/types/src/annotations.d.ts.map +1 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +12 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +46 -65
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.theme.d.ts +252 -0
- package/dist/types/src/components/Form/Form.theme.d.ts.map +1 -0
- package/dist/types/src/components/Form/Form.theme.test.d.ts +2 -0
- package/dist/types/src/components/Form/Form.theme.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormControls.d.ts +17 -8
- package/dist/types/src/components/Form/FormControls.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/FormField.d.ts +16 -2
- package/dist/types/src/components/Form/FormField/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/FormFieldHeader.d.ts +27 -0
- package/dist/types/src/components/Form/FormField/FormFieldHeader.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/FormRow.d.ts +85 -0
- package/dist/types/src/components/Form/FormField/FormRow.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/{FormFieldWrapper.stories.d.ts → FormRow.stories.d.ts} +4 -1
- package/dist/types/src/components/Form/FormField/FormRow.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts +1 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.d.ts +12 -0
- package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/AutofillField/AutofillField.d.ts +12 -0
- package/dist/types/src/components/Form/FormField/fields/AutofillField/AutofillField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/AutofillField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/AutofillField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/ComboboxField/ComboboxField.d.ts +12 -0
- package/dist/types/src/components/Form/FormField/fields/ComboboxField/ComboboxField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/ComboboxField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/ComboboxField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts +1 -1
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.d.ts +1 -1
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts +11 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.test.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.d.ts +4 -0
- package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.d.ts.map +1 -0
- package/dist/types/src/components/{Settings/Settings.stories.d.ts → Form/FormField/fields/PasswordField/PasswordField.stories.d.ts} +1 -2
- package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/PasswordField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/PasswordField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/InlineRefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts +18 -2
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/fields/index.d.ts +4 -0
- package/dist/types/src/components/Form/FormField/fields/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/index.d.ts +4 -3
- package/dist/types/src/components/Form/FormField/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField/presentation.d.ts +22 -0
- package/dist/types/src/components/Form/FormField/presentation.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldSet/FormFieldSet.d.ts +8 -5
- package/dist/types/src/components/Form/FormFieldSet/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts +22 -0
- package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldSet/index.d.ts +1 -0
- package/dist/types/src/components/Form/FormFieldSet/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts +2 -0
- package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts +5 -5
- package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormLayout/index.d.ts +1 -1
- package/dist/types/src/components/Form/FormLayout/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/meta-tags.d.ts +1 -12
- package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +2 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -1
- package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts +2 -2
- package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +2 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAsyncFieldEffect.d.ts +24 -0
- package/dist/types/src/hooks/useAsyncFieldEffect.d.ts.map +1 -0
- package/dist/types/src/hooks/useFormContext.d.ts +8 -3
- package/dist/types/src/hooks/useFormContext.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts +8 -2
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/TestLayout.d.ts +1 -1
- package/dist/types/src/testing/TestLayout.d.ts.map +1 -1
- package/dist/types/src/testing/schema.d.ts +13 -16
- package/dist/types/src/testing/schema.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +50 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/omit.d.ts +1 -1
- package/dist/types/src/util/omit.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -40
- package/src/annotations.ts +63 -0
- package/src/components/FieldEditor/FieldEditor.tsx +5 -2
- package/src/components/Form/DESIGN.md +24 -0
- package/src/components/Form/Form.stories.tsx +257 -13
- package/src/components/Form/Form.theme.test.ts +33 -0
- package/src/components/Form/Form.theme.ts +86 -0
- package/src/components/Form/Form.tsx +15 -9
- package/src/components/Form/FormControls.tsx +94 -37
- package/src/components/Form/FormField/FormField.tsx +83 -32
- package/src/components/Form/FormField/FormFieldHeader.tsx +68 -0
- package/src/components/Form/FormField/{FormFieldWrapper.stories.tsx → FormRow.stories.tsx} +34 -9
- package/src/components/Form/FormField/FormRow.tsx +298 -0
- package/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.tsx +25 -9
- package/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx +83 -24
- package/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.tsx +59 -0
- package/src/components/Form/FormField/fields/AsyncSelectField/index.ts +5 -0
- package/src/components/Form/FormField/fields/AutofillField/AutofillField.tsx +67 -0
- package/src/components/Form/FormField/fields/AutofillField/index.ts +5 -0
- package/src/components/Form/FormField/fields/BooleanField/BooleanField.tsx +5 -6
- package/src/components/Form/FormField/fields/ComboboxField/ComboboxField.tsx +104 -0
- package/src/components/Form/FormField/fields/ComboboxField/index.ts +5 -0
- package/src/components/Form/FormField/fields/DateField/DateField.tsx +9 -9
- package/src/components/Form/FormField/fields/GeoPointField/GeoPointField.tsx +17 -20
- package/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.tsx +2 -2
- package/src/components/Form/FormField/fields/MarkdownField/MarkdownField.tsx +39 -40
- package/src/components/Form/FormField/fields/NumberField/NumberField.test.ts +29 -0
- package/src/components/Form/FormField/fields/NumberField/NumberField.tsx +60 -21
- package/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.tsx +53 -0
- package/src/components/Form/FormField/fields/PasswordField/PasswordField.tsx +41 -0
- package/src/components/Form/FormField/fields/PasswordField/index.ts +5 -0
- package/src/components/Form/FormField/fields/RefField/InlineRefField.tsx +16 -10
- package/src/components/Form/FormField/fields/RefField/RefField.test.ts +8 -0
- package/src/components/Form/FormField/fields/RefField/RefField.tsx +50 -23
- package/src/components/Form/FormField/fields/SelectField/SelectField.tsx +104 -47
- package/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.tsx +3 -3
- package/src/components/Form/FormField/fields/TextAreaField/TextAreaField.tsx +3 -3
- package/src/components/Form/FormField/fields/TextField/TextField.tsx +3 -3
- package/src/components/Form/FormField/fields/TupleField/TupleField.tsx +1 -1
- package/src/components/Form/FormField/fields/index.ts +4 -0
- package/src/components/Form/FormField/index.ts +5 -3
- package/src/components/Form/FormField/presentation.tsx +32 -0
- package/src/components/Form/FormFieldSet/FormFieldSet.tsx +49 -70
- package/src/components/Form/FormFieldSet/FormFieldSetContainer.tsx +93 -0
- package/src/components/Form/FormFieldSet/index.ts +1 -0
- package/src/components/Form/FormLayout/FormLayout.stories.tsx +7 -5
- package/src/components/Form/FormLayout/FormLayout.tsx +24 -14
- package/src/components/Form/FormLayout/index.ts +1 -1
- package/src/components/Form/meta-tags.test.ts +1 -1
- package/src/components/ObjectForm/ObjectForm.tsx +2 -2
- package/src/components/ObjectPicker/ObjectPicker.tsx +1 -0
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +1 -1
- package/src/components/ObjectProperties/ObjectProperties.tsx +12 -8
- package/src/components/RefEditor/RefEditor.stories.tsx +2 -2
- package/src/components/RefEditor/RefEditor.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +2 -2
- package/src/components/ViewEditor/ViewEditor.test.tsx +8 -2
- package/src/components/ViewEditor/ViewEditor.tsx +3 -3
- package/src/components/index.ts +0 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAsyncFieldEffect.ts +81 -0
- package/src/hooks/useFormContext.ts +6 -3
- package/src/hooks/useFormHandler.ts +11 -6
- package/src/index.ts +1 -0
- package/src/testing/TestLayout.tsx +15 -12
- package/src/testing/schema.ts +43 -43
- package/src/types.ts +53 -1
- package/src/util/index.ts +0 -1
- package/src/util/omit.ts +25 -11
- package/src/util/properties.test.ts +110 -28
- package/src/util/refs.ts +1 -1
- package/dist/types/src/components/Form/FormField/FieldHeader.d.ts +0 -19
- package/dist/types/src/components/Form/FormField/FieldHeader.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormField/FormFieldWrapper.d.ts +0 -47
- package/dist/types/src/components/Form/FormField/FormFieldWrapper.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormField/FormFieldWrapper.stories.d.ts.map +0 -1
- package/dist/types/src/components/Settings/Settings.d.ts +0 -51
- package/dist/types/src/components/Settings/Settings.d.ts.map +0 -1
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +0 -1
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +0 -43
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +0 -1
- package/dist/types/src/components/Settings/SettingsItem.d.ts +0 -16
- package/dist/types/src/components/Settings/SettingsItem.d.ts.map +0 -1
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts +0 -18
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +0 -1
- package/dist/types/src/components/Settings/index.d.ts +0 -4
- package/dist/types/src/components/Settings/index.d.ts.map +0 -1
- package/dist/types/src/util/field-type.d.ts +0 -19
- package/dist/types/src/util/field-type.d.ts.map +0 -1
- package/src/components/Form/FormField/FieldHeader.tsx +0 -36
- package/src/components/Form/FormField/FormFieldWrapper.tsx +0 -179
- package/src/components/Settings/AUDIT.md +0 -51
- package/src/components/Settings/Settings.stories.tsx +0 -104
- package/src/components/Settings/Settings.tsx +0 -81
- package/src/components/Settings/SettingsFieldSet.tsx +0 -257
- package/src/components/Settings/SettingsItem.tsx +0 -58
- package/src/components/Settings/SettingsSelectField.tsx +0 -59
- package/src/components/Settings/index.ts +0 -7
- package/src/util/field-type.ts +0 -60
|
@@ -14,7 +14,7 @@ import { hues, osTranslations } from '@dxos/ui-theme';
|
|
|
14
14
|
import { translationKey } from '#translations';
|
|
15
15
|
import { type FormFieldRendererProps } from '#types';
|
|
16
16
|
|
|
17
|
-
import { FormFieldLabel } from '../../
|
|
17
|
+
import { FormFieldLabel } from '../../FormRow';
|
|
18
18
|
|
|
19
19
|
export const SelectOptionField = ({
|
|
20
20
|
type,
|
|
@@ -145,7 +145,7 @@ export const SelectOptionField = ({
|
|
|
145
145
|
<OrderedList.DragHandle />
|
|
146
146
|
{/* TODO(ZaymonFC): Move spacer into Tag component. */}
|
|
147
147
|
<div className='flex grow items-center truncate px-2' onClick={() => handleClick(item.id)}>
|
|
148
|
-
<Tag
|
|
148
|
+
<Tag hue={item.color as ChromaticPalette}>{item.title || '\u200b'}</Tag>
|
|
149
149
|
</div>
|
|
150
150
|
<ToggleIconButton
|
|
151
151
|
iconOnly
|
|
@@ -183,12 +183,12 @@ export const SelectOptionField = ({
|
|
|
183
183
|
</OrderedList.Item>
|
|
184
184
|
))}
|
|
185
185
|
<IconButton
|
|
186
|
+
classNames='w-full'
|
|
186
187
|
variant='ghost'
|
|
187
188
|
icon='ph--plus--regular'
|
|
188
189
|
label={t('select-option-add.button')}
|
|
189
190
|
onClick={handleAdd}
|
|
190
191
|
disabled={!!readonly}
|
|
191
|
-
classNames='w-full'
|
|
192
192
|
/>
|
|
193
193
|
</OrderedList.Content>
|
|
194
194
|
)}
|
|
@@ -8,7 +8,7 @@ import { Input, type TextAreaProps } from '@dxos/react-ui';
|
|
|
8
8
|
|
|
9
9
|
import { type FormFieldRendererProps } from '#types';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { FormRow } from '../../FormRow';
|
|
12
12
|
|
|
13
13
|
export const TextAreaField = ({
|
|
14
14
|
type,
|
|
@@ -24,7 +24,7 @@ export const TextAreaField = ({
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
<
|
|
27
|
+
<FormRow<string> readonly={readonly} {...props}>
|
|
28
28
|
{({ value = '' }) => (
|
|
29
29
|
<Input.TextArea
|
|
30
30
|
rows={5}
|
|
@@ -35,6 +35,6 @@ export const TextAreaField = ({
|
|
|
35
35
|
onBlur={onBlur}
|
|
36
36
|
/>
|
|
37
37
|
)}
|
|
38
|
-
</
|
|
38
|
+
</FormRow>
|
|
39
39
|
);
|
|
40
40
|
};
|
|
@@ -8,7 +8,7 @@ import { Input, type TextInputProps } from '@dxos/react-ui';
|
|
|
8
8
|
|
|
9
9
|
import { type FormFieldRendererProps } from '#types';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { FormRow } from '../../FormRow';
|
|
12
12
|
|
|
13
13
|
export const TextField = ({
|
|
14
14
|
type,
|
|
@@ -24,7 +24,7 @@ export const TextField = ({
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
<
|
|
27
|
+
<FormRow<string> readonly={readonly} {...props}>
|
|
28
28
|
{({ value = '' }) => (
|
|
29
29
|
<Input.TextInput
|
|
30
30
|
noAutoFill
|
|
@@ -35,6 +35,6 @@ export const TextField = ({
|
|
|
35
35
|
onChange={handleChange}
|
|
36
36
|
/>
|
|
37
37
|
)}
|
|
38
|
-
</
|
|
38
|
+
</FormRow>
|
|
39
39
|
);
|
|
40
40
|
};
|
|
@@ -10,7 +10,7 @@ import { safeParseFloat } from '@dxos/util';
|
|
|
10
10
|
|
|
11
11
|
import { type FormFieldRendererProps } from '#types';
|
|
12
12
|
|
|
13
|
-
import { FormFieldLabel } from '../../
|
|
13
|
+
import { FormFieldLabel } from '../../FormRow';
|
|
14
14
|
|
|
15
15
|
const gridCols = ['grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4'];
|
|
16
16
|
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
export * from './ArrayField';
|
|
6
|
+
export * from './AsyncSelectField';
|
|
7
|
+
export * from './AutofillField';
|
|
6
8
|
export * from './BooleanField';
|
|
9
|
+
export * from './ComboboxField';
|
|
7
10
|
export * from './DateField';
|
|
8
11
|
export * from './GeoPointField';
|
|
9
12
|
export * from './MarkdownField';
|
|
10
13
|
export * from './NumberField';
|
|
14
|
+
export * from './PasswordField';
|
|
11
15
|
export * from './RefField';
|
|
12
16
|
export * from './SelectField';
|
|
13
17
|
export * from './SelectOptionField';
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './FieldHeader';
|
|
6
|
-
export * from './FormField';
|
|
7
5
|
export * from './fields';
|
|
8
|
-
export * from './
|
|
6
|
+
export * from './presentation';
|
|
7
|
+
|
|
8
|
+
export * from './FormFieldHeader';
|
|
9
|
+
export * from './FormField';
|
|
10
|
+
export * from './FormRow';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type FormPresentation } from '#types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolved presentation strategy for a field — which parts render, derived from {@link FormPresentation}.
|
|
9
|
+
* Visual chrome (borders, grid, spacing) is owned by the Form theme recipe, not here.
|
|
10
|
+
*/
|
|
11
|
+
export type FormFieldPresentation = {
|
|
12
|
+
layout?: FormPresentation;
|
|
13
|
+
/** Render the value as read-only plain DOM rather than an editable control. */
|
|
14
|
+
isStatic: boolean;
|
|
15
|
+
/** Render the field's label row. */
|
|
16
|
+
showLabel: boolean;
|
|
17
|
+
/** Render the inline validation/error block beneath the control. */
|
|
18
|
+
showError: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Derive the {@link FormFieldPresentation} for a layout.
|
|
23
|
+
* - `static` renders read-only plain DOM.
|
|
24
|
+
* - `inline` drops the label (the control stands alone, e.g. an array row).
|
|
25
|
+
* - `full` adds the inline error block.
|
|
26
|
+
*/
|
|
27
|
+
export const presentationFor = (layout?: FormPresentation): FormFieldPresentation => ({
|
|
28
|
+
layout,
|
|
29
|
+
isStatic: layout === 'static',
|
|
30
|
+
showLabel: layout !== 'inline',
|
|
31
|
+
showError: layout === 'full',
|
|
32
|
+
});
|
|
@@ -3,65 +3,66 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Option from 'effect/Option';
|
|
6
|
-
import React, { useMemo
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { DEFAULT_LAYOUT_NAME, FormLayoutAnnotation } from '@dxos/echo/Annotation';
|
|
9
9
|
import { type AnyProperties } from '@dxos/echo/internal';
|
|
10
10
|
import { SchemaEx } from '@dxos/effect';
|
|
11
|
-
import {
|
|
11
|
+
import { type Merge } from '@dxos/util';
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import { type CreateOptions, type FormFieldOptions, type RefFieldDataProps } from '#types';
|
|
13
|
+
import { type FieldContext } from '#types';
|
|
15
14
|
|
|
16
15
|
import { type FormHandlerProps, useFormValues } from '../../../hooks';
|
|
17
16
|
import { getRootFormProperties } from '../../../util';
|
|
18
|
-
import { FormField, FormFieldErrorBoundary,
|
|
17
|
+
import { FormField, FormFieldErrorBoundary, type FormFieldProps, presentationFor } from '../FormField';
|
|
19
18
|
import { FormLayout } from '../FormLayout';
|
|
19
|
+
import { FormFieldSetContainer } from './FormFieldSetContainer';
|
|
20
20
|
|
|
21
21
|
const FORM_FIELDSET_NAME = 'Form.FieldSet';
|
|
22
22
|
|
|
23
|
-
export type FormFieldSetProps<T extends AnyProperties> =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Pick<
|
|
23
|
+
export type FormFieldSetProps<T extends AnyProperties> = Merge<
|
|
24
|
+
{
|
|
25
|
+
/** Applied to the field set's container (the bordered box when collapsible). */
|
|
26
|
+
classNames?: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
sort?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* When set, renders a toggle button at the end of the label row that
|
|
31
|
+
* shows/hides the field set body. Used for nested objects.
|
|
32
|
+
*/
|
|
33
|
+
collapsible?: boolean;
|
|
34
|
+
filter?: (props: SchemaEx.SchemaProperty[]) => SchemaEx.SchemaProperty[];
|
|
35
|
+
/**
|
|
36
|
+
* Picks a named layout out of `FormLayoutAnnotation` when present.
|
|
37
|
+
* Falls back to `'default'`. Ignored when the schema has no annotation
|
|
38
|
+
* (linear rendering then takes over).
|
|
39
|
+
*/
|
|
40
|
+
layoutName?: string;
|
|
41
|
+
},
|
|
42
|
+
Pick<FormHandlerProps<T>, 'schema'>,
|
|
43
|
+
Pick<FormFieldProps, 'path' | 'autoFocus'>,
|
|
44
|
+
FieldContext
|
|
45
|
+
>;
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
48
|
* Renders a set of form fields derived from a schema object.
|
|
46
49
|
*/
|
|
47
50
|
export const FormFieldSet = ({
|
|
51
|
+
classNames,
|
|
48
52
|
label,
|
|
49
53
|
schema,
|
|
50
54
|
readonly,
|
|
51
55
|
path,
|
|
52
56
|
sort,
|
|
53
57
|
collapsible,
|
|
54
|
-
|
|
58
|
+
filter,
|
|
55
59
|
projection,
|
|
56
60
|
layout,
|
|
57
61
|
layoutName = DEFAULT_LAYOUT_NAME,
|
|
58
62
|
...props
|
|
59
63
|
}: FormFieldSetProps<any>) => {
|
|
60
|
-
const { t } = useTranslation(translationKey);
|
|
61
64
|
const values = useFormValues(FORM_FIELDSET_NAME, path);
|
|
62
|
-
const properties = useFormFieldSetProperties({ schema, values,
|
|
63
|
-
// TODO(burdon): Generalize collapse state (cf. useSelection in react-ui-attention, plugin-markdown cursor state).
|
|
64
|
-
const [collapsed, setCollapsed] = useState(false);
|
|
65
|
+
const properties = useFormFieldSetProperties({ schema, values, filter, sort, projection });
|
|
65
66
|
if ((readonly || layout === 'static') && values == null) {
|
|
66
67
|
return null;
|
|
67
68
|
}
|
|
@@ -89,6 +90,7 @@ export const FormFieldSet = ({
|
|
|
89
90
|
type={property.type}
|
|
90
91
|
name={name}
|
|
91
92
|
path={[...(path ?? []), name]}
|
|
93
|
+
required={!property.isOptional}
|
|
92
94
|
readonly={readonly}
|
|
93
95
|
layout={layout}
|
|
94
96
|
projection={projection}
|
|
@@ -99,56 +101,33 @@ export const FormFieldSet = ({
|
|
|
99
101
|
})
|
|
100
102
|
);
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<ToggleIconButton
|
|
114
|
-
active={!collapsed}
|
|
115
|
-
classNames='px-1 mr-0.5'
|
|
116
|
-
variant='ghost'
|
|
117
|
-
density='xs'
|
|
118
|
-
iconOnly
|
|
119
|
-
icon='ph--caret-right--regular'
|
|
120
|
-
label={t(collapsed ? 'expand-fields.label' : 'collapse-fields.label')}
|
|
121
|
-
/>
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
onClick={() => setCollapsed((value) => !value)}
|
|
125
|
-
/>
|
|
126
|
-
)}
|
|
127
|
-
{showBody && (collapsible ? <div className='px-2 pb-2'>{body}</div> : body)}
|
|
128
|
-
</>
|
|
104
|
+
return (
|
|
105
|
+
<FormFieldSetContainer
|
|
106
|
+
classNames={classNames}
|
|
107
|
+
label={label}
|
|
108
|
+
path={SchemaEx.createJsonPath(path ?? [])}
|
|
109
|
+
readonly={readonly}
|
|
110
|
+
presentation={presentationFor(layout)}
|
|
111
|
+
collapsible={collapsible}
|
|
112
|
+
>
|
|
113
|
+
{body}
|
|
114
|
+
</FormFieldSetContainer>
|
|
129
115
|
);
|
|
130
|
-
|
|
131
|
-
// Nested field sets render inside an indented, bordered container with a collapse toggle.
|
|
132
|
-
if (collapsible) {
|
|
133
|
-
return <div className='border border-subdued-separator rounded-sm mb-1'>{content}</div>;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return content;
|
|
137
116
|
};
|
|
138
117
|
|
|
139
118
|
FormFieldSet.displayName = FORM_FIELDSET_NAME;
|
|
140
119
|
|
|
141
|
-
type UseFormFieldSetPropertiesParams = Pick<FormFieldSetProps<any>, 'schema' | '
|
|
120
|
+
type UseFormFieldSetPropertiesParams = Pick<FormFieldSetProps<any>, 'schema' | 'filter' | 'projection' | 'sort'> & {
|
|
142
121
|
values: AnyProperties | undefined;
|
|
143
122
|
sort?: string[];
|
|
144
123
|
};
|
|
145
124
|
|
|
146
125
|
/**
|
|
147
|
-
* Resolves ordered schema properties for a field set (projection order,
|
|
126
|
+
* Resolves ordered schema properties for a field set (projection order, filter, or sort).
|
|
148
127
|
*/
|
|
149
128
|
const useFormFieldSetProperties = ({
|
|
150
129
|
schema,
|
|
151
|
-
|
|
130
|
+
filter,
|
|
152
131
|
projection,
|
|
153
132
|
values,
|
|
154
133
|
sort,
|
|
@@ -160,9 +139,9 @@ const useFormFieldSetProperties = ({
|
|
|
160
139
|
return [];
|
|
161
140
|
}
|
|
162
141
|
|
|
163
|
-
// TODO(wittjosiah): Reconcile FormInputAnnotation with projection hidden properties &
|
|
142
|
+
// TODO(wittjosiah): Reconcile FormInputAnnotation with projection hidden properties & filter function.
|
|
164
143
|
const schemaProps = getRootFormProperties(schema.ast, values);
|
|
165
|
-
const filteredProps =
|
|
144
|
+
const filteredProps = filter ? filter(schemaProps) : schemaProps;
|
|
166
145
|
|
|
167
146
|
// Use projection-based field management when view and projection are available.
|
|
168
147
|
if (projection) {
|
|
@@ -193,5 +172,5 @@ const useFormFieldSetProperties = ({
|
|
|
193
172
|
return sort
|
|
194
173
|
? [...filteredProps].sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString()))
|
|
195
174
|
: filteredProps;
|
|
196
|
-
}, [schema, values,
|
|
175
|
+
}, [schema, values, filter, sort, projection]);
|
|
197
176
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type PropsWithChildren, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ThemedClassName, ToggleIconButton, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { mx } from '@dxos/ui-theme';
|
|
9
|
+
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
|
+
|
|
12
|
+
import { formTheme } from '../Form.theme';
|
|
13
|
+
import { FormFieldHeader } from '../FormField/FormFieldHeader';
|
|
14
|
+
import { type FormFieldPresentation } from '../FormField/presentation';
|
|
15
|
+
|
|
16
|
+
const styles = formTheme.styles();
|
|
17
|
+
|
|
18
|
+
export type FormFieldSetContainerProps = ThemedClassName<
|
|
19
|
+
PropsWithChildren<{
|
|
20
|
+
label?: string;
|
|
21
|
+
/** JSON path of the group, forwarded to the label as field metadata. */
|
|
22
|
+
path?: string;
|
|
23
|
+
readonly?: boolean;
|
|
24
|
+
presentation: FormFieldPresentation;
|
|
25
|
+
/**
|
|
26
|
+
* Render a collapse toggle in the header and wrap the body in an indented, bordered box.
|
|
27
|
+
* Used for nested objects (struct fields, object-array items, inline refs).
|
|
28
|
+
*/
|
|
29
|
+
collapsible?: boolean;
|
|
30
|
+
}>
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Shared chrome for a labelled group of sub-fields: an optional header (label + collapse toggle) and,
|
|
35
|
+
* when collapsible, an indented bordered container around the body. Nested structs, object-array items,
|
|
36
|
+
* and inline refs all reach this via `FormFieldSet`, so their visual containment is identical.
|
|
37
|
+
*/
|
|
38
|
+
export const FormFieldSetContainer = ({
|
|
39
|
+
classNames,
|
|
40
|
+
label,
|
|
41
|
+
path,
|
|
42
|
+
readonly,
|
|
43
|
+
presentation,
|
|
44
|
+
collapsible,
|
|
45
|
+
children,
|
|
46
|
+
}: FormFieldSetContainerProps) => {
|
|
47
|
+
const { t } = useTranslation(translationKey);
|
|
48
|
+
// TODO(burdon): Generalize collapse state (cf. useSelection in react-ui-attention, plugin-markdown cursor state).
|
|
49
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
50
|
+
const showBody = !(collapsible && collapsed);
|
|
51
|
+
|
|
52
|
+
const content = (
|
|
53
|
+
<>
|
|
54
|
+
{presentation.showLabel && label && (
|
|
55
|
+
<FormFieldHeader
|
|
56
|
+
label={label}
|
|
57
|
+
path={path}
|
|
58
|
+
readonly={readonly}
|
|
59
|
+
classNames='pl-2'
|
|
60
|
+
actions={
|
|
61
|
+
collapsible ? (
|
|
62
|
+
<ToggleIconButton
|
|
63
|
+
active={!collapsed}
|
|
64
|
+
classNames='px-1 mr-0.5'
|
|
65
|
+
variant='ghost'
|
|
66
|
+
density='xs'
|
|
67
|
+
iconOnly
|
|
68
|
+
icon='ph--caret-right--regular'
|
|
69
|
+
label={t(collapsed ? 'expand-fields.label' : 'collapse-fields.label')}
|
|
70
|
+
/>
|
|
71
|
+
) : undefined
|
|
72
|
+
}
|
|
73
|
+
onClick={collapsible ? () => setCollapsed((value) => !value) : undefined}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
{showBody && (collapsible ? <div className={styles.fieldSetBody()}>{children}</div> : children)}
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Nested groups render inside an indented, bordered container with a collapse toggle. A non-collapsible
|
|
81
|
+
// group only materializes a wrapper when `classNames` is supplied — otherwise the body flows straight
|
|
82
|
+
// into the parent grid (the default, grid-transparent behavior).
|
|
83
|
+
if (collapsible) {
|
|
84
|
+
return (
|
|
85
|
+
<div className={styles.fieldSetBoxOuter()}>
|
|
86
|
+
<div className={styles.fieldSetBox({ class: mx(classNames) })}>{content}</div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Only materialize a wrapper when `classNames` is supplied; otherwise stay grid-transparent.
|
|
92
|
+
return classNames ? <div className={mx(classNames)}>{content}</div> : <>{content}</>;
|
|
93
|
+
};
|
|
@@ -125,12 +125,12 @@ const JOURNEY_LAYOUT = trim`
|
|
|
125
125
|
</grid>
|
|
126
126
|
`;
|
|
127
127
|
|
|
128
|
-
type
|
|
128
|
+
type StoryArgs = {
|
|
129
129
|
schema: Schema.Schema<any>;
|
|
130
130
|
template?: string;
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
const DefaultStory = ({ schema, template }:
|
|
133
|
+
const DefaultStory = ({ schema, template }: StoryArgs) => {
|
|
134
134
|
const [values, setValues] = useState<Partial<FlightValues>>(flight);
|
|
135
135
|
|
|
136
136
|
const handleSave = useCallback<NonNullable<FormRootProps<any>['onSave']>>((next) => {
|
|
@@ -164,11 +164,11 @@ const meta = {
|
|
|
164
164
|
layout: 'fullscreen',
|
|
165
165
|
translations,
|
|
166
166
|
},
|
|
167
|
-
} satisfies Meta<
|
|
167
|
+
} satisfies Meta<StoryArgs>;
|
|
168
168
|
|
|
169
169
|
export default meta;
|
|
170
170
|
|
|
171
|
-
type Story = StoryObj<
|
|
171
|
+
type Story = StoryObj<StoryArgs>;
|
|
172
172
|
|
|
173
173
|
/** Baseline: no annotation, no override — `Form.FieldSet` renders linearly (one field per row). */
|
|
174
174
|
export const Linear: Story = {
|
|
@@ -383,7 +383,9 @@ const PlaygroundStory = ({ card = false }: PlaygroundStoryProps) => {
|
|
|
383
383
|
onChange={setTemplate}
|
|
384
384
|
/>
|
|
385
385
|
</Editor.Root>
|
|
386
|
-
{error &&
|
|
386
|
+
{error && (
|
|
387
|
+
<div className='border-t border-input-separator p-2 text-sm text-error-text font-mono'>{error}</div>
|
|
388
|
+
)}
|
|
387
389
|
</div>
|
|
388
390
|
</TestPanel>
|
|
389
391
|
<TestPanel>
|
|
@@ -15,10 +15,10 @@ import { Input } from '@dxos/react-ui';
|
|
|
15
15
|
|
|
16
16
|
import { type FormPresentation } from '#types';
|
|
17
17
|
|
|
18
|
-
import { useFormFieldState } from '../../../hooks';
|
|
19
|
-
import {
|
|
20
|
-
import { FormFieldErrorBoundary, FormFieldLabel } from '../FormField';
|
|
21
|
-
import {
|
|
18
|
+
import { useFormContext, useFormFieldState } from '../../../hooks';
|
|
19
|
+
import { formTheme } from '../Form.theme';
|
|
20
|
+
import { FormField, FormFieldErrorBoundary, FormFieldLabel, type FormFieldProps, presentationFor } from '../FormField';
|
|
21
|
+
import { type LayoutNode, LayoutParseError, parseLayout } from './parser';
|
|
22
22
|
|
|
23
23
|
const FORM_LAYOUT_NAME = 'Form.Layout';
|
|
24
24
|
|
|
@@ -115,7 +115,7 @@ const RenderNode = ({ node, schema, basePath, ...props }: RenderNodeProps) => {
|
|
|
115
115
|
throw new LayoutParseError(`field "${node.name}" not found on schema`);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const { type, segments, leafName, title, labelType } = resolved;
|
|
118
|
+
const { type, segments, leafName, title, labelType, required } = resolved;
|
|
119
119
|
const path = [...basePath, ...segments];
|
|
120
120
|
const span = node.span ? { gridColumn: `span ${node.span} / span ${node.span}` } : undefined;
|
|
121
121
|
|
|
@@ -130,7 +130,7 @@ const RenderNode = ({ node, schema, basePath, ...props }: RenderNodeProps) => {
|
|
|
130
130
|
layout={props.layout}
|
|
131
131
|
/>
|
|
132
132
|
) : (
|
|
133
|
-
<FormField type={type} name={leafName} path={path} {...props} />
|
|
133
|
+
<FormField type={type} name={leafName} path={path} required={required} {...props} />
|
|
134
134
|
)}
|
|
135
135
|
</FormFieldErrorBoundary>
|
|
136
136
|
</div>
|
|
@@ -152,6 +152,8 @@ export type ResolvedLayoutField = {
|
|
|
152
152
|
* sub-fields. Undefined for ordinary (non-auto-labelled) fields.
|
|
153
153
|
*/
|
|
154
154
|
labelType?: SchemaAST.AST;
|
|
155
|
+
/** Whether the resolved property is required (non-optional). */
|
|
156
|
+
required: boolean;
|
|
155
157
|
};
|
|
156
158
|
|
|
157
159
|
/**
|
|
@@ -216,6 +218,7 @@ export const resolveLayoutField = (schema: Schema.Schema<any>, name: string): Re
|
|
|
216
218
|
leafName: segments[segments.length - 1],
|
|
217
219
|
title,
|
|
218
220
|
labelType: labelled ? labelType : undefined,
|
|
221
|
+
required: !prop.isOptional,
|
|
219
222
|
};
|
|
220
223
|
};
|
|
221
224
|
|
|
@@ -232,6 +235,8 @@ type LabelFieldProps = {
|
|
|
232
235
|
* presentation of regular fields.
|
|
233
236
|
*/
|
|
234
237
|
const LabelField = ({ schema, label, path, layout }: LabelFieldProps) => {
|
|
238
|
+
const { variant = 'default' } = useFormContext(FORM_LAYOUT_NAME);
|
|
239
|
+
const styles = formTheme.styles({ variant });
|
|
235
240
|
const { getValue } = useFormFieldState(FORM_LAYOUT_NAME, path);
|
|
236
241
|
const value = getValue();
|
|
237
242
|
const text = value == null ? undefined : Annotation.getLabelWithSchema(schema, value);
|
|
@@ -239,14 +244,19 @@ const LabelField = ({ schema, label, path, layout }: LabelFieldProps) => {
|
|
|
239
244
|
return null;
|
|
240
245
|
}
|
|
241
246
|
|
|
247
|
+
const presentation = presentationFor(layout);
|
|
242
248
|
return (
|
|
243
|
-
<
|
|
244
|
-
<
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
<Input.Root>
|
|
250
|
+
<div className={styles.field()}>
|
|
251
|
+
{presentation.showLabel && (
|
|
252
|
+
<FormFieldLabel variant={variant} readonly label={label} path={SchemaEx.createJsonPath(path)} />
|
|
253
|
+
)}
|
|
254
|
+
<div className={styles.fieldControl()}>
|
|
255
|
+
<p className='truncate min-w-0' title={text}>
|
|
256
|
+
{text}
|
|
257
|
+
</p>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</Input.Root>
|
|
251
261
|
);
|
|
252
262
|
};
|
|
@@ -11,7 +11,7 @@ import { Obj, Ref, Tag } from '@dxos/echo';
|
|
|
11
11
|
import { META_TAGS_KEY, withMetaTags } from './meta-tags';
|
|
12
12
|
|
|
13
13
|
// A feed-host schema whose own `tags` field is a `TagIndex` record (tagId -> objectId[]), not an
|
|
14
|
-
// array — mirrors plugin-commerce `Search`, plugin-inbox `Mailbox`, plugin-
|
|
14
|
+
// array — mirrors plugin-commerce `Search`, plugin-inbox `Mailbox`, plugin-magazine `Subscription`.
|
|
15
15
|
const TagIndexHost = Schema.Struct({
|
|
16
16
|
name: Schema.String.pipe(Schema.optional),
|
|
17
17
|
tags: Schema.Record({ key: Schema.String, value: Schema.Array(Obj.ID) }).pipe(Schema.optional),
|
|
@@ -99,12 +99,12 @@ export const ObjectForm = ({ object, type }: ObjectFormProps) => {
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
const createFieldMap: FormFieldMap = {
|
|
102
|
-
hue: ({ type, label,
|
|
102
|
+
hue: ({ type, label, presentation, getValue, onValueChange }) => {
|
|
103
103
|
const handleChange = useCallback((nextHue: string) => onValueChange(type, nextHue), [onValueChange, type]);
|
|
104
104
|
const handleReset = useCallback(() => onValueChange(type, undefined), [onValueChange, type]);
|
|
105
105
|
return (
|
|
106
106
|
<>
|
|
107
|
-
{
|
|
107
|
+
{presentation !== 'inline' && <Form.Label label={label} />}
|
|
108
108
|
<HuePicker value={getValue()} onChange={handleChange} onReset={handleReset} />
|
|
109
109
|
</>
|
|
110
110
|
);
|
|
@@ -129,6 +129,7 @@ const ObjectPickerContent = forwardRef<HTMLDivElement, ObjectPickerContentProps>
|
|
|
129
129
|
key={option.id}
|
|
130
130
|
value={option.id}
|
|
131
131
|
label={option.label}
|
|
132
|
+
description={option.description}
|
|
132
133
|
checked={selectedIds?.includes(option.id)}
|
|
133
134
|
onSelect={() => onSelect?.(option.id)}
|
|
134
135
|
classNames='flex items-center gap-2'
|
|
@@ -7,7 +7,7 @@ import * as Schema from 'effect/Schema';
|
|
|
7
7
|
import React, { useEffect } from 'react';
|
|
8
8
|
import { expect, userEvent, within } from 'storybook/test';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Annotation, DXN, Filter, Obj, Ref, Tag, Type } from '@dxos/echo';
|
|
11
11
|
import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';
|
|
12
12
|
import { useQuery } from '@dxos/react-client/echo';
|
|
13
13
|
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|