@dxos/react-ui-form 0.8.4-main.3c1ae3b → 0.8.4-main.3eb6e50203
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 +1610 -1825
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1610 -1825
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form-old.stories.d.ts +1 -42
- package/dist/types/src/components/Form/Form-old.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +9 -6
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +32 -32
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +2 -2
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +3 -3
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +8 -12
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +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 +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/Settings/Settings.d.ts +56 -0
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
- package/dist/types/src/components/{ControlSection/ControlSection.stories.d.ts → Settings/Settings.stories.d.ts} +3 -9
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +2 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +0 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/echo.d.ts +6 -0
- package/dist/types/src/util/echo.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +49 -45
- package/src/components/FieldEditor/FieldEditor.stories.tsx +39 -18
- package/src/components/FieldEditor/FieldEditor.tsx +13 -6
- package/src/components/Form/Form-old.stories.tsx +42 -42
- package/src/components/Form/Form.stories.tsx +12 -17
- package/src/components/Form/Form.tsx +52 -27
- package/src/components/Form/FormField.tsx +23 -17
- package/src/components/Form/FormFieldComponent.stories.tsx +1 -1
- package/src/components/Form/FormFieldComponent.tsx +17 -16
- package/src/components/Form/FormFieldSet.tsx +8 -5
- package/src/components/Form/fields/ArrayField.tsx +11 -11
- package/src/components/Form/fields/RefField.tsx +82 -99
- package/src/components/Form/fields/SelectOptionField.stories.tsx +1 -1
- package/src/components/Form/fields/SelectOptionField.tsx +2 -2
- package/src/components/Form/fields/TupleField.tsx +1 -1
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +12 -5
- package/src/components/ObjectPicker/ObjectPicker.tsx +66 -57
- package/src/components/Settings/Settings.stories.tsx +56 -0
- package/src/components/Settings/Settings.tsx +234 -0
- package/src/components/{ControlSection → Settings}/index.ts +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +18 -12
- package/src/components/ViewEditor/ViewEditor.test.tsx +1 -5
- package/src/components/ViewEditor/ViewEditor.tsx +77 -44
- package/src/components/index.ts +1 -1
- package/src/components/testing.tsx +3 -5
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useFormHandler.ts +20 -6
- package/src/translations.ts +1 -1
- package/src/util/echo.ts +25 -0
- package/src/util/index.ts +1 -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.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/hooks/useQueryRefOptions.d.ts +0 -30
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
- package/dist/types/src/hooks/useRefQueryOptions.d.ts +0 -8
- package/dist/types/src/hooks/useRefQueryOptions.d.ts.map +0 -1
- package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
- package/src/components/ControlSection/ControlSection.tsx +0 -153
- package/src/hooks/useQueryRefOptions.ts +0 -70
- package/src/hooks/useRefQueryOptions.ts +0 -37
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import { createContext } from '@radix-ui/react-context';
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import type * as SchemaAST from 'effect/SchemaAST';
|
|
8
|
-
import React, { type PropsWithChildren, useMemo, useRef } from 'react';
|
|
8
|
+
import React, { type PropsWithChildren, useEffect, useMemo, useRef } from 'react';
|
|
9
9
|
|
|
10
10
|
import { type AnyProperties } from '@dxos/echo/internal';
|
|
11
11
|
import { createJsonPath, getValue as getValue$ } from '@dxos/effect';
|
|
12
12
|
import { IconButton, type IconButtonProps, ScrollArea, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
13
|
-
import { mx } from '@dxos/
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
type FormHandler,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
useKeyHandler,
|
|
21
21
|
} from '../../hooks';
|
|
22
22
|
import { translationKey } from '../../translations';
|
|
23
|
+
import { setValueEchoAware } from '../../util';
|
|
23
24
|
|
|
24
25
|
import { FormFieldLabel, type FormFieldLabelProps, type FormFieldStateProps } from './FormFieldComponent';
|
|
25
26
|
import {
|
|
@@ -76,13 +77,28 @@ const [FormContextProvider, useFormContext] = createContext<FormContextValue>('F
|
|
|
76
77
|
/**
|
|
77
78
|
* Get the current form values.
|
|
78
79
|
*/
|
|
79
|
-
const useFormValues
|
|
80
|
+
const useFormValues: {
|
|
81
|
+
<T extends AnyProperties>(componentName: string, path?: (string | number)[]): T;
|
|
82
|
+
<T extends AnyProperties>(
|
|
83
|
+
componentName: string,
|
|
84
|
+
path: (string | number)[] | undefined,
|
|
85
|
+
defaultValue: () => T,
|
|
86
|
+
): T | undefined;
|
|
87
|
+
} = (componentName: string, path: (string | number)[] = [], defaultValue?: () => any) => {
|
|
80
88
|
const jsonPath = createJsonPath(path);
|
|
81
89
|
const {
|
|
82
90
|
form: { values },
|
|
83
91
|
} = useFormContext(componentName);
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
const value = getValue$(values, jsonPath);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!value && defaultValue) {
|
|
97
|
+
setValueEchoAware(values, jsonPath, defaultValue());
|
|
98
|
+
}
|
|
99
|
+
}, [value, defaultValue]);
|
|
100
|
+
|
|
101
|
+
return value;
|
|
86
102
|
};
|
|
87
103
|
|
|
88
104
|
/**
|
|
@@ -148,30 +164,32 @@ FormRoot.displayName = 'Form.Root';
|
|
|
148
164
|
// Viewport
|
|
149
165
|
//
|
|
150
166
|
|
|
151
|
-
|
|
167
|
+
const FORM_VIEWPORT_NAME = 'Form.Viewport';
|
|
152
168
|
|
|
153
|
-
|
|
169
|
+
type FormViewportProps = PropsWithChildren<{}>;
|
|
170
|
+
|
|
171
|
+
// TODO(burdon): Ref and props (allow asChild).
|
|
172
|
+
const FormViewport = ({ children }: FormViewportProps) => {
|
|
154
173
|
return (
|
|
155
|
-
<ScrollArea.Root>
|
|
156
|
-
<ScrollArea.Viewport
|
|
157
|
-
<ScrollArea.Scrollbar orientation='vertical'>
|
|
158
|
-
<ScrollArea.Thumb />
|
|
159
|
-
</ScrollArea.Scrollbar>
|
|
174
|
+
<ScrollArea.Root orientation='vertical'>
|
|
175
|
+
<ScrollArea.Viewport>{children}</ScrollArea.Viewport>
|
|
160
176
|
</ScrollArea.Root>
|
|
161
177
|
);
|
|
162
178
|
};
|
|
163
179
|
|
|
164
|
-
FormViewport.displayName =
|
|
180
|
+
FormViewport.displayName = FORM_VIEWPORT_NAME;
|
|
165
181
|
|
|
166
182
|
//
|
|
167
183
|
// Content
|
|
168
184
|
//
|
|
169
185
|
|
|
186
|
+
const FORM_CONTENT_NAME = 'Form.Content';
|
|
187
|
+
|
|
170
188
|
type FormContentProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
171
189
|
|
|
172
190
|
// TOOD(burdon): Figure out nesting (indent and testId).
|
|
173
191
|
const FormContent = ({ classNames, children }: FormContentProps) => {
|
|
174
|
-
const { form, testId } = useFormContext(
|
|
192
|
+
const { form, testId } = useFormContext(FORM_CONTENT_NAME);
|
|
175
193
|
const ref = useRef<HTMLDivElement>(null);
|
|
176
194
|
useKeyHandler(ref.current, form);
|
|
177
195
|
|
|
@@ -179,7 +197,7 @@ const FormContent = ({ classNames, children }: FormContentProps) => {
|
|
|
179
197
|
<div
|
|
180
198
|
ref={ref}
|
|
181
199
|
role='form'
|
|
182
|
-
className={mx('flex flex-col is-full pli-cardSpacingInline', classNames)}
|
|
200
|
+
className={mx('flex flex-col is-full pli-cardSpacingInline density-fine', classNames)}
|
|
183
201
|
data-testid={testId}
|
|
184
202
|
>
|
|
185
203
|
{children}
|
|
@@ -187,35 +205,39 @@ const FormContent = ({ classNames, children }: FormContentProps) => {
|
|
|
187
205
|
);
|
|
188
206
|
};
|
|
189
207
|
|
|
190
|
-
FormContent.displayName =
|
|
208
|
+
FormContent.displayName = FORM_CONTENT_NAME;
|
|
191
209
|
|
|
192
210
|
//
|
|
193
211
|
// FieldSet
|
|
194
212
|
//
|
|
195
213
|
|
|
214
|
+
const FORM_FIELDSET_NAME = 'Form.FieldSet';
|
|
215
|
+
|
|
196
216
|
type FormFieldSetProps = ThemedClassName<{}>;
|
|
197
217
|
|
|
198
218
|
const FormFieldSet = ({ classNames }: FormFieldSetProps) => {
|
|
199
|
-
const { form, ...props } = useFormContext(
|
|
219
|
+
const { form, ...props } = useFormContext(FORM_FIELDSET_NAME);
|
|
200
220
|
|
|
201
221
|
return <NaturalFormFieldSet classNames={classNames} schema={form.schema} {...props} />;
|
|
202
222
|
};
|
|
203
223
|
|
|
204
|
-
FormFieldSet.displayName =
|
|
224
|
+
FormFieldSet.displayName = FORM_FIELDSET_NAME;
|
|
205
225
|
|
|
206
226
|
//
|
|
207
227
|
// Actions
|
|
208
228
|
//
|
|
209
229
|
|
|
230
|
+
const FORM_ACTIONS_NAME = 'Form.Actions';
|
|
231
|
+
|
|
210
232
|
type FormActionsProps = ThemedClassName<{}>;
|
|
211
233
|
|
|
212
234
|
const FormActions = ({ classNames }: FormActionsProps) => {
|
|
213
235
|
const { t } = useTranslation(translationKey);
|
|
214
236
|
const {
|
|
215
|
-
form: {
|
|
237
|
+
form: { canSave, onSave, onCancel },
|
|
216
238
|
readonly,
|
|
217
239
|
layout,
|
|
218
|
-
} = useFormContext(
|
|
240
|
+
} = useFormContext(FORM_ACTIONS_NAME);
|
|
219
241
|
|
|
220
242
|
if (readonly || layout === 'static') {
|
|
221
243
|
return null;
|
|
@@ -239,7 +261,7 @@ const FormActions = ({ classNames }: FormActionsProps) => {
|
|
|
239
261
|
<IconButton
|
|
240
262
|
type='submit'
|
|
241
263
|
variant='primary'
|
|
242
|
-
disabled={!
|
|
264
|
+
disabled={!canSave}
|
|
243
265
|
icon='ph--check--regular'
|
|
244
266
|
iconEnd
|
|
245
267
|
label={t('save button label')}
|
|
@@ -251,21 +273,23 @@ const FormActions = ({ classNames }: FormActionsProps) => {
|
|
|
251
273
|
);
|
|
252
274
|
};
|
|
253
275
|
|
|
254
|
-
FormActions.displayName =
|
|
276
|
+
FormActions.displayName = FORM_ACTIONS_NAME;
|
|
255
277
|
|
|
256
278
|
//
|
|
257
279
|
// Submit
|
|
258
280
|
//
|
|
259
281
|
|
|
260
|
-
|
|
282
|
+
const FORM_SUBMIT_NAME = 'Form.Submit';
|
|
283
|
+
|
|
284
|
+
type FormSubmitProps = ThemedClassName<Partial<Pick<IconButtonProps, 'icon' | 'label' | 'disabled'>>>;
|
|
261
285
|
|
|
262
|
-
const FormSubmit = ({ classNames, label, icon }: FormSubmitProps) => {
|
|
286
|
+
const FormSubmit = ({ classNames, label, icon, disabled }: FormSubmitProps) => {
|
|
263
287
|
const { t } = useTranslation(translationKey);
|
|
264
288
|
const {
|
|
265
|
-
form: {
|
|
289
|
+
form: { canSave, onSave },
|
|
266
290
|
readonly,
|
|
267
291
|
layout,
|
|
268
|
-
} = useFormContext(
|
|
292
|
+
} = useFormContext(FORM_SUBMIT_NAME);
|
|
269
293
|
|
|
270
294
|
if (readonly || layout === 'static') {
|
|
271
295
|
return null;
|
|
@@ -277,7 +301,7 @@ const FormSubmit = ({ classNames, label, icon }: FormSubmitProps) => {
|
|
|
277
301
|
classNames='is-full'
|
|
278
302
|
type='submit'
|
|
279
303
|
variant='primary'
|
|
280
|
-
disabled={!
|
|
304
|
+
disabled={disabled ?? !canSave}
|
|
281
305
|
icon={icon ?? 'ph--check--regular'}
|
|
282
306
|
label={label ?? t('save button label')}
|
|
283
307
|
onClick={onSave}
|
|
@@ -287,7 +311,7 @@ const FormSubmit = ({ classNames, label, icon }: FormSubmitProps) => {
|
|
|
287
311
|
);
|
|
288
312
|
};
|
|
289
313
|
|
|
290
|
-
FormSubmit.displayName =
|
|
314
|
+
FormSubmit.displayName = FORM_SUBMIT_NAME;
|
|
291
315
|
|
|
292
316
|
//
|
|
293
317
|
// Form
|
|
@@ -312,5 +336,6 @@ export type {
|
|
|
312
336
|
FormContentProps,
|
|
313
337
|
FormFieldSetProps,
|
|
314
338
|
FormActionsProps,
|
|
339
|
+
FormSubmitProps,
|
|
315
340
|
FormFieldLabelProps as LabelProps,
|
|
316
341
|
};
|
|
@@ -78,18 +78,17 @@ export type FormFieldProps = {
|
|
|
78
78
|
* Function to lookup custom renderers for specific properties.
|
|
79
79
|
*/
|
|
80
80
|
fieldProvider?: FormFieldProvider;
|
|
81
|
-
} & Pick<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
>;
|
|
81
|
+
} & Pick<FormFieldComponentProps, 'autoFocus' | 'readonly' | 'layout'> &
|
|
82
|
+
Pick<
|
|
83
|
+
RefFieldProps,
|
|
84
|
+
| 'createOptionLabel'
|
|
85
|
+
| 'createOptionIcon'
|
|
86
|
+
| 'createInitialValuePath'
|
|
87
|
+
| 'db'
|
|
88
|
+
| 'schemaHook'
|
|
89
|
+
| 'getOptions'
|
|
90
|
+
| 'onCreate'
|
|
91
|
+
>;
|
|
93
92
|
|
|
94
93
|
export const FormField = (props: FormFieldProps) => {
|
|
95
94
|
const {
|
|
@@ -101,12 +100,15 @@ export const FormField = (props: FormFieldProps) => {
|
|
|
101
100
|
fieldProvider,
|
|
102
101
|
readonly,
|
|
103
102
|
layout,
|
|
104
|
-
|
|
103
|
+
|
|
104
|
+
// RefFieldProps
|
|
105
105
|
createOptionLabel,
|
|
106
106
|
createOptionIcon,
|
|
107
107
|
createInitialValuePath,
|
|
108
|
+
db,
|
|
109
|
+
schemaHook,
|
|
110
|
+
getOptions: getRefOptions,
|
|
108
111
|
onCreate,
|
|
109
|
-
onQueryRefOptions,
|
|
110
112
|
} = props;
|
|
111
113
|
const { t } = useTranslation(translationKey);
|
|
112
114
|
const title = getAnnotation<string>(SchemaAST.TitleAnnotationId)(type);
|
|
@@ -190,12 +192,13 @@ export const FormField = (props: FormFieldProps) => {
|
|
|
190
192
|
<RefField
|
|
191
193
|
{...fieldProps}
|
|
192
194
|
{...refProps}
|
|
193
|
-
createSchema={createSchema}
|
|
194
195
|
createOptionLabel={createOptionLabel}
|
|
195
196
|
createOptionIcon={createOptionIcon}
|
|
196
197
|
createInitialValuePath={createInitialValuePath}
|
|
198
|
+
db={db}
|
|
199
|
+
schemaHook={schemaHook}
|
|
200
|
+
getOptions={getRefOptions}
|
|
197
201
|
onCreate={onCreate}
|
|
198
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
199
202
|
/>
|
|
200
203
|
);
|
|
201
204
|
}
|
|
@@ -224,8 +227,11 @@ export const FormField = (props: FormFieldProps) => {
|
|
|
224
227
|
fieldProvider={fieldProvider}
|
|
225
228
|
createOptionLabel={createOptionLabel}
|
|
226
229
|
createOptionIcon={createOptionIcon}
|
|
230
|
+
createInitialValuePath={createInitialValuePath}
|
|
231
|
+
db={db}
|
|
232
|
+
schemaHook={schemaHook}
|
|
233
|
+
getOptions={getRefOptions}
|
|
227
234
|
onCreate={onCreate}
|
|
228
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
229
235
|
/>
|
|
230
236
|
);
|
|
231
237
|
}
|
|
@@ -44,7 +44,7 @@ const Component = ({
|
|
|
44
44
|
const meta: Meta<typeof Component> = {
|
|
45
45
|
title: 'ui/react-ui-form/FormFieldComponent',
|
|
46
46
|
component: Component,
|
|
47
|
-
decorators: [withTheme, withLayout({
|
|
47
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
48
48
|
parameters: {
|
|
49
49
|
layout: 'fullscreen',
|
|
50
50
|
},
|
|
@@ -15,11 +15,12 @@ import React, {
|
|
|
15
15
|
|
|
16
16
|
import { type Format } from '@dxos/echo/internal';
|
|
17
17
|
import { Icon, Input, Tooltip } from '@dxos/react-ui';
|
|
18
|
-
import {
|
|
19
|
-
import { errorText, mx } from '@dxos/react-ui-theme';
|
|
18
|
+
import { errorText } from '@dxos/ui-theme';
|
|
20
19
|
|
|
21
20
|
import { type FormFieldStatus } from '../../hooks';
|
|
22
21
|
|
|
22
|
+
const labelSpacing = 'mbs-inputSpacingBlock mbe-labelSpacingBlock';
|
|
23
|
+
|
|
23
24
|
/**
|
|
24
25
|
* Dynamic props passed to input components.
|
|
25
26
|
*/
|
|
@@ -78,17 +79,15 @@ export type FormFieldLabelProps = {
|
|
|
78
79
|
|
|
79
80
|
export const FormFieldLabel = ({ label, error, readonly, asChild }: FormFieldLabelProps) => {
|
|
80
81
|
const Label = readonly || asChild ? 'span' : Input.Label;
|
|
81
|
-
const labelProps = readonly || asChild ? { className: 'text-description text-sm' } : { classNames: '!mlb-0 text-sm' };
|
|
82
|
-
|
|
83
82
|
return (
|
|
84
|
-
|
|
85
|
-
<Label
|
|
83
|
+
<>
|
|
84
|
+
<Label>{label}</Label>
|
|
86
85
|
{error && (
|
|
87
86
|
<Tooltip.Trigger asChild content={error} side='bottom'>
|
|
88
87
|
<Icon icon='ph--warning--regular' size={4} classNames={errorText} />
|
|
89
88
|
</Tooltip.Trigger>
|
|
90
89
|
)}
|
|
91
|
-
|
|
90
|
+
</>
|
|
92
91
|
);
|
|
93
92
|
};
|
|
94
93
|
|
|
@@ -117,15 +116,17 @@ export const FormFieldWrapper = <T,>(props: FormFieldWrapperProps<T>) => {
|
|
|
117
116
|
const str = String(value ?? '');
|
|
118
117
|
|
|
119
118
|
return (
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<Input.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
<div role='none' className='contents'>
|
|
120
|
+
<Input.Root validationValence={status}>
|
|
121
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} />}
|
|
122
|
+
{layout === 'static' ? <p>{str}</p> : children ? children({ value }) : null}
|
|
123
|
+
{layout === 'full' && (
|
|
124
|
+
<Input.DescriptionAndValidation>
|
|
125
|
+
<Input.Validation>{error}</Input.Validation>
|
|
126
|
+
</Input.DescriptionAndValidation>
|
|
127
|
+
)}
|
|
128
|
+
</Input.Root>
|
|
129
|
+
</div>
|
|
129
130
|
);
|
|
130
131
|
};
|
|
131
132
|
|
|
@@ -7,7 +7,7 @@ import React, { forwardRef, useMemo } from 'react';
|
|
|
7
7
|
import { type AnyProperties } from '@dxos/echo/internal';
|
|
8
8
|
import { type SchemaProperty } from '@dxos/effect';
|
|
9
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
-
import { mx } from '@dxos/
|
|
10
|
+
import { mx } from '@dxos/ui-theme';
|
|
11
11
|
import { isTruthy } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
import { type FormHandlerProps } from '../../hooks';
|
|
@@ -32,18 +32,21 @@ export type FormFieldSetProps<T extends AnyProperties> = ThemedClassName<
|
|
|
32
32
|
| 'projection'
|
|
33
33
|
| 'fieldMap'
|
|
34
34
|
| 'fieldProvider'
|
|
35
|
-
| 'createSchema'
|
|
36
35
|
| 'createOptionLabel'
|
|
37
36
|
| 'createOptionIcon'
|
|
38
37
|
| 'createInitialValuePath'
|
|
38
|
+
| 'db'
|
|
39
|
+
| 'schemaHook'
|
|
40
|
+
| 'getOptions'
|
|
39
41
|
| 'onCreate'
|
|
40
|
-
| 'onQueryRefOptions'
|
|
41
42
|
>
|
|
42
43
|
>;
|
|
43
44
|
|
|
45
|
+
const FORM_FIELDSET_NAME = 'Form.FieldSet';
|
|
46
|
+
|
|
44
47
|
export const FormFieldSet = forwardRef<HTMLDivElement, FormFieldSetProps<any>>(
|
|
45
48
|
({ classNames, label, schema, readonly, path, exclude, sort, projection, layout, ...props }, forwardRef) => {
|
|
46
|
-
const values = useFormValues(
|
|
49
|
+
const values = useFormValues(FORM_FIELDSET_NAME, path);
|
|
47
50
|
|
|
48
51
|
// TODO(burdon): Updates on every value change.
|
|
49
52
|
// Remove values dep if can remove from getSchemaProperties.
|
|
@@ -121,4 +124,4 @@ export const FormFieldSet = forwardRef<HTMLDivElement, FormFieldSetProps<any>>(
|
|
|
121
124
|
},
|
|
122
125
|
);
|
|
123
126
|
|
|
124
|
-
FormFieldSet.displayName =
|
|
127
|
+
FormFieldSet.displayName = FORM_FIELDSET_NAME;
|
|
@@ -7,7 +7,6 @@ import * as SchemaAST from 'effect/SchemaAST';
|
|
|
7
7
|
import React, { Fragment, useCallback } from 'react';
|
|
8
8
|
|
|
9
9
|
import { findNode, getArrayElementType, getDiscriminatedType, isDiscriminatedUnion, isNestedType } from '@dxos/effect';
|
|
10
|
-
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
12
11
|
|
|
13
12
|
import { translationKey } from '../../../translations';
|
|
@@ -33,11 +32,7 @@ export const ArrayField = ({
|
|
|
33
32
|
const { t } = useTranslation(translationKey);
|
|
34
33
|
const elementType = getArrayElementType(type);
|
|
35
34
|
const { onValueChange } = inputProps;
|
|
36
|
-
|
|
37
|
-
// TODO(wittjosiah): The fallback to an empty array stops the form from crashing but isn't immediately live.
|
|
38
|
-
// It doesn't become live until another field is touched, but that's better than the whole form crashing.
|
|
39
|
-
const values = useFormValues(ArrayField.displayName, path) ?? [];
|
|
40
|
-
invariant(Array.isArray(values), `Expected array at: ${path?.join('.')}`);
|
|
35
|
+
const values = useFormValues(ArrayField.displayName, path, () => []);
|
|
41
36
|
|
|
42
37
|
const getDefaultObjectValue = (typeNode: SchemaAST.AST): any => {
|
|
43
38
|
const baseNode = findNode(typeNode, isDiscriminatedUnion);
|
|
@@ -59,29 +54,31 @@ export const ArrayField = ({
|
|
|
59
54
|
const handleAdd = useCallback(() => {
|
|
60
55
|
const defaultValue =
|
|
61
56
|
isNestedType(type) && elementType ? getDefaultObjectValue(elementType) : getDefaultValue(elementType);
|
|
62
|
-
onValueChange(type, [...values, defaultValue]);
|
|
57
|
+
values && onValueChange(type, [...values, defaultValue]);
|
|
63
58
|
}, [onValueChange, type, elementType, values]);
|
|
64
59
|
|
|
65
60
|
const handleDelete = useCallback(
|
|
66
61
|
(idx: number) => {
|
|
67
62
|
onValueChange(
|
|
68
63
|
type,
|
|
69
|
-
values
|
|
64
|
+
values?.filter((_, i) => i !== idx),
|
|
70
65
|
);
|
|
71
66
|
},
|
|
72
67
|
[onValueChange, type, values],
|
|
73
68
|
);
|
|
74
69
|
|
|
75
|
-
if (!elementType || ((readonly || layout === 'static') && values.length < 1)) {
|
|
70
|
+
if (!elementType || ((readonly || layout === 'static') && values && values.length < 1)) {
|
|
76
71
|
return null;
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
return (
|
|
80
75
|
<>
|
|
81
|
-
{(layout !== 'static' || values.length > 0) &&
|
|
76
|
+
{(layout !== 'static' || (values && values.length > 0)) && (
|
|
77
|
+
<FormFieldLabel readonly={readonly} label={label} asChild />
|
|
78
|
+
)}
|
|
82
79
|
|
|
83
80
|
<div role='none' className='flex flex-col gap-2'>
|
|
84
|
-
{values
|
|
81
|
+
{values?.map((_, index) => {
|
|
85
82
|
return (
|
|
86
83
|
<div role='none' key={index} className='grid grid-cols-[1fr_min-content] gap-2 last:mb-3'>
|
|
87
84
|
<FormField
|
|
@@ -143,6 +140,9 @@ export const getDefaultValue = (ast?: SchemaAST.AST): any => {
|
|
|
143
140
|
case 'BooleanKeyword': {
|
|
144
141
|
return false;
|
|
145
142
|
}
|
|
143
|
+
case 'Suspend': {
|
|
144
|
+
return getDefaultValue(ast.f());
|
|
145
|
+
}
|
|
146
146
|
default: {
|
|
147
147
|
if (ast && isNestedType(ast)) {
|
|
148
148
|
return {};
|