@dxos/react-ui-form 0.8.4-main.fffef41 → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/README.md +0 -1
- package/dist/lib/browser/index.mjs +4005 -2402
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +87 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +4005 -2402
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +89 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- 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 +73 -59
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +135 -27
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +192 -137
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +52 -0
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +98 -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 +26 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts +19 -0
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.d.ts +50 -0
- package/dist/types/src/components/Form/Layout/FormLayout.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts +123 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/annotation.d.ts +30 -0
- package/dist/types/src/components/Form/Layout/annotation.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/index.d.ts +4 -0
- package/dist/types/src/components/Form/Layout/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/parser.d.ts +31 -0
- package/dist/types/src/components/Form/Layout/parser.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/parser.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/parser.test.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/DateField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/DateField.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/InlineRefField.d.ts +13 -0
- package/dist/types/src/components/Form/fields/InlineRefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +11 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +89 -0
- package/dist/types/src/components/Form/fields/MarkdownField.stories.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 +34 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/RefField.test.d.ts +2 -0
- package/dist/types/src/components/Form/fields/RefField.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts +13 -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 +89 -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 +14 -0
- package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +5 -4
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/meta-tags.d.ts +30 -0
- package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -0
- package/dist/types/src/components/Form/meta-tags.test.d.ts +2 -0
- package/dist/types/src/components/Form/meta-tags.test.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +8 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts +87 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
- package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +39 -12
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +69 -58
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +132 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.d.ts +9 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.d.ts.map +1 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts +91 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectTree/index.d.ts +2 -0
- package/dist/types/src/components/ObjectTree/index.d.ts.map +1 -0
- package/dist/types/src/components/RefEditor/RefEditor.d.ts +73 -0
- package/dist/types/src/components/RefEditor/RefEditor.d.ts.map +1 -0
- package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts +92 -0
- package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/RefEditor/index.d.ts +3 -0
- package/dist/types/src/components/RefEditor/index.d.ts.map +1 -0
- package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts +47 -0
- package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.d.ts +51 -0
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts +89 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +4 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +15 -19
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +75 -64
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +5 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +13 -6
- 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 +7 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +70 -59
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field-type.d.ts +19 -0
- package/dist/types/src/util/field-type.d.ts.map +1 -0
- 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 +2 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +35 -0
- package/dist/types/src/util/properties.d.ts.map +1 -0
- package/dist/types/src/util/properties.test.d.ts +2 -0
- package/dist/types/src/util/properties.test.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +2 -2
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +64 -52
- package/src/components/FieldEditor/FieldEditor.stories.tsx +44 -25
- package/src/components/FieldEditor/FieldEditor.test.tsx +4 -2
- package/src/components/FieldEditor/FieldEditor.tsx +91 -86
- package/src/components/Form/AUDIT.md +17 -0
- package/src/components/Form/Form.stories.tsx +147 -252
- package/src/components/Form/Form.tsx +461 -108
- package/src/components/Form/FormField.tsx +345 -0
- package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
- package/src/components/Form/FormFieldComponent.tsx +253 -0
- package/src/components/Form/FormFieldSet.tsx +213 -0
- package/src/components/Form/FormTooltipsContext.ts +25 -0
- package/src/components/Form/Layout/FormLayout.stories.test.tsx +51 -0
- package/src/components/Form/Layout/FormLayout.stories.tsx +422 -0
- package/src/components/Form/Layout/FormLayout.test.ts +59 -0
- package/src/components/Form/Layout/FormLayout.tsx +251 -0
- package/src/components/Form/Layout/annotation.ts +38 -0
- package/src/components/Form/Layout/index.ts +7 -0
- package/src/components/Form/Layout/parser.test.ts +120 -0
- package/src/components/Form/Layout/parser.ts +167 -0
- package/src/components/Form/README.md +3 -0
- package/src/components/Form/fields/ArrayField.tsx +173 -0
- package/src/components/Form/fields/BooleanField.tsx +22 -0
- package/src/components/Form/fields/DateField.tsx +101 -0
- package/src/components/Form/fields/GeoPointField.tsx +124 -0
- package/src/components/Form/fields/InlineRefField.tsx +138 -0
- package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
- package/src/components/Form/fields/MarkdownField.tsx +167 -0
- package/src/components/Form/fields/NumberField.tsx +76 -0
- package/src/components/Form/fields/RefField.test.ts +45 -0
- package/src/components/Form/fields/RefField.tsx +266 -0
- package/src/components/Form/fields/SelectField.tsx +77 -0
- package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +23 -18
- package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +33 -26
- 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} +17 -9
- package/src/components/Form/fields/index.ts +17 -0
- package/src/components/Form/index.ts +6 -4
- package/src/components/Form/meta-tags.test.ts +47 -0
- package/src/components/Form/meta-tags.ts +33 -0
- package/src/components/ObjectForm/ObjectForm.stories.tsx +62 -0
- package/src/components/ObjectForm/ObjectForm.tsx +111 -0
- package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +26 -21
- package/src/components/ObjectPicker/ObjectPicker.tsx +123 -82
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +350 -0
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +111 -0
- package/src/components/ObjectProperties/ObjectProperties.tsx +146 -0
- package/src/components/ObjectProperties/index.ts +5 -0
- package/src/components/ObjectTree/ObjectTree.stories.tsx +134 -0
- package/src/components/ObjectTree/ObjectTree.tsx +190 -0
- package/src/components/ObjectTree/index.ts +5 -0
- package/src/components/RefEditor/RefEditor.stories.tsx +148 -0
- package/src/components/RefEditor/RefEditor.tsx +275 -0
- package/src/components/RefEditor/index.ts +6 -0
- package/src/components/RefEditor/ref-editor-extension.ts +254 -0
- package/src/components/Settings/AUDIT.md +51 -0
- package/src/components/Settings/Settings.stories.tsx +104 -0
- package/src/components/Settings/Settings.tsx +81 -0
- package/src/components/Settings/SettingsFieldSet.tsx +257 -0
- package/src/components/Settings/SettingsItem.tsx +58 -0
- package/src/components/Settings/SettingsSelectField.tsx +59 -0
- package/src/components/Settings/index.ts +7 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +70 -76
- package/src/components/ViewEditor/ViewEditor.test.tsx +17 -13
- package/src/components/ViewEditor/ViewEditor.tsx +340 -226
- package/src/components/index.ts +5 -1
- package/src/components/testing.tsx +24 -11
- package/src/hooks/index.ts +2 -3
- package/src/hooks/useFormHandler.ts +380 -0
- package/src/hooks/useKeyHandler.tsx +37 -0
- package/src/index.ts +0 -4
- package/src/translations.ts +76 -64
- package/src/util/field-type.ts +60 -0
- package/src/util/field.ts +25 -25
- package/src/util/formatting.ts +22 -22
- package/src/util/index.ts +2 -1
- package/src/util/properties.test.ts +153 -0
- package/src/util/properties.ts +100 -0
- package/src/util/refs.ts +16 -15
- package/src/vite-env.d.ts +10 -0
- 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 -83
- 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 -63
- 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 -29
- package/dist/types/src/components/Form/Input.d.ts.map +0 -1
- package/dist/types/src/components/Form/RefField.d.ts +0 -22
- 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 -78
- 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/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 -29
- 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 -38
- package/src/components/ControlSection/ControlSection.tsx +0 -153
- package/src/components/Form/ArrayField.tsx +0 -125
- package/src/components/Form/Defaults.tsx +0 -205
- package/src/components/Form/FormActions.tsx +0 -54
- package/src/components/Form/FormContent.tsx +0 -362
- package/src/components/Form/FormContext.tsx +0 -110
- package/src/components/Form/Input.tsx +0 -54
- package/src/components/Form/RefField.tsx +0 -283
- package/src/components/Form/custom/GeoPointInput.tsx +0 -80
- package/src/components/Form/custom/index.ts +0 -7
- package/src/components/Form/factory.tsx +0 -31
- package/src/hooks/useForm.ts +0 -296
- package/src/hooks/useQueryRefOptions.ts +0 -62
- package/src/hooks/useRefQueryLookupHandler.ts +0 -39
- package/src/util/schema.ts +0 -12
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Option from 'effect/Option';
|
|
6
|
+
import React, { useMemo, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type AnyProperties } from '@dxos/echo/internal';
|
|
9
|
+
import { SchemaEx } from '@dxos/effect';
|
|
10
|
+
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
import { mx } from '@dxos/ui-theme';
|
|
12
|
+
|
|
13
|
+
import { translationKey } from '#translations';
|
|
14
|
+
|
|
15
|
+
import { type FormHandlerProps } from '../../hooks';
|
|
16
|
+
import { getRootFormProperties } from '../../util';
|
|
17
|
+
import { useFormValues } from './Form';
|
|
18
|
+
import { FormField, type FormFieldProps } from './FormField';
|
|
19
|
+
import { FormFieldErrorBoundary, FormFieldLabel } from './FormFieldComponent';
|
|
20
|
+
import { DEFAULT_LAYOUT_NAME, FormLayout, FormLayoutAnnotation } from './Layout';
|
|
21
|
+
|
|
22
|
+
const FORM_FIELDSET_NAME = 'Form.FieldSet';
|
|
23
|
+
|
|
24
|
+
export type FormFieldSetProps<T extends AnyProperties> = {
|
|
25
|
+
label?: string;
|
|
26
|
+
sort?: string[];
|
|
27
|
+
/**
|
|
28
|
+
* When set, renders a toggle button at the end of the label row that
|
|
29
|
+
* shows/hides the field set body. Used for nested objects.
|
|
30
|
+
*/
|
|
31
|
+
collapsible?: boolean;
|
|
32
|
+
exclude?: (props: SchemaEx.SchemaProperty[]) => SchemaEx.SchemaProperty[];
|
|
33
|
+
/**
|
|
34
|
+
* Picks a named layout out of `FormLayoutAnnotation` when present.
|
|
35
|
+
* Falls back to `'default'`. Ignored when the schema has no annotation
|
|
36
|
+
* (linear rendering then takes over).
|
|
37
|
+
*/
|
|
38
|
+
layoutName?: string;
|
|
39
|
+
} & Pick<FormHandlerProps<T>, 'schema'> &
|
|
40
|
+
Pick<
|
|
41
|
+
FormFieldProps,
|
|
42
|
+
| 'path'
|
|
43
|
+
| 'autoFocus'
|
|
44
|
+
| 'readonly'
|
|
45
|
+
| 'layout'
|
|
46
|
+
| 'projection'
|
|
47
|
+
| 'fieldMap'
|
|
48
|
+
| 'fieldProvider'
|
|
49
|
+
| 'createTypename'
|
|
50
|
+
| 'createOptionLabel'
|
|
51
|
+
| 'createOptionIcon'
|
|
52
|
+
| 'createInitialValuePath'
|
|
53
|
+
| 'createFieldMap'
|
|
54
|
+
| 'db'
|
|
55
|
+
| 'useType'
|
|
56
|
+
| 'getOptions'
|
|
57
|
+
| 'onCreate'
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Renders a set of form fields derived from a schema object.
|
|
62
|
+
*/
|
|
63
|
+
export const FormFieldSet = ({
|
|
64
|
+
label,
|
|
65
|
+
schema,
|
|
66
|
+
readonly,
|
|
67
|
+
path,
|
|
68
|
+
sort,
|
|
69
|
+
collapsible,
|
|
70
|
+
exclude,
|
|
71
|
+
projection,
|
|
72
|
+
layout,
|
|
73
|
+
layoutName = DEFAULT_LAYOUT_NAME,
|
|
74
|
+
...props
|
|
75
|
+
}: FormFieldSetProps<any>) => {
|
|
76
|
+
const { t } = useTranslation(translationKey);
|
|
77
|
+
const values = useFormValues(FORM_FIELDSET_NAME, path);
|
|
78
|
+
const properties = useFormFieldSetProperties({ schema, values, exclude, sort, projection });
|
|
79
|
+
// TODO(burdon): Generalize collapse state (cf. useSelected in react-ui-attention, plugin-markdown cursor state).
|
|
80
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
81
|
+
if ((readonly || layout === 'static') && values == null) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If the schema carries a layout template, hand off to <Form.Layout/> which renders the DSL.
|
|
86
|
+
// Linear rendering still runs when no annotation is present, so existing call sites are unchanged.
|
|
87
|
+
const layouts = schema ? Option.getOrUndefined(FormLayoutAnnotation.get(schema)) : undefined;
|
|
88
|
+
const body =
|
|
89
|
+
layouts?.[layoutName] !== undefined && schema ? (
|
|
90
|
+
<FormLayout
|
|
91
|
+
schema={schema}
|
|
92
|
+
name={layoutName}
|
|
93
|
+
path={path}
|
|
94
|
+
readonly={readonly}
|
|
95
|
+
layout={layout}
|
|
96
|
+
projection={projection}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
) : (
|
|
100
|
+
properties.map((property) => {
|
|
101
|
+
const name = property.name.toString();
|
|
102
|
+
return (
|
|
103
|
+
<FormFieldErrorBoundary key={name} path={[...(path ?? []), name]}>
|
|
104
|
+
<FormField
|
|
105
|
+
type={property.type}
|
|
106
|
+
name={name}
|
|
107
|
+
path={[...(path ?? []), name]}
|
|
108
|
+
readonly={readonly}
|
|
109
|
+
layout={layout}
|
|
110
|
+
projection={projection}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
</FormFieldErrorBoundary>
|
|
114
|
+
);
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const showBody = !(collapsible && collapsed);
|
|
119
|
+
const content = (
|
|
120
|
+
<>
|
|
121
|
+
{layout !== 'inline' && label && (
|
|
122
|
+
<FormFieldLabel
|
|
123
|
+
asChild
|
|
124
|
+
classNames='pl-2'
|
|
125
|
+
label={label}
|
|
126
|
+
path={SchemaEx.createJsonPath(path ?? [])}
|
|
127
|
+
button={
|
|
128
|
+
collapsible && (
|
|
129
|
+
<IconButton
|
|
130
|
+
classNames='px-1 mr-0.5'
|
|
131
|
+
variant='ghost'
|
|
132
|
+
density='xs'
|
|
133
|
+
iconOnly
|
|
134
|
+
icon='ph--caret-right--regular'
|
|
135
|
+
iconClassNames={mx('transition-transform', !collapsed && 'rotate-90')}
|
|
136
|
+
label={t(collapsed ? 'expand-fields.label' : 'collapse-fields.label')}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
onClick={() => setCollapsed((value) => !value)}
|
|
141
|
+
/>
|
|
142
|
+
)}
|
|
143
|
+
{showBody && (collapsible ? <div className='px-2 pb-2'>{body}</div> : body)}
|
|
144
|
+
</>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Nested field sets render inside an indented, bordered container with a collapse toggle.
|
|
148
|
+
if (collapsible) {
|
|
149
|
+
return <div className='border border-subdued-separator rounded-sm mt-2'>{content}</div>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return content;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
FormFieldSet.displayName = FORM_FIELDSET_NAME;
|
|
156
|
+
|
|
157
|
+
type UseFormFieldSetPropertiesParams = Pick<FormFieldSetProps<any>, 'schema' | 'exclude' | 'projection' | 'sort'> & {
|
|
158
|
+
values: AnyProperties | undefined;
|
|
159
|
+
sort?: string[];
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolves ordered schema properties for a field set (projection order, exclude, or sort).
|
|
164
|
+
*/
|
|
165
|
+
const useFormFieldSetProperties = ({
|
|
166
|
+
schema,
|
|
167
|
+
exclude,
|
|
168
|
+
projection,
|
|
169
|
+
values,
|
|
170
|
+
sort,
|
|
171
|
+
}: UseFormFieldSetPropertiesParams): SchemaEx.SchemaProperty[] => {
|
|
172
|
+
// TODO(burdon): Updates on every value change.
|
|
173
|
+
// Remove values dep if can remove from getSchemaProperties.
|
|
174
|
+
return useMemo(() => {
|
|
175
|
+
if (!schema) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// TODO(wittjosiah): Reconcile FormInputAnnotation with projection hidden properties & exclude function.
|
|
180
|
+
const schemaProps = getRootFormProperties(schema.ast, values);
|
|
181
|
+
const filteredProps = exclude ? exclude(schemaProps) : schemaProps;
|
|
182
|
+
|
|
183
|
+
// Use projection-based field management when view and projection are available.
|
|
184
|
+
if (projection) {
|
|
185
|
+
const fieldProjections = projection.getFieldProjections();
|
|
186
|
+
const hiddenProperties = new Set(projection.getHiddenProperties());
|
|
187
|
+
|
|
188
|
+
// Filter properties to only include visible ones and order by projection.
|
|
189
|
+
const visibleProps = filteredProps.filter((prop) => !hiddenProperties.has(prop.name.toString()));
|
|
190
|
+
const orderedProps: SchemaEx.SchemaProperty[] = [];
|
|
191
|
+
|
|
192
|
+
// Add properties in projection field order.
|
|
193
|
+
for (const fieldProjection of fieldProjections) {
|
|
194
|
+
const fieldPath = String(fieldProjection.field.path);
|
|
195
|
+
const prop = visibleProps.find((prop) => prop.name === fieldPath);
|
|
196
|
+
if (prop) {
|
|
197
|
+
orderedProps.push(prop);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Add any remaining properties not in projection.
|
|
202
|
+
const projectionPaths = new Set(fieldProjections.map((projection) => String(projection.field.path)));
|
|
203
|
+
const remainingProps = visibleProps.filter((prop) => !projectionPaths.has(prop.name.toString()));
|
|
204
|
+
orderedProps.push(...remainingProps);
|
|
205
|
+
return orderedProps;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Fallback to legacy filter/sort behavior.
|
|
209
|
+
return sort
|
|
210
|
+
? [...filteredProps].sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString()))
|
|
211
|
+
: filteredProps;
|
|
212
|
+
}, [schema, values, exclude, sort, projection]);
|
|
213
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext, useContext } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A standalone context for the `tooltips` flag (Form.Root's only label
|
|
9
|
+
* decoration today: a JSON-path hover tooltip rendered by
|
|
10
|
+
* `FormFieldLabel`).
|
|
11
|
+
*
|
|
12
|
+
* Lives in its own module so that `FormFieldComponent.tsx` -- imported at
|
|
13
|
+
* the top of `Form.tsx` -- can read the flag without re-importing from
|
|
14
|
+
* `Form.tsx`. That round-trip used to create a circular module-eval
|
|
15
|
+
* dependency that intermittently hit a TDZ ("Cannot access 'FormFieldLabel'
|
|
16
|
+
* before initialization") in browser-mode storybook tests.
|
|
17
|
+
*
|
|
18
|
+
* Defaults to `true` both when `Form.Root` doesn't pass an explicit value
|
|
19
|
+
* AND when there is no enclosing `Form.Root` (e.g. `FormFieldWrapper`
|
|
20
|
+
* stories that render an input without a Form provider).
|
|
21
|
+
*/
|
|
22
|
+
export const FormTooltipsContext = createContext<boolean>(true);
|
|
23
|
+
|
|
24
|
+
/** Returns whether the enclosing `Form.Root` has field tooltips enabled. */
|
|
25
|
+
export const useFormTooltips = (): boolean => useContext(FormTooltipsContext);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { composeStories } from '@storybook/react';
|
|
6
|
+
import { act, cleanup, screen, within } from '@testing-library/react';
|
|
7
|
+
import { afterEach, describe, expect, test } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import * as stories from './FormLayout.stories';
|
|
10
|
+
|
|
11
|
+
const { NestedLabel, NestedLabelStatic } = composeStories(stories);
|
|
12
|
+
|
|
13
|
+
describe('FormLayout — nested fields', () => {
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
// Flush pending React scheduler work before teardown to prevent
|
|
16
|
+
// "window is not defined" errors from setImmediate callbacks firing after happy-dom cleanup.
|
|
17
|
+
await act(async () => {});
|
|
18
|
+
cleanup();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('renders nested struct labels and dotted sub-fields', { timeout: 30_000 }, async () => {
|
|
22
|
+
await NestedLabel.run();
|
|
23
|
+
|
|
24
|
+
// `<field name="origin"/>` / `<field name="destination"/>` auto-convert to the
|
|
25
|
+
// place's `LabelAnnotation` label (the `name` field), rendered as read-only text.
|
|
26
|
+
// `findByText` retries while the async client provider initializes.
|
|
27
|
+
expect(await screen.findByText('John F. Kennedy Intl')).toBeInTheDocument();
|
|
28
|
+
expect(await screen.findByText('Charles de Gaulle')).toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
// `<field name="origin.code"/>` / `<field name="destination.code"/>` drill into the
|
|
31
|
+
// leaf sub-field, rendered as editable inputs holding the code.
|
|
32
|
+
expect(await screen.findByDisplayValue('JFK')).toBeInTheDocument();
|
|
33
|
+
expect(await screen.findByDisplayValue('CDG')).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('static layout formats dates and renders labels as read-only text', { timeout: 30_000 }, async () => {
|
|
37
|
+
await NestedLabelStatic.run();
|
|
38
|
+
|
|
39
|
+
// Scope assertions to the form: the debug panel echoes the raw values JSON.
|
|
40
|
+
const form = await screen.findByRole('form');
|
|
41
|
+
|
|
42
|
+
// The depart date renders human-readable (contains the year) rather than the raw ISO
|
|
43
|
+
// string. Match `2026` and assert no ISO time fragment leaks through — timezone-robust.
|
|
44
|
+
expect(within(form).getByText(/2026/)).toBeInTheDocument();
|
|
45
|
+
expect(within(form).queryByText(/T\d\d:\d\d/)).toBeNull();
|
|
46
|
+
|
|
47
|
+
// Nested struct labels and dotted sub-fields render as static text (not inputs).
|
|
48
|
+
expect(within(form).getByText('John F. Kennedy Intl')).toBeInTheDocument();
|
|
49
|
+
expect(within(form).getByText('JFK')).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
});
|