@dxos/react-ui-form 0.9.0 → 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 +2337 -1919
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +2337 -1919
- 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 +3 -3
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +23 -110
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +48 -101
- 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 +75 -0
- package/dist/types/src/components/Form/FormControls.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/FormField.d.ts +44 -0
- package/dist/types/src/components/Form/FormField/FormField.d.ts.map +1 -0
- 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/{FormFieldComponent.stories.d.ts → FormField/FormRow.stories.d.ts} +6 -3
- package/dist/types/src/components/Form/FormField/FormRow.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/{fields → FormField/fields/ArrayField}/ArrayField.d.ts +2 -2
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts.map +1 -0
- 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 +4 -0
- package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.d.ts.map +1 -0
- package/dist/types/src/components/{Settings/Settings.stories.d.ts → Form/FormField/fields/BooleanField/BooleanField.stories.d.ts} +1 -2
- package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/BooleanField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/BooleanField/index.d.ts.map +1 -0
- 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/DateField/DateField.d.ts +4 -0
- package/dist/types/src/components/Form/FormField/fields/DateField/DateField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/DateField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/DateField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts +5 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/GeoPointField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.d.ts +2 -2
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/MarkdownField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts +15 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts.map +1 -0
- 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/NumberField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/NumberField/index.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/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts +88 -0
- 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 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts +16 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts +92 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/RefField.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts +3 -0
- package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts +29 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.d.ts +5 -0
- package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/SelectOptionField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/SelectOptionField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.d.ts +4 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/TextAreaField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/TextField.d.ts +4 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/TextField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/TextField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/{fields → FormField/fields/TupleField}/TupleField.d.ts +2 -2
- package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts +88 -0
- package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/fields/TupleField/index.d.ts +2 -0
- package/dist/types/src/components/Form/FormField/fields/TupleField/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/{fields → FormField/fields}/index.d.ts +4 -1
- package/dist/types/src/components/Form/FormField/fields/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormField/index.d.ts +6 -0
- package/dist/types/src/components/Form/FormField/index.d.ts.map +1 -0
- 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 +30 -0
- package/dist/types/src/components/Form/FormFieldSet/FormFieldSet.d.ts.map +1 -0
- 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 +3 -0
- package/dist/types/src/components/Form/FormFieldSet/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.d.ts +2 -0
- package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts.map +1 -0
- package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.stories.d.ts +5 -5
- package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormLayout/FormLayout.stories.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormLayout/FormLayout.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormLayout/index.d.ts +3 -0
- package/dist/types/src/components/Form/FormLayout/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormLayout/parser.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormLayout/parser.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +3 -5
- package/dist/types/src/components/Form/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/ObjectForm/ObjectForm.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +6 -23
- 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 +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 +2 -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 +42 -0
- package/dist/types/src/hooks/useFormContext.d.ts.map +1 -0
- package/dist/types/src/hooks/useFormHandler.d.ts +9 -6
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/{components/testing.d.ts → testing/TestLayout.d.ts} +2 -2
- package/dist/types/src/testing/TestLayout.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +3 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/schema.d.ts +40 -0
- package/dist/types/src/testing/schema.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +161 -0
- package/dist/types/src/types.d.ts.map +1 -0
- 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/omit.d.ts +12 -0
- package/dist/types/src/util/omit.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -41
- package/src/annotations.ts +63 -0
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.test.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.tsx +10 -6
- package/src/components/Form/DESIGN.md +24 -0
- package/src/components/Form/Form.stories.tsx +260 -61
- 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 +30 -453
- package/src/components/Form/FormControls.tsx +377 -0
- package/src/components/Form/{FormField.tsx → FormField/FormField.tsx} +98 -71
- package/src/components/Form/FormField/FormFieldHeader.tsx +68 -0
- package/src/components/Form/FormField/FormRow.stories.tsx +97 -0
- package/src/components/Form/FormField/FormRow.tsx +298 -0
- package/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.tsx +119 -0
- package/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx +328 -0
- package/src/components/Form/FormField/fields/ArrayField/index.ts +5 -0
- 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.stories.tsx +52 -0
- package/src/components/Form/FormField/fields/BooleanField/BooleanField.tsx +28 -0
- package/src/components/Form/FormField/fields/BooleanField/index.ts +5 -0
- 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.stories.tsx +60 -0
- package/src/components/Form/{fields → FormField/fields/DateField}/DateField.tsx +12 -10
- package/src/components/Form/FormField/fields/DateField/index.ts +5 -0
- package/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.tsx +53 -0
- package/src/components/Form/{fields → FormField/fields/GeoPointField}/GeoPointField.tsx +19 -21
- package/src/components/Form/FormField/fields/GeoPointField/index.ts +5 -0
- package/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.stories.tsx +8 -8
- package/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.tsx +41 -41
- package/src/components/Form/FormField/fields/MarkdownField/index.ts +5 -0
- package/src/components/Form/FormField/fields/NumberField/NumberField.stories.tsx +52 -0
- package/src/components/Form/FormField/fields/NumberField/NumberField.test.ts +29 -0
- package/src/components/Form/FormField/fields/NumberField/NumberField.tsx +140 -0
- package/src/components/Form/FormField/fields/NumberField/index.ts +5 -0
- 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/{fields → FormField/fields/RefField}/InlineRefField.tsx +21 -14
- package/src/components/Form/FormField/fields/RefField/RefField.stories.tsx +97 -0
- package/src/components/Form/{fields → FormField/fields/RefField}/RefField.test.ts +10 -1
- package/src/components/Form/{fields → FormField/fields/RefField}/RefField.tsx +55 -47
- package/src/components/Form/FormField/fields/RefField/index.ts +6 -0
- package/src/components/Form/FormField/fields/SelectField/SelectField.stories.tsx +53 -0
- package/src/components/Form/FormField/fields/SelectField/SelectField.tsx +136 -0
- package/src/components/Form/FormField/fields/SelectField/index.ts +5 -0
- package/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.stories.tsx +5 -5
- package/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.tsx +56 -54
- package/src/components/Form/FormField/fields/SelectOptionField/index.ts +5 -0
- package/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.tsx +53 -0
- package/src/components/Form/{fields → FormField/fields/TextAreaField}/TextAreaField.tsx +6 -4
- package/src/components/Form/FormField/fields/TextAreaField/index.ts +5 -0
- package/src/components/Form/FormField/fields/TextField/TextField.stories.tsx +52 -0
- package/src/components/Form/{fields → FormField/fields/TextField}/TextField.tsx +6 -4
- package/src/components/Form/FormField/fields/TextField/index.ts +5 -0
- package/src/components/Form/FormField/fields/TupleField/TupleField.stories.tsx +55 -0
- package/src/components/Form/{fields → FormField/fields/TupleField}/TupleField.tsx +4 -2
- package/src/components/Form/FormField/fields/TupleField/index.ts +5 -0
- package/src/components/Form/{fields → FormField/fields}/index.ts +4 -1
- package/src/components/Form/FormField/index.ts +10 -0
- package/src/components/Form/FormField/presentation.tsx +32 -0
- package/src/components/Form/{FormFieldSet.tsx → FormFieldSet/FormFieldSet.tsx} +53 -90
- package/src/components/Form/FormFieldSet/FormFieldSetContainer.tsx +93 -0
- package/src/components/Form/FormFieldSet/index.ts +6 -0
- package/src/components/Form/{Layout → FormLayout}/FormLayout.stories.tsx +11 -9
- package/src/components/Form/{Layout → FormLayout}/FormLayout.tsx +29 -18
- package/src/components/Form/FormLayout/index.ts +6 -0
- package/src/components/Form/index.ts +3 -5
- package/src/components/Form/meta-tags.test.ts +1 -1
- package/src/components/Form/meta-tags.ts +1 -1
- package/src/components/ObjectForm/ObjectForm.tsx +15 -14
- package/src/components/ObjectPicker/ObjectPicker.tsx +20 -27
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +2 -2
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +1 -1
- package/src/components/ObjectProperties/ObjectProperties.tsx +13 -8
- package/src/components/RefEditor/RefEditor.stories.tsx +2 -3
- package/src/components/RefEditor/RefEditor.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +6 -7
- package/src/components/ViewEditor/ViewEditor.test.tsx +9 -3
- package/src/components/ViewEditor/ViewEditor.tsx +98 -128
- package/src/components/index.ts +0 -1
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useAsyncFieldEffect.ts +81 -0
- package/src/hooks/useFormContext.ts +89 -0
- package/src/hooks/useFormHandler.ts +12 -10
- package/src/index.ts +2 -0
- package/src/{components/testing.tsx → testing/TestLayout.tsx} +16 -13
- package/src/testing/index.ts +7 -0
- package/src/testing/schema.ts +59 -0
- package/src/types.ts +186 -0
- package/src/util/index.ts +1 -1
- package/src/util/omit.ts +61 -0
- package/src/util/properties.test.ts +110 -28
- package/src/util/refs.ts +1 -1
- package/src/vitest-setup.ts +40 -0
- package/dist/types/src/components/Form/FormField.d.ts +0 -52
- package/dist/types/src/components/Form/FormField.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +0 -98
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +0 -26
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts +0 -19
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/FormLayout.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/annotation.d.ts +0 -30
- package/dist/types/src/components/Form/Layout/annotation.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/index.d.ts +0 -4
- package/dist/types/src/components/Form/Layout/index.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/parser.d.ts.map +0 -1
- package/dist/types/src/components/Form/Layout/parser.test.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/BooleanField.d.ts +0 -4
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/DateField.d.ts +0 -4
- package/dist/types/src/components/Form/fields/DateField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts +0 -5
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/InlineRefField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/NumberField.d.ts +0 -4
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +0 -34
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/RefField.test.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/SelectField.d.ts +0 -13
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +0 -5
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts +0 -4
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/TextField.d.ts +0 -4
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +0 -1
- package/dist/types/src/components/Form/fields/index.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/components/testing.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/AUDIT.md +0 -17
- package/src/components/Form/FormFieldComponent.stories.tsx +0 -65
- package/src/components/Form/FormFieldComponent.tsx +0 -253
- package/src/components/Form/FormTooltipsContext.ts +0 -25
- package/src/components/Form/Layout/annotation.ts +0 -38
- package/src/components/Form/Layout/index.ts +0 -7
- package/src/components/Form/README.md +0 -3
- package/src/components/Form/fields/ArrayField.tsx +0 -173
- package/src/components/Form/fields/BooleanField.tsx +0 -22
- package/src/components/Form/fields/NumberField.tsx +0 -76
- package/src/components/Form/fields/SelectField.tsx +0 -77
- 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
- /package/dist/types/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.stories.d.ts +0 -0
- /package/dist/types/src/components/Form/{fields → FormField/fields/RefField}/InlineRefField.d.ts +0 -0
- /package/dist/types/src/components/Form/{fields → FormField/fields/RefField}/RefField.test.d.ts +0 -0
- /package/dist/types/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.stories.d.ts +0 -0
- /package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.stories.test.d.ts +0 -0
- /package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.test.d.ts +0 -0
- /package/dist/types/src/components/Form/{Layout → FormLayout}/parser.d.ts +0 -0
- /package/dist/types/src/components/Form/{Layout → FormLayout}/parser.test.d.ts +0 -0
- /package/src/components/Form/{Layout → FormLayout}/FormLayout.stories.test.tsx +0 -0
- /package/src/components/Form/{Layout → FormLayout}/FormLayout.test.ts +0 -0
- /package/src/components/Form/{Layout → FormLayout}/parser.test.ts +0 -0
- /package/src/components/Form/{Layout → FormLayout}/parser.ts +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
import { CompactIconButton } from './FormField';
|
|
8
|
+
import { FormFieldLabel } from './FormRow';
|
|
9
|
+
|
|
10
|
+
export type FormFieldHeaderProps = {
|
|
11
|
+
label: string;
|
|
12
|
+
/** JSON path forwarded to the label as field metadata. */
|
|
13
|
+
path?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
readonly?: boolean;
|
|
16
|
+
classNames?: string;
|
|
17
|
+
/** Trailing inline add affordance; omit to hide it. */
|
|
18
|
+
add?: { icon?: string; label: string; disabled?: boolean; onClick: () => void };
|
|
19
|
+
/** Extra trailing controls placed after the add affordance (e.g. a collapse toggle). */
|
|
20
|
+
actions?: ReactNode;
|
|
21
|
+
/** Header row click (e.g. toggle collapse). The add affordance stops propagation so it doesn't trigger this. */
|
|
22
|
+
onClick?: () => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Header row for a labelled group or list: a label with optional trailing controls (an inline add
|
|
27
|
+
* affordance and/or arbitrary `actions`). Shared by array/list fields ({@link ArrayField}, the view
|
|
28
|
+
* editor) and {@link FormFieldSetContainer}'s collapse header, so all group/list headers render identically.
|
|
29
|
+
*/
|
|
30
|
+
export const FormFieldHeader = ({
|
|
31
|
+
label,
|
|
32
|
+
path,
|
|
33
|
+
required,
|
|
34
|
+
readonly,
|
|
35
|
+
classNames,
|
|
36
|
+
add,
|
|
37
|
+
actions,
|
|
38
|
+
onClick,
|
|
39
|
+
}: FormFieldHeaderProps) => (
|
|
40
|
+
<FormFieldLabel
|
|
41
|
+
standalone
|
|
42
|
+
classNames={classNames}
|
|
43
|
+
label={label}
|
|
44
|
+
required={required}
|
|
45
|
+
readonly={readonly}
|
|
46
|
+
path={path}
|
|
47
|
+
button={
|
|
48
|
+
(!readonly && add) || actions ? (
|
|
49
|
+
<>
|
|
50
|
+
{!readonly && add && (
|
|
51
|
+
<CompactIconButton
|
|
52
|
+
disabled={add.disabled}
|
|
53
|
+
icon={add.icon ?? 'ph--plus--regular'}
|
|
54
|
+
label={add.label}
|
|
55
|
+
onClick={(event) => {
|
|
56
|
+
// Don't let the add click bubble to the header's onClick (e.g. collapse toggle).
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
add.onClick();
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
{actions}
|
|
63
|
+
</>
|
|
64
|
+
) : undefined
|
|
65
|
+
}
|
|
66
|
+
onClick={onClick}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { type ChangeEvent, useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Input } from '@dxos/react-ui';
|
|
9
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
10
|
+
|
|
11
|
+
import { type FormFieldRendererProps } from '#types';
|
|
12
|
+
|
|
13
|
+
import { Form } from '../Form';
|
|
14
|
+
import { FormRow } from './FormRow';
|
|
15
|
+
|
|
16
|
+
const DefaultStory = ({
|
|
17
|
+
type,
|
|
18
|
+
placeholder,
|
|
19
|
+
readonly,
|
|
20
|
+
onBlur,
|
|
21
|
+
onValueChange,
|
|
22
|
+
...props
|
|
23
|
+
}: FormFieldRendererProps<string>) => {
|
|
24
|
+
const handleChange = useCallback(
|
|
25
|
+
(event: ChangeEvent<HTMLInputElement>) => onValueChange(type, event.target.value),
|
|
26
|
+
[type, onValueChange],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Form.Root>
|
|
31
|
+
<Form.Viewport>
|
|
32
|
+
<Form.Content>
|
|
33
|
+
<FormRow<string> {...props}>
|
|
34
|
+
{({ value = '' }) => (
|
|
35
|
+
<Input.TextInput
|
|
36
|
+
disabled={!!readonly}
|
|
37
|
+
placeholder={placeholder}
|
|
38
|
+
noAutoFill
|
|
39
|
+
value={value}
|
|
40
|
+
onChange={handleChange}
|
|
41
|
+
onBlur={onBlur}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
</FormRow>
|
|
45
|
+
</Form.Content>
|
|
46
|
+
</Form.Viewport>
|
|
47
|
+
</Form.Root>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const meta: Meta<typeof DefaultStory> = {
|
|
52
|
+
title: 'ui/react-ui-form/FormRow',
|
|
53
|
+
component: DefaultStory,
|
|
54
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
55
|
+
parameters: {
|
|
56
|
+
layout: 'fullscreen',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default meta;
|
|
61
|
+
|
|
62
|
+
type Story = StoryObj<typeof meta>;
|
|
63
|
+
|
|
64
|
+
const props: Story['args'] = {
|
|
65
|
+
label: 'Label',
|
|
66
|
+
required: true,
|
|
67
|
+
getStatus: () => ({ status: 'error', error: 'Required field' }),
|
|
68
|
+
getValue: () => 'DXOS',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const Default: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
...props,
|
|
74
|
+
presentation: 'full',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const Compact: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
...props,
|
|
81
|
+
presentation: 'compact',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const Inline: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
...props,
|
|
88
|
+
presentation: 'inline',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const Static: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
...props,
|
|
95
|
+
presentation: 'static',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { format as formatDate } from 'date-fns';
|
|
6
|
+
import React, { Component, type PropsWithChildren, type ReactNode, type Ref } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Format } from '@dxos/echo';
|
|
9
|
+
import { Icon, Input, type ThemedClassName, Tooltip } from '@dxos/react-ui';
|
|
10
|
+
import { mx } from '@dxos/ui-theme';
|
|
11
|
+
|
|
12
|
+
import { type FormFieldRendererProps } from '#types';
|
|
13
|
+
|
|
14
|
+
import { useFormContext } from '../../../hooks';
|
|
15
|
+
import { type FormVariant, formTheme } from '../Form.theme';
|
|
16
|
+
import { type FormFieldPresentation, presentationFor } from './presentation';
|
|
17
|
+
|
|
18
|
+
//
|
|
19
|
+
// FormFieldLabel
|
|
20
|
+
//
|
|
21
|
+
|
|
22
|
+
export type FormFieldLabelProps = ThemedClassName<
|
|
23
|
+
{
|
|
24
|
+
/** Render a plain `<span>` instead of an input-associated `Input.Label`, for labels used outside an `Input.Root` (e.g. section/group headers). */
|
|
25
|
+
standalone?: boolean;
|
|
26
|
+
/** Class applied to the inner label text node, overriding the default size/color (e.g. `text-lg`). */
|
|
27
|
+
labelClassName?: string;
|
|
28
|
+
/** Form variant; selects the label's chrome (e.g. `settings` enlarges the text and places it in the field grid). */
|
|
29
|
+
variant?: FormVariant;
|
|
30
|
+
error?: string;
|
|
31
|
+
/**
|
|
32
|
+
* JSON path of the field this label describes (e.g. `runtime.client.storage.persistent`).
|
|
33
|
+
* Field metadata; accepted by callers but not currently rendered.
|
|
34
|
+
*/
|
|
35
|
+
path?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Trailing button rendered at the end of the label row (third grid column).
|
|
38
|
+
* Used by nested-object field sets to surface a collapse toggle.
|
|
39
|
+
*/
|
|
40
|
+
button?: ReactNode;
|
|
41
|
+
onClick?: () => void;
|
|
42
|
+
} & Pick<FormFieldRendererProps, 'label' | 'readonly' | 'required'>
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
export const FormFieldLabel = ({
|
|
46
|
+
classNames,
|
|
47
|
+
labelClassName,
|
|
48
|
+
variant = 'default',
|
|
49
|
+
label,
|
|
50
|
+
error,
|
|
51
|
+
readonly,
|
|
52
|
+
required,
|
|
53
|
+
standalone,
|
|
54
|
+
button,
|
|
55
|
+
onClick,
|
|
56
|
+
}: FormFieldLabelProps) => {
|
|
57
|
+
const styles = formTheme.styles({ variant });
|
|
58
|
+
// Render the required asterisk via a `::after` pseudo-element rather than a DOM node: it keeps the
|
|
59
|
+
// label's `textContent` exactly `label`, so fields stay locatable by their exact label text
|
|
60
|
+
// (`getByLabelText('Name')`), which the DOM-text-based query would otherwise miss as `Name *`.
|
|
61
|
+
// The `fieldLabelText` slot is applied last so a variant/caller size/color (e.g. `text-lg`) wins over
|
|
62
|
+
const labelClassNames = mx(
|
|
63
|
+
'text-sm text-description',
|
|
64
|
+
required && "after:content-['*'] after:ms-0.5 after:text-warning-text",
|
|
65
|
+
styles.fieldLabelText({ class: labelClassName }),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// `Input.Label` is a themed primitive that reads `classNames` (and ignores `className`), whereas the
|
|
69
|
+
// plain `span` used for read-only/standalone labels reads `className`.
|
|
70
|
+
const labelNode =
|
|
71
|
+
readonly || standalone ? (
|
|
72
|
+
<span className={labelClassNames}>{label}</span>
|
|
73
|
+
) : (
|
|
74
|
+
<Input.Label classNames={labelClassNames}>{label}</Input.Label>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={styles.fieldLabel({ class: mx(onClick && 'cursor-pointer', classNames) })} onClick={onClick}>
|
|
79
|
+
{labelNode}
|
|
80
|
+
{error ? (
|
|
81
|
+
<Tooltip.Trigger asChild content={error} side='bottom'>
|
|
82
|
+
<Icon icon='ph--warning--regular' size={4} classNames='text-error-text' />
|
|
83
|
+
</Tooltip.Trigger>
|
|
84
|
+
) : (
|
|
85
|
+
<span />
|
|
86
|
+
)}
|
|
87
|
+
{button}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
FormFieldLabel.displayName = 'Form.FieldLabel';
|
|
93
|
+
|
|
94
|
+
//
|
|
95
|
+
// FormRow
|
|
96
|
+
//
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Formats a value for `static` (read-only, plain-DOM) presentation based on its
|
|
100
|
+
* type `format`. Dates/times are rendered human-readable; everything else falls
|
|
101
|
+
* back to `String(value)`.
|
|
102
|
+
*/
|
|
103
|
+
const formatStaticValue = (value: unknown, format?: Format.TypeFormat): string => {
|
|
104
|
+
if (value == null) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
switch (format) {
|
|
109
|
+
case Format.TypeFormat.DateTime:
|
|
110
|
+
case Format.TypeFormat.Date:
|
|
111
|
+
case Format.TypeFormat.Time: {
|
|
112
|
+
const date = new Date(value as string);
|
|
113
|
+
if (Number.isNaN(date.getTime())) {
|
|
114
|
+
return String(value);
|
|
115
|
+
}
|
|
116
|
+
const pattern = format === Format.TypeFormat.DateTime ? 'PPp' : format === Format.TypeFormat.Date ? 'PP' : 'p';
|
|
117
|
+
return formatDate(date, pattern);
|
|
118
|
+
}
|
|
119
|
+
default:
|
|
120
|
+
return String(value);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const FORM_ROW_NAME = 'Form.Row';
|
|
125
|
+
|
|
126
|
+
export type FormRowProps<T = any> = ThemedClassName<
|
|
127
|
+
Pick<FormFieldRendererProps<T>, 'readonly' | 'label' | 'description' | 'presentation' | 'required'> &
|
|
128
|
+
Partial<Pick<FormFieldRendererProps<T>, 'getStatus' | 'getValue' | 'jsonPath' | 'format'>> & {
|
|
129
|
+
/**
|
|
130
|
+
* Ref to the row's outer element. Enables measurement/positioning (e.g. a future virtualized
|
|
131
|
+
* field set) without forwarding through the generic component.
|
|
132
|
+
*/
|
|
133
|
+
rootRef?: Ref<HTMLDivElement>;
|
|
134
|
+
/**
|
|
135
|
+
* Render the label as a standalone `<span>` (group/multi-input fields with no single associated control).
|
|
136
|
+
*/
|
|
137
|
+
standalone?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Override the read-only/`static` rendering of the value. Fields whose value is not plain text
|
|
140
|
+
* (refs, selects, markdown) supply this; the default formats scalars/dates via `formatStaticValue`.
|
|
141
|
+
* Return `null` to render nothing (e.g. an empty/unresolved value).
|
|
142
|
+
*/
|
|
143
|
+
renderStatic?: (value: T | undefined) => ReactNode;
|
|
144
|
+
/**
|
|
145
|
+
* Validation/error content rendered in the validation slot. Supplied directly in action mode; in
|
|
146
|
+
* field mode it is derived from `getStatus`.
|
|
147
|
+
*/
|
|
148
|
+
validation?: ReactNode;
|
|
149
|
+
/**
|
|
150
|
+
* The control. A render-prop binds to the form value (field mode: `Input.Root` validation, static
|
|
151
|
+
* rendering, value via `getValue`). Plain nodes render an arbitrary control with no value wiring
|
|
152
|
+
* (action mode, e.g. a button) — the labeled-card escape hatch for an arbitrary control.
|
|
153
|
+
*
|
|
154
|
+
* `value` is `T | undefined` because `getValue()` returns no value when the field is unset (optional
|
|
155
|
+
* schema properties, freshly-added array items); renderers default it (e.g. `{ value = '' }`).
|
|
156
|
+
*/
|
|
157
|
+
children?: ReactNode | ((props: { value: T | undefined; presentation: FormFieldPresentation }) => ReactNode);
|
|
158
|
+
}
|
|
159
|
+
>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A labeled card row — the single shell behind both schema fields and free-form "action" rows. Field
|
|
163
|
+
* renderers pass a render-prop `children` (field mode: bound to the form value, with validation and
|
|
164
|
+
* static rendering); consumers pass plain `children` for an arbitrary control (action mode), the
|
|
165
|
+
* affordance for an arbitrary labeled control (action rows, settings cards).
|
|
166
|
+
*/
|
|
167
|
+
export const FormRow = <T,>({
|
|
168
|
+
classNames,
|
|
169
|
+
children,
|
|
170
|
+
readonly,
|
|
171
|
+
presentation,
|
|
172
|
+
label,
|
|
173
|
+
description,
|
|
174
|
+
jsonPath,
|
|
175
|
+
format,
|
|
176
|
+
required,
|
|
177
|
+
standalone,
|
|
178
|
+
renderStatic,
|
|
179
|
+
validation,
|
|
180
|
+
getStatus,
|
|
181
|
+
getValue,
|
|
182
|
+
rootRef,
|
|
183
|
+
}: FormRowProps<T>) => {
|
|
184
|
+
const { variant = 'default' } = useFormContext(FORM_ROW_NAME);
|
|
185
|
+
const styles = formTheme.styles({ variant });
|
|
186
|
+
const { showDescription } = formTheme.behavior[variant];
|
|
187
|
+
const resolved = presentationFor(presentation);
|
|
188
|
+
|
|
189
|
+
//
|
|
190
|
+
// Field mode: a render-prop control bound to the form value.
|
|
191
|
+
//
|
|
192
|
+
if (typeof children === 'function' && getStatus && getValue) {
|
|
193
|
+
const { status, error } = getStatus();
|
|
194
|
+
const value: T | undefined = getValue();
|
|
195
|
+
if (resolved.isStatic && value == null) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const control = resolved.isStatic
|
|
200
|
+
? (renderStatic?.(value) ?? <p className='truncate min-w-0'>{formatStaticValue(value, format)}</p>)
|
|
201
|
+
: children({ value, presentation: resolved });
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Input.Root validationValence={status}>
|
|
205
|
+
<div className={styles.field({ class: classNames })} ref={rootRef}>
|
|
206
|
+
{resolved.showLabel && (
|
|
207
|
+
<FormFieldLabel
|
|
208
|
+
variant={variant}
|
|
209
|
+
error={error}
|
|
210
|
+
readonly={readonly}
|
|
211
|
+
required={required}
|
|
212
|
+
standalone={standalone}
|
|
213
|
+
label={label}
|
|
214
|
+
path={jsonPath}
|
|
215
|
+
/>
|
|
216
|
+
)}
|
|
217
|
+
{showDescription && description && (
|
|
218
|
+
<Input.Description classNames={styles.fieldDescription()}>{description}</Input.Description>
|
|
219
|
+
)}
|
|
220
|
+
<div className={styles.fieldControl()}>{control}</div>
|
|
221
|
+
{resolved.showError && error && (
|
|
222
|
+
<div className={styles.fieldValidation()}>
|
|
223
|
+
<Input.DescriptionAndValidation>
|
|
224
|
+
<Input.Validation>{error}</Input.Validation>
|
|
225
|
+
</Input.DescriptionAndValidation>
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
</Input.Root>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
//
|
|
234
|
+
// Action mode: an arbitrary control with a standalone label; no value/`Input.Root` wiring.
|
|
235
|
+
//
|
|
236
|
+
return (
|
|
237
|
+
<div ref={rootRef} className={styles.field({ class: classNames })}>
|
|
238
|
+
{resolved.showLabel && label && (
|
|
239
|
+
<FormFieldLabel
|
|
240
|
+
variant={variant}
|
|
241
|
+
readonly={readonly}
|
|
242
|
+
required={required}
|
|
243
|
+
standalone
|
|
244
|
+
label={label}
|
|
245
|
+
path={jsonPath}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
{showDescription && description && <p className={styles.fieldDescription()}>{description}</p>}
|
|
249
|
+
<div className={styles.fieldControl()}>{typeof children === 'function' ? null : children}</div>
|
|
250
|
+
{validation && <div className={styles.fieldValidation()}>{validation}</div>}
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
FormRow.displayName = FORM_ROW_NAME;
|
|
256
|
+
|
|
257
|
+
//
|
|
258
|
+
// FormFieldErrorBoundary
|
|
259
|
+
//
|
|
260
|
+
|
|
261
|
+
type FormFieldErrorState = {
|
|
262
|
+
error: Error | undefined;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
type FormFieldErrorBoundaryProps = PropsWithChildren<{
|
|
266
|
+
path?: (string | number)[];
|
|
267
|
+
}>;
|
|
268
|
+
|
|
269
|
+
export class FormFieldErrorBoundary extends Component<FormFieldErrorBoundaryProps, FormFieldErrorState> {
|
|
270
|
+
static getDerivedStateFromError(error: Error): { error: Error } {
|
|
271
|
+
return { error };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
override state = { error: undefined };
|
|
275
|
+
|
|
276
|
+
override componentDidUpdate(prevProps: FormFieldErrorBoundaryProps) {
|
|
277
|
+
if (prevProps.path !== this.props.path) {
|
|
278
|
+
this.resetError();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
override render() {
|
|
283
|
+
if (this.state.error) {
|
|
284
|
+
return (
|
|
285
|
+
<div className='flex gap-2 border border-error-border font-mono text-sm'>
|
|
286
|
+
<span className='bg-error-bg text-base-fg px-1 font-thin'>ERROR</span>
|
|
287
|
+
{String(this.props.path?.join('.'))}
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return this.props.children;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private resetError() {
|
|
296
|
+
this.setState({ error: undefined });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { FormLayoutAnnotation, FormOrderedAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
import { trim } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { translations } from '#translations';
|
|
14
|
+
|
|
15
|
+
import { TestLayout } from '../../../../../testing';
|
|
16
|
+
import { Form } from '../../../Form';
|
|
17
|
+
|
|
18
|
+
// Mirrors the structure of `Pipeline` (`@dxos/types`): a scalar header plus an
|
|
19
|
+
// array of `Column`-like structs. The array is the field we render as either a
|
|
20
|
+
// static or an ordered (drag-to-reorder) list.
|
|
21
|
+
const Column = Schema.Struct({
|
|
22
|
+
name: Schema.String.annotations({ title: 'Name' }),
|
|
23
|
+
value: Schema.optional(Schema.Number.annotations({ title: 'Value' })),
|
|
24
|
+
}).pipe(
|
|
25
|
+
Schema.mutable,
|
|
26
|
+
LabelAnnotation.set(['name']),
|
|
27
|
+
FormLayoutAnnotation.set({
|
|
28
|
+
default: trim`
|
|
29
|
+
<grid cols="2">
|
|
30
|
+
<field name="name"/>
|
|
31
|
+
<field name="value"/>
|
|
32
|
+
</grid>
|
|
33
|
+
`,
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
type Column = Schema.Schema.Type<typeof Column>;
|
|
37
|
+
|
|
38
|
+
const columnsField = Schema.Array(Column).pipe(Schema.mutable, Schema.annotations({ title: 'Columns' }));
|
|
39
|
+
|
|
40
|
+
const headerFields = {
|
|
41
|
+
name: Schema.String.pipe(Schema.annotations({ title: 'Name' }), Schema.optional),
|
|
42
|
+
description: Schema.String.pipe(Schema.annotations({ title: 'Description' }), Schema.optional),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const Pipeline = Schema.Struct({
|
|
46
|
+
...headerFields,
|
|
47
|
+
columns: columnsField,
|
|
48
|
+
}).pipe(Schema.mutable);
|
|
49
|
+
|
|
50
|
+
const OrderedPipeline = Schema.Struct({
|
|
51
|
+
...headerFields,
|
|
52
|
+
columns: columnsField.pipe(FormOrderedAnnotation.set(true)),
|
|
53
|
+
}).pipe(Schema.mutable);
|
|
54
|
+
|
|
55
|
+
const StringPipeline = Schema.Struct({
|
|
56
|
+
...headerFields,
|
|
57
|
+
columns: Schema.Array(Schema.String).pipe(FormOrderedAnnotation.set(true)),
|
|
58
|
+
}).pipe(Schema.mutable);
|
|
59
|
+
|
|
60
|
+
type PipelineValues = { name?: string; description?: string; columns: readonly unknown[] };
|
|
61
|
+
|
|
62
|
+
// Object-array columns (matches `Pipeline` / `OrderedPipeline`).
|
|
63
|
+
const objectColumns: PipelineValues = {
|
|
64
|
+
name: 'Sales',
|
|
65
|
+
description: 'Lead qualification pipeline.',
|
|
66
|
+
columns: [{ name: 'Contacts' }, { name: 'Organizations' }, { name: 'Tasks' }, { name: 'Messages', value: 100 }],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// String-array columns (matches `StringPipeline`); feeding object columns here renders `[object Object]`.
|
|
70
|
+
const stringColumns: PipelineValues = {
|
|
71
|
+
name: 'Sales',
|
|
72
|
+
description: 'Lead qualification pipeline.',
|
|
73
|
+
columns: ['Contacts', 'Organizations', 'Tasks', 'Messages'],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const DefaultStory = ({ schema, values: initial }: { schema: Schema.Schema<any>; values: PipelineValues }) => {
|
|
77
|
+
const [values, setValues] = useState<PipelineValues>(initial);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<TestLayout json={values}>
|
|
81
|
+
<Form.Root
|
|
82
|
+
schema={schema}
|
|
83
|
+
values={values}
|
|
84
|
+
onValuesChanged={(next) => setValues((prev) => ({ ...prev, ...next }))}
|
|
85
|
+
>
|
|
86
|
+
<Form.Viewport scroll>
|
|
87
|
+
<Form.Content>
|
|
88
|
+
<Form.FieldSet />
|
|
89
|
+
</Form.Content>
|
|
90
|
+
</Form.Viewport>
|
|
91
|
+
</Form.Root>
|
|
92
|
+
</TestLayout>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const meta = {
|
|
97
|
+
title: 'ui/react-ui-form/FormField/ArrayField',
|
|
98
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
99
|
+
parameters: {
|
|
100
|
+
layout: 'fullscreen',
|
|
101
|
+
translations,
|
|
102
|
+
},
|
|
103
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
104
|
+
|
|
105
|
+
export default meta;
|
|
106
|
+
|
|
107
|
+
type Story = StoryObj<typeof meta>;
|
|
108
|
+
|
|
109
|
+
export const Default: Story = {
|
|
110
|
+
render: () => <DefaultStory schema={Pipeline} values={objectColumns} />,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const Ordered: Story = {
|
|
114
|
+
render: () => <DefaultStory schema={OrderedPipeline} values={objectColumns} />,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const Simple: Story = {
|
|
118
|
+
render: () => <DefaultStory schema={StringPipeline} values={stringColumns} />,
|
|
119
|
+
};
|