@dxos/react-ui-form 0.8.4-main.c1de068 → 0.8.4-main.f5c0578
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 +168 -149
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +168 -149
- 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.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +2 -2
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +1 -1
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +1 -1
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -29
- package/src/components/ControlSection/ControlSection.stories.tsx +2 -1
- package/src/components/FieldEditor/FieldEditor.stories.tsx +4 -3
- package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
- package/src/components/FieldEditor/FieldEditor.tsx +16 -6
- package/src/components/Form/ArrayField.tsx +7 -6
- package/src/components/Form/Defaults.tsx +2 -1
- package/src/components/Form/Form.stories.tsx +4 -3
- package/src/components/Form/Form.tsx +4 -3
- package/src/components/Form/FormActions.tsx +2 -1
- package/src/components/Form/FormContent.tsx +6 -5
- package/src/components/Form/FormContext.tsx +5 -5
- package/src/components/Form/RefField.tsx +6 -5
- package/src/components/Form/custom/GeoPointInput.tsx +2 -2
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +3 -2
- package/src/components/Form/custom/SelectOptionsInput.tsx +28 -29
- package/src/components/Form/factory.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +5 -4
- package/src/components/ViewEditor/ViewEditor.test.tsx +5 -4
- package/src/components/ViewEditor/ViewEditor.tsx +69 -54
- package/src/components/deprecated/Form.stories.tsx +3 -2
- package/src/components/deprecated/Form.tsx +3 -3
- package/src/hooks/useForm.ts +5 -3
- package/src/hooks/useRefQueryLookupHandler.ts +1 -1
- package/src/translations.ts +3 -2
|
@@ -11,18 +11,19 @@ import { invariant } from '@dxos/invariant';
|
|
|
11
11
|
import { Callout, IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
12
12
|
import { List } from '@dxos/react-ui-list';
|
|
13
13
|
import { cardSpacing } from '@dxos/react-ui-stack';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { inputTextLabel, mx, subtleHover } from '@dxos/react-ui-theme';
|
|
15
|
+
import { type DataType, FieldSchema, type FieldType, ProjectionModel, VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
16
16
|
|
|
17
17
|
import { translationKey } from '../../translations';
|
|
18
18
|
import { FieldEditor } from '../FieldEditor';
|
|
19
19
|
import { Form, type FormProps } from '../Form';
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const listGrid = 'grid grid-cols-[min-content_1fr_min-content_min-content_min-content]';
|
|
22
|
+
const listItemGrid = 'grid grid-cols-subgrid col-span-5';
|
|
22
23
|
|
|
23
24
|
const ViewMetaSchema = Schema.Struct({
|
|
24
25
|
typename: Format.URL.annotations({
|
|
25
|
-
title: '
|
|
26
|
+
title: 'Record type',
|
|
26
27
|
}),
|
|
27
28
|
}).pipe(Schema.mutable);
|
|
28
29
|
|
|
@@ -60,7 +61,7 @@ export const ViewEditor = ({
|
|
|
60
61
|
const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
|
|
61
62
|
return new ProjectionModel(jsonSchema, view.projection);
|
|
62
63
|
}, [schema, view.projection]);
|
|
63
|
-
const [
|
|
64
|
+
const [expandedField, setExpandedField] = useState<FieldType['id']>();
|
|
64
65
|
const readonly = _readonly || !isMutable(schema);
|
|
65
66
|
|
|
66
67
|
// TODO(burdon): Should be reactive.
|
|
@@ -72,12 +73,9 @@ export const ViewEditor = ({
|
|
|
72
73
|
};
|
|
73
74
|
}, [view.query.typename]);
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
+
const handleToggleField = useCallback(
|
|
76
77
|
(field: FieldType) => {
|
|
77
|
-
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
setField((f) => (f === field ? undefined : field));
|
|
78
|
+
setExpandedField((prevExpandedFieldId) => (prevExpandedFieldId === field.id ? undefined : field.id));
|
|
81
79
|
},
|
|
82
80
|
[readonly],
|
|
83
81
|
);
|
|
@@ -87,7 +85,7 @@ export const ViewEditor = ({
|
|
|
87
85
|
const handleAdd = useCallback(() => {
|
|
88
86
|
invariant(!readonly);
|
|
89
87
|
const field = projection.createFieldProjection();
|
|
90
|
-
|
|
88
|
+
setExpandedField(field.id);
|
|
91
89
|
}, [schema, projection, readonly]);
|
|
92
90
|
|
|
93
91
|
const handleUpdate = useCallback(
|
|
@@ -105,13 +103,13 @@ export const ViewEditor = ({
|
|
|
105
103
|
const handleDelete = useCallback(
|
|
106
104
|
(fieldId: string) => {
|
|
107
105
|
invariant(!readonly);
|
|
108
|
-
if (fieldId ===
|
|
109
|
-
|
|
106
|
+
if (fieldId === expandedField) {
|
|
107
|
+
setExpandedField(undefined);
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
onDelete?.(fieldId);
|
|
113
111
|
},
|
|
114
|
-
[
|
|
112
|
+
[expandedField, onDelete, readonly],
|
|
115
113
|
);
|
|
116
114
|
|
|
117
115
|
const handleMove = useCallback(
|
|
@@ -126,13 +124,13 @@ export const ViewEditor = ({
|
|
|
126
124
|
[view.projection.fields, readonly],
|
|
127
125
|
);
|
|
128
126
|
|
|
129
|
-
const handleClose = useCallback(() =>
|
|
127
|
+
const handleClose = useCallback(() => setExpandedField(undefined), []);
|
|
130
128
|
|
|
131
129
|
const hiddenProperties = projection.getHiddenProperties();
|
|
132
130
|
|
|
133
131
|
const handleHide = useCallback(
|
|
134
132
|
(fieldId: string) => {
|
|
135
|
-
|
|
133
|
+
setExpandedField(undefined);
|
|
136
134
|
projection.hideFieldProjection(fieldId);
|
|
137
135
|
},
|
|
138
136
|
[projection],
|
|
@@ -140,7 +138,7 @@ export const ViewEditor = ({
|
|
|
140
138
|
|
|
141
139
|
const handleShow = useCallback(
|
|
142
140
|
(property: string) => {
|
|
143
|
-
|
|
141
|
+
setExpandedField(undefined);
|
|
144
142
|
projection.showFieldProjection(property as JsonProp);
|
|
145
143
|
},
|
|
146
144
|
[projection],
|
|
@@ -149,11 +147,12 @@ export const ViewEditor = ({
|
|
|
149
147
|
return (
|
|
150
148
|
<div role='none' className={mx(classNames)}>
|
|
151
149
|
{readonly && (
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
<Callout.Root
|
|
151
|
+
valence='info'
|
|
152
|
+
classNames={['is-full mlb-cardSpacingBlock', outerSpacing && 'mli-cardSpacingInline']}
|
|
153
|
+
>
|
|
154
|
+
<Callout.Title>{t('system schema description')}</Callout.Title>
|
|
155
|
+
</Callout.Root>
|
|
157
156
|
)}
|
|
158
157
|
|
|
159
158
|
{/* TODO(burdon): Is the form read-only or just the schema? */}
|
|
@@ -168,46 +167,65 @@ export const ViewEditor = ({
|
|
|
168
167
|
/>
|
|
169
168
|
|
|
170
169
|
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
171
|
-
<
|
|
170
|
+
<h2 className={mx(inputTextLabel)}>{t('fields label')}</h2>
|
|
172
171
|
|
|
173
172
|
<List.Root<FieldType>
|
|
174
173
|
items={view.projection.fields}
|
|
175
174
|
isItem={Schema.is(FieldSchema)}
|
|
176
175
|
getId={(field) => field.id}
|
|
177
|
-
readonly={readonly}
|
|
178
176
|
onMove={readonly ? undefined : handleMove}
|
|
177
|
+
readonly={readonly}
|
|
179
178
|
>
|
|
180
179
|
{({ items: fields }) => (
|
|
181
180
|
<>
|
|
182
|
-
{showHeading && (
|
|
183
|
-
<div role='heading' className={grid}>
|
|
184
|
-
<div />
|
|
185
|
-
<div className='flex items-center text-sm'>{t('field path label')}</div>
|
|
186
|
-
</div>
|
|
187
|
-
)}
|
|
181
|
+
{showHeading && <h3 className='text-sm'>{t('field path label')}</h3>}
|
|
188
182
|
|
|
189
|
-
<div role='list' className=
|
|
183
|
+
<div role='list' className={listGrid}>
|
|
190
184
|
{fields?.map((field) => (
|
|
191
185
|
<List.Item<FieldType>
|
|
192
186
|
key={field.id}
|
|
193
187
|
item={field}
|
|
194
|
-
classNames={
|
|
188
|
+
classNames={listItemGrid}
|
|
189
|
+
aria-expanded={expandedField === field.id}
|
|
195
190
|
>
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
191
|
+
<div role='none' className={mx(subtleHover, listItemGrid, 'rounded-sm cursor-pointer min-bs-10')}>
|
|
192
|
+
<List.ItemDragHandle disabled={readonly} />
|
|
193
|
+
<List.ItemTitle onClick={() => handleToggleField(field)}>{field.path}</List.ItemTitle>
|
|
194
|
+
<List.ItemButton
|
|
195
|
+
label={t('hide field label')}
|
|
196
|
+
data-testid='hide-field-button'
|
|
197
|
+
icon='ph--eye-slash--regular'
|
|
198
|
+
autoHide={false}
|
|
199
|
+
// TDOO(burdon): Is this the correct test?
|
|
200
|
+
disabled={readonly || view.projection.fields.length <= 1}
|
|
201
|
+
onClick={() => handleHide(field.id)}
|
|
202
|
+
/>
|
|
203
|
+
{/* TODO(burdon): Remove unless implement undo. */}
|
|
207
204
|
<List.ItemDeleteButton
|
|
208
|
-
|
|
205
|
+
label={t('delete field label')}
|
|
206
|
+
autoHide={false}
|
|
207
|
+
disabled={readonly || view.projection.fields.length <= 1}
|
|
209
208
|
onClick={() => handleDelete(field.id)}
|
|
209
|
+
data-testid='field.delete'
|
|
210
210
|
/>
|
|
211
|
+
<IconButton
|
|
212
|
+
iconOnly
|
|
213
|
+
variant='ghost'
|
|
214
|
+
label={t('toggle expand label', { ns: 'os' })}
|
|
215
|
+
icon={expandedField === field.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
|
|
216
|
+
onClick={() => handleToggleField(field)}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
{expandedField === field.id && (
|
|
220
|
+
<div role='none' className='col-span-5'>
|
|
221
|
+
<FieldEditor
|
|
222
|
+
readonly={readonly}
|
|
223
|
+
projection={projection}
|
|
224
|
+
field={field}
|
|
225
|
+
registry={registry}
|
|
226
|
+
onSave={handleClose}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
211
229
|
)}
|
|
212
230
|
</List.Item>
|
|
213
231
|
))}
|
|
@@ -218,7 +236,7 @@ export const ViewEditor = ({
|
|
|
218
236
|
|
|
219
237
|
{hiddenProperties.length > 0 && (
|
|
220
238
|
<>
|
|
221
|
-
<
|
|
239
|
+
<h2 className={mx(inputTextLabel)}>{t('hidden fields label')}</h2>
|
|
222
240
|
|
|
223
241
|
<List.Root<string>
|
|
224
242
|
items={hiddenProperties}
|
|
@@ -226,16 +244,17 @@ export const ViewEditor = ({
|
|
|
226
244
|
getId={(property) => property}
|
|
227
245
|
>
|
|
228
246
|
{({ items: properties }) => (
|
|
229
|
-
<div role='list' className='
|
|
247
|
+
<div role='list' className='grid grid-cols-[1fr_min-content]'>
|
|
230
248
|
{properties?.map((property) => (
|
|
231
249
|
<List.Item<string>
|
|
232
250
|
key={property}
|
|
233
251
|
item={property}
|
|
234
|
-
classNames={mx(grid
|
|
252
|
+
classNames={mx('grid grid-cols-subgrid col-span-2 rounded-sm', subtleHover)}
|
|
235
253
|
>
|
|
236
|
-
<List.ItemDragHandle disabled />
|
|
237
254
|
<List.ItemTitle>{property}</List.ItemTitle>
|
|
238
255
|
<List.ItemButton
|
|
256
|
+
label={t('show field label')}
|
|
257
|
+
disabled={readonly}
|
|
239
258
|
icon='ph--eye--regular'
|
|
240
259
|
onClick={() => handleShow(property)}
|
|
241
260
|
data-testid='show-field-button'
|
|
@@ -249,11 +268,7 @@ export const ViewEditor = ({
|
|
|
249
268
|
)}
|
|
250
269
|
</div>
|
|
251
270
|
|
|
252
|
-
{
|
|
253
|
-
<FieldEditor key={field.id} projection={projection} field={field} registry={registry} onSave={handleClose} />
|
|
254
|
-
)}
|
|
255
|
-
|
|
256
|
-
{!readonly && !field && (
|
|
271
|
+
{!readonly && !expandedField && (
|
|
257
272
|
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
258
273
|
<IconButton
|
|
259
274
|
icon='ph--plus--regular'
|
|
@@ -261,7 +276,7 @@ export const ViewEditor = ({
|
|
|
261
276
|
onClick={readonly ? undefined : handleAdd}
|
|
262
277
|
// TODO(burdon): Show field limit in ux (not tooltip).
|
|
263
278
|
disabled={view.projection.fields.length >= VIEW_FIELD_LIMIT}
|
|
264
|
-
classNames='
|
|
279
|
+
classNames='is-full'
|
|
265
280
|
/>
|
|
266
281
|
</div>
|
|
267
282
|
)}
|
|
@@ -8,13 +8,14 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
|
|
10
10
|
import { live } from '@dxos/live-object';
|
|
11
|
-
import {
|
|
11
|
+
import { TestSchema, type TestType, testData, testView } from '@dxos/schema/testing';
|
|
12
12
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
13
|
|
|
14
|
-
import { DeprecatedForm, type DeprecatedFormProps } from './Form';
|
|
15
14
|
import { translations } from '../../translations';
|
|
16
15
|
import { TestLayout, TestPanel } from '../testing';
|
|
17
16
|
|
|
17
|
+
import { DeprecatedForm, type DeprecatedFormProps } from './Form';
|
|
18
|
+
|
|
18
19
|
const DefaultStory = (props: DeprecatedFormProps) => (
|
|
19
20
|
<TestLayout json={{ props }}>
|
|
20
21
|
<TestPanel>
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Option, type Schema, SchemaAST, pipe } from 'effect';
|
|
6
6
|
import React, { useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { findProperty } from '@dxos/effect';
|
|
9
|
-
import { Input, type
|
|
9
|
+
import { Input, type TextInputProps as NativeTextInputProps, type ThemedClassName } from '@dxos/react-ui';
|
|
10
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
11
|
-
import { getFieldValue, setFieldValue
|
|
11
|
+
import { type Projection, getFieldValue, setFieldValue } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
|
|
14
14
|
projection: Projection;
|
package/src/hooks/useForm.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type Schema, SchemaAST } from 'effect';
|
|
6
6
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type BaseObject, getValue, setValue } from '@dxos/echo-schema';
|
|
9
|
-
import { type
|
|
9
|
+
import { type JsonPath, type SimpleType, createJsonPath, fromEffectValidationPath } from '@dxos/effect';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
|
-
import {
|
|
12
|
+
import { type ValidationError, validateSchema } from '@dxos/schema';
|
|
13
13
|
import { type MaybePromise } from '@dxos/util';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -26,6 +26,8 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
26
26
|
changed: Record<JsonPath, boolean>;
|
|
27
27
|
canSave: boolean;
|
|
28
28
|
formIsValid: boolean;
|
|
29
|
+
|
|
30
|
+
// TODO(burdon): Return onSave.
|
|
29
31
|
handleSave: () => void;
|
|
30
32
|
|
|
31
33
|
//
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { type TypeAnnotation, getObjectDXN } from '@dxos/echo-schema';
|
|
8
8
|
import { Filter, type Space } from '@dxos/react-client/echo';
|
|
9
9
|
import { isNonNullable } from '@dxos/util';
|
|
10
10
|
|
package/src/translations.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const translations = [
|
|
|
25
25
|
'field limit reached': 'Maximum number of fields reached',
|
|
26
26
|
'boolean input true value': 'Yes',
|
|
27
27
|
'boolean input false value': 'No',
|
|
28
|
+
'delete field label': 'Delete field',
|
|
28
29
|
|
|
29
30
|
// TODO(burdon): Factor out?
|
|
30
31
|
'button cancel': 'Cancel',
|
|
@@ -72,8 +73,8 @@ export const translations = [
|
|
|
72
73
|
'select option add': 'Add option',
|
|
73
74
|
|
|
74
75
|
// System schema message.
|
|
75
|
-
'system schema title': 'System
|
|
76
|
-
'system schema description': 'This is a system
|
|
76
|
+
'system schema title': 'System record type',
|
|
77
|
+
'system schema description': 'This is a system record type and cannot be modified.',
|
|
77
78
|
},
|
|
78
79
|
},
|
|
79
80
|
},
|