@dxos/react-ui-form 0.8.4-main.7ace549 → 0.8.4-main.937b3ca
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 +2191 -2283
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +2191 -2283
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +2 -2
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +7 -4
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +6 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +9 -3
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form-old.stories.d.ts +131 -0
- package/dist/types/src/components/Form/Form-old.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/Form.d.ts +81 -27
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +53 -92
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +37 -0
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +78 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts +19 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts +14 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
- package/dist/types/src/components/Form/{custom/SelectOptionsInput.stories.d.ts → fields/SelectOptionField.stories.d.ts} +6 -3
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/index.d.ts +12 -0
- package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +2 -4
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +14 -9
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +5 -2
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +6 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +5 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +0 -2
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -3
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +6 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +7 -0
- package/dist/types/src/util/properties.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +1 -2
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +49 -44
- package/src/components/ControlSection/ControlSection.tsx +8 -8
- package/src/components/FieldEditor/FieldEditor.stories.tsx +42 -21
- package/src/components/FieldEditor/FieldEditor.tsx +72 -75
- package/src/components/Form/Form-old.stories.tsx +314 -0
- package/src/components/Form/Form.stories.tsx +146 -246
- package/src/components/Form/Form.tsx +319 -110
- package/src/components/Form/FormField.tsx +286 -0
- package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
- package/src/components/Form/FormFieldComponent.tsx +174 -0
- package/src/components/Form/FormFieldSet.tsx +125 -0
- package/src/components/Form/README.md +3 -0
- package/src/components/Form/fields/ArrayField.tsx +151 -0
- package/src/components/Form/fields/BooleanField.tsx +22 -0
- package/src/components/Form/fields/GeoPointField.tsx +120 -0
- package/src/components/Form/fields/MarkdownField.tsx +78 -0
- package/src/components/Form/fields/NumberField.tsx +39 -0
- package/src/components/Form/fields/RefField.tsx +181 -0
- package/src/components/Form/fields/SelectField.tsx +65 -0
- package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +17 -14
- package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +20 -14
- 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} +15 -8
- package/src/components/Form/fields/index.ts +15 -0
- package/src/components/Form/index.ts +3 -4
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +15 -8
- package/src/components/ObjectPicker/ObjectPicker.tsx +76 -62
- package/src/components/ViewEditor/ViewEditor.stories.tsx +29 -25
- package/src/components/ViewEditor/ViewEditor.test.tsx +6 -2
- package/src/components/ViewEditor/ViewEditor.tsx +220 -154
- package/src/components/testing.tsx +9 -7
- package/src/hooks/index.ts +2 -3
- package/src/hooks/useFormHandler.ts +356 -0
- package/src/hooks/useKeyHandler.tsx +37 -0
- package/src/translations.ts +8 -6
- package/src/util/index.ts +1 -1
- package/src/util/properties.ts +18 -0
- package/src/util/refs.ts +7 -13
- 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.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/Form/ArrayField.tsx +0 -127
- package/src/components/Form/Defaults.tsx +0 -205
- package/src/components/Form/FormActions.tsx +0 -59
- 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 -34
- 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
|
@@ -1,122 +1,331 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { type
|
|
11
|
-
import {
|
|
12
|
-
import { type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import type * as SchemaAST from 'effect/SchemaAST';
|
|
8
|
+
import React, { type PropsWithChildren, useEffect, useMemo, useRef } from 'react';
|
|
9
|
+
|
|
10
|
+
import { type AnyProperties } from '@dxos/echo/internal';
|
|
11
|
+
import { createJsonPath, getValue as getValue$, setValue as setValue$ } from '@dxos/effect';
|
|
12
|
+
import { IconButton, type IconButtonProps, ScrollArea, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
type FormHandler,
|
|
17
|
+
type FormHandlerProps,
|
|
18
|
+
type FormUpdateMeta,
|
|
19
|
+
useFormHandler,
|
|
20
|
+
useKeyHandler,
|
|
21
|
+
} from '../../hooks';
|
|
22
|
+
import { translationKey } from '../../translations';
|
|
23
|
+
|
|
24
|
+
import { FormFieldLabel, type FormFieldLabelProps, type FormFieldStateProps } from './FormFieldComponent';
|
|
25
|
+
import {
|
|
26
|
+
FormFieldSet as NaturalFormFieldSet,
|
|
27
|
+
type FormFieldSetProps as NaturalFormFieldSetProps,
|
|
28
|
+
} from './FormFieldSet';
|
|
29
|
+
|
|
30
|
+
// New features/polish
|
|
31
|
+
// [x] Unify readonly/inline modes
|
|
32
|
+
// [ ] Don't call save/autoSave if value hasn't changed
|
|
33
|
+
// [ ] Fix onCancel (restore values)
|
|
34
|
+
// [ ] Fix useSchema Type.Obj.Any cast
|
|
35
|
+
// [ ] Remove @dxos/echo-db deps
|
|
36
|
+
// [ ] TableCellEditor (handleEnter/ModalController).
|
|
37
|
+
// [ ] Use FormFieldWrapper uniformly
|
|
38
|
+
// [ ] Inline tables for object arrays
|
|
39
|
+
// [ ] Defer query until popover
|
|
40
|
+
// [ ] Omit id from sub properties.
|
|
41
|
+
// [x] Refs
|
|
42
|
+
// [x] Single-select (fix popover)
|
|
43
|
+
// [x] Multi-select (array)
|
|
44
|
+
// [ ] auto save doesn't work for combobox + select due to only firing on blur (workaround is to use onValuesChanged)
|
|
45
|
+
|
|
46
|
+
// TODO(burdon): Move to @dxos/schema (re-export here).
|
|
47
|
+
export type ExcludeId<S extends Schema.Schema.AnyNoContext> = Omit<Schema.Schema.Type<S>, 'id'>;
|
|
48
|
+
|
|
49
|
+
// TODO(burdon): Move to @dxos/schema (re-export here).
|
|
50
|
+
export const omitId = <S extends Schema.Schema.AnyNoContext>(schema: S): Schema.Schema<ExcludeId<S>, ExcludeId<S>> =>
|
|
51
|
+
schema.pipe(Schema.omit('id')) as any;
|
|
52
|
+
|
|
53
|
+
//
|
|
54
|
+
// Context
|
|
55
|
+
//
|
|
56
|
+
|
|
57
|
+
type FormContextValue<T extends AnyProperties = any> = {
|
|
58
|
+
/**
|
|
59
|
+
* Form handler.
|
|
60
|
+
*/
|
|
61
|
+
form: FormHandler<T>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Show debug info.
|
|
65
|
+
*/
|
|
66
|
+
debug?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Testing.
|
|
70
|
+
*/
|
|
71
|
+
testId?: string;
|
|
72
|
+
} & Pick<NaturalFormFieldSetProps<T>, 'readonly' | 'layout' | 'fieldMap' | 'fieldProvider'>;
|
|
73
|
+
|
|
74
|
+
const [FormContextProvider, useFormContext] = createContext<FormContextValue>('Form');
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the current form values.
|
|
78
|
+
*/
|
|
79
|
+
const useFormValues: {
|
|
80
|
+
<T extends AnyProperties>(componentName: string, path?: (string | number)[]): T;
|
|
81
|
+
<T extends AnyProperties>(
|
|
82
|
+
componentName: string,
|
|
83
|
+
path: (string | number)[] | undefined,
|
|
84
|
+
defaultValue: () => T,
|
|
85
|
+
): T | undefined;
|
|
86
|
+
} = (componentName: string, path: (string | number)[] = [], defaultValue?: () => any) => {
|
|
87
|
+
const jsonPath = createJsonPath(path);
|
|
88
|
+
const {
|
|
89
|
+
form: { values },
|
|
90
|
+
} = useFormContext(componentName);
|
|
91
|
+
|
|
92
|
+
const value = getValue$(values, jsonPath);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!value && defaultValue) {
|
|
96
|
+
setValue$(values, jsonPath, defaultValue());
|
|
97
|
+
}
|
|
98
|
+
}, [value, defaultValue]);
|
|
99
|
+
|
|
100
|
+
return value;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the state props for the given field.
|
|
105
|
+
*/
|
|
106
|
+
const useFormFieldState = (componentName: string, path: (string | number)[] = []): FormFieldStateProps => {
|
|
107
|
+
const stablePath = useMemo(() => path, [Array.isArray(path) ? path.join('.') : path]);
|
|
108
|
+
const {
|
|
109
|
+
form: { getStatus, getValue, onBlur, onValueChange },
|
|
110
|
+
} = useFormContext(componentName);
|
|
111
|
+
|
|
112
|
+
return useMemo(
|
|
113
|
+
() => ({
|
|
114
|
+
getStatus: () => getStatus(stablePath),
|
|
115
|
+
getValue: () => getValue(stablePath),
|
|
116
|
+
onBlur: () => onBlur(stablePath),
|
|
117
|
+
onValueChange: (ast: SchemaAST.AST, value: any) => onValueChange(stablePath, ast, value),
|
|
118
|
+
}),
|
|
119
|
+
[getStatus, getValue, onBlur, onValueChange, stablePath],
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
//
|
|
124
|
+
// Root
|
|
125
|
+
//
|
|
126
|
+
|
|
127
|
+
type FormRootProps<T extends AnyProperties = AnyProperties> = PropsWithChildren<
|
|
128
|
+
{
|
|
129
|
+
/**
|
|
130
|
+
* Called when the form is submitted and passes validation.
|
|
131
|
+
*/
|
|
132
|
+
onSave?: (values: T, meta: FormUpdateMeta<T>) => void;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Called when the form is canceled to abandon/undo any pending changes.
|
|
136
|
+
*/
|
|
137
|
+
onCancel?: () => void;
|
|
138
|
+
} & Omit<FormContextValue<T>, 'form'> &
|
|
139
|
+
Pick<FormHandlerProps<T>, 'schema' | 'autoSave' | 'values' | 'defaultValues' | 'onValidate' | 'onValuesChanged'> &
|
|
140
|
+
Omit<NaturalFormFieldSetProps<T>, 'schema' | 'path'>
|
|
141
|
+
>;
|
|
142
|
+
|
|
143
|
+
const FormRoot = <T extends AnyProperties = AnyProperties>({
|
|
144
|
+
children,
|
|
57
145
|
schema,
|
|
58
|
-
|
|
59
|
-
onValuesChanged,
|
|
60
|
-
onValidate,
|
|
146
|
+
values,
|
|
61
147
|
onSave,
|
|
148
|
+
onCancel,
|
|
62
149
|
...props
|
|
63
|
-
}:
|
|
64
|
-
const
|
|
150
|
+
}: FormRootProps<T>) => {
|
|
151
|
+
const form = useFormHandler({ schema, values, onSave, onCancel, ...props });
|
|
65
152
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const firstFocusable = findFirstFocusable(formRef.current);
|
|
71
|
-
if (firstFocusable) {
|
|
72
|
-
firstFocusable.focus();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}, [autoFocus]);
|
|
76
|
-
|
|
77
|
-
// TODO(burdon): Name?
|
|
78
|
-
const handleValid = useCallback(
|
|
79
|
-
async (values: any, meta: { changed: FormHandler<T>['changed'] }) => {
|
|
80
|
-
if (autoSave) {
|
|
81
|
-
await onSave?.(values, meta);
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
[autoSave, onSave],
|
|
153
|
+
return (
|
|
154
|
+
<FormContextProvider form={form} {...props}>
|
|
155
|
+
{children}
|
|
156
|
+
</FormContextProvider>
|
|
85
157
|
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
FormRoot.displayName = 'Form.Root';
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
// Viewport
|
|
164
|
+
//
|
|
165
|
+
|
|
166
|
+
type FormViewportProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
167
|
+
|
|
168
|
+
const FormViewport = ({ classNames, children }: FormViewportProps) => {
|
|
169
|
+
return (
|
|
170
|
+
<ScrollArea.Root>
|
|
171
|
+
<ScrollArea.Viewport classNames={['plb-cardSpacingBlock', classNames]}>{children}</ScrollArea.Viewport>
|
|
172
|
+
<ScrollArea.Scrollbar orientation='vertical'>
|
|
173
|
+
<ScrollArea.Thumb />
|
|
174
|
+
</ScrollArea.Scrollbar>
|
|
175
|
+
</ScrollArea.Root>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
FormViewport.displayName = 'Form.Viewport';
|
|
180
|
+
|
|
181
|
+
//
|
|
182
|
+
// Content
|
|
183
|
+
//
|
|
184
|
+
|
|
185
|
+
type FormContentProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
186
|
+
|
|
187
|
+
// TOOD(burdon): Figure out nesting (indent and testId).
|
|
188
|
+
const FormContent = ({ classNames, children }: FormContentProps) => {
|
|
189
|
+
const { form, testId } = useFormContext(FormContent.displayName);
|
|
190
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
191
|
+
useKeyHandler(ref.current, form);
|
|
86
192
|
|
|
87
193
|
return (
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
onValuesChanged={onValuesChanged}
|
|
94
|
-
onValidate={onValidate}
|
|
95
|
-
onValid={handleValid}
|
|
96
|
-
onSave={onSave}
|
|
194
|
+
<div
|
|
195
|
+
ref={ref}
|
|
196
|
+
role='form'
|
|
197
|
+
className={mx('flex flex-col is-full pli-cardSpacingInline density-fine', classNames)}
|
|
198
|
+
data-testid={testId}
|
|
97
199
|
>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
200
|
+
{children}
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
FormContent.displayName = 'Form.Content';
|
|
206
|
+
|
|
207
|
+
//
|
|
208
|
+
// FieldSet
|
|
209
|
+
//
|
|
210
|
+
|
|
211
|
+
type FormFieldSetProps = ThemedClassName<{}>;
|
|
212
|
+
|
|
213
|
+
const FormFieldSet = ({ classNames }: FormFieldSetProps) => {
|
|
214
|
+
const { form, ...props } = useFormContext(FormFieldSet.displayName);
|
|
215
|
+
|
|
216
|
+
return <NaturalFormFieldSet classNames={classNames} schema={form.schema} {...props} />;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
FormFieldSet.displayName = 'Form.FieldSet';
|
|
220
|
+
|
|
221
|
+
//
|
|
222
|
+
// Actions
|
|
223
|
+
//
|
|
224
|
+
|
|
225
|
+
type FormActionsProps = ThemedClassName<{}>;
|
|
226
|
+
|
|
227
|
+
const FormActions = ({ classNames }: FormActionsProps) => {
|
|
228
|
+
const { t } = useTranslation(translationKey);
|
|
229
|
+
const {
|
|
230
|
+
form: { canSave, onSave, onCancel },
|
|
231
|
+
readonly,
|
|
232
|
+
layout,
|
|
233
|
+
} = useFormContext(FormActions.displayName);
|
|
234
|
+
|
|
235
|
+
if (readonly || layout === 'static') {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// TODO(burdon): Currently onCancel is a no-op; implement "revert values".
|
|
240
|
+
// Deprecate FormSubmit ans use FormActions without Cancel button if no callback is supplied.
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div role='none' className={mx('grid grid-flow-col auto-cols-fr gap-2 pbs-cardSpacingBlock', classNames)}>
|
|
244
|
+
{onCancel && (
|
|
245
|
+
<IconButton
|
|
246
|
+
icon='ph--x--regular'
|
|
247
|
+
iconEnd
|
|
248
|
+
label={t('cancel button label')}
|
|
249
|
+
onClick={onCancel}
|
|
250
|
+
data-testid='cancel-button'
|
|
251
|
+
/>
|
|
252
|
+
)}
|
|
253
|
+
{onSave && (
|
|
254
|
+
<IconButton
|
|
255
|
+
type='submit'
|
|
256
|
+
variant='primary'
|
|
257
|
+
disabled={!canSave}
|
|
258
|
+
icon='ph--check--regular'
|
|
259
|
+
iconEnd
|
|
260
|
+
label={t('save button label')}
|
|
261
|
+
onClick={onSave}
|
|
262
|
+
data-testid='save-button'
|
|
115
263
|
/>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)}
|
|
119
|
-
</div>
|
|
120
|
-
</FormProvider>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
121
266
|
);
|
|
122
267
|
};
|
|
268
|
+
|
|
269
|
+
FormActions.displayName = 'Form.Actions';
|
|
270
|
+
|
|
271
|
+
//
|
|
272
|
+
// Submit
|
|
273
|
+
//
|
|
274
|
+
|
|
275
|
+
type FormSubmitProps = ThemedClassName<Partial<Pick<IconButtonProps, 'icon' | 'label'>>>;
|
|
276
|
+
|
|
277
|
+
const FormSubmit = ({ classNames, label, icon }: FormSubmitProps) => {
|
|
278
|
+
const { t } = useTranslation(translationKey);
|
|
279
|
+
const {
|
|
280
|
+
form: { canSave, onSave },
|
|
281
|
+
readonly,
|
|
282
|
+
layout,
|
|
283
|
+
} = useFormContext(FormSubmit.displayName);
|
|
284
|
+
|
|
285
|
+
if (readonly || layout === 'static') {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<div role='none' className={mx('flex is-full pbs-cardSpacingBlock', classNames)}>
|
|
291
|
+
<IconButton
|
|
292
|
+
classNames='is-full'
|
|
293
|
+
type='submit'
|
|
294
|
+
variant='primary'
|
|
295
|
+
disabled={!canSave}
|
|
296
|
+
icon={icon ?? 'ph--check--regular'}
|
|
297
|
+
label={label ?? t('save button label')}
|
|
298
|
+
onClick={onSave}
|
|
299
|
+
data-testid='save-button'
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
FormSubmit.displayName = 'Form.Submit';
|
|
306
|
+
|
|
307
|
+
//
|
|
308
|
+
// Form
|
|
309
|
+
// https://www.radix-ui.com/primitives/docs/guides/composition
|
|
310
|
+
//
|
|
311
|
+
|
|
312
|
+
export const Form = {
|
|
313
|
+
Root: FormRoot,
|
|
314
|
+
Viewport: FormViewport,
|
|
315
|
+
Content: FormContent,
|
|
316
|
+
FieldSet: FormFieldSet,
|
|
317
|
+
Actions: FormActions,
|
|
318
|
+
Submit: FormSubmit,
|
|
319
|
+
Label: FormFieldLabel,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
export { useFormContext, useFormValues, useFormFieldState };
|
|
323
|
+
|
|
324
|
+
export type {
|
|
325
|
+
FormRootProps,
|
|
326
|
+
FormViewportProps,
|
|
327
|
+
FormContentProps,
|
|
328
|
+
FormFieldSetProps,
|
|
329
|
+
FormActionsProps,
|
|
330
|
+
FormFieldLabelProps as LabelProps,
|
|
331
|
+
};
|