@dxos/react-ui-form 0.8.4-main.3f58842 → 0.8.4-main.5ea62a8
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 +556 -508
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +556 -508
- 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 +79 -4
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +74 -4
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +2 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +5 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +126 -30
- 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 +16 -10
- 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/Input.d.ts +4 -1
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +10 -4
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +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 +74 -3
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +73 -3
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +6 -3
- package/dist/types/src/components/testing.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 +9 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -30
- package/src/components/ControlSection/ControlSection.stories.tsx +8 -10
- package/src/components/ControlSection/ControlSection.tsx +26 -12
- package/src/components/FieldEditor/FieldEditor.stories.tsx +8 -9
- package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
- package/src/components/FieldEditor/FieldEditor.tsx +22 -17
- package/src/components/Form/ArrayField.tsx +8 -8
- package/src/components/Form/Defaults.tsx +22 -20
- package/src/components/Form/Form.stories.tsx +129 -93
- package/src/components/Form/Form.tsx +16 -12
- package/src/components/Form/FormActions.tsx +2 -1
- package/src/components/Form/FormContent.tsx +96 -34
- package/src/components/Form/FormContext.tsx +5 -5
- package/src/components/Form/Input.tsx +4 -1
- package/src/components/Form/RefField.tsx +124 -38
- package/src/components/Form/custom/GeoPointInput.tsx +29 -21
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +7 -6
- package/src/components/Form/custom/SelectOptionsInput.tsx +29 -30
- package/src/components/Form/custom/TupleInput.tsx +2 -2
- package/src/components/Form/factory.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +17 -8
- package/src/components/ViewEditor/ViewEditor.test.tsx +7 -10
- package/src/components/ViewEditor/ViewEditor.tsx +90 -107
- package/src/components/index.ts +0 -2
- package/src/components/testing.tsx +15 -9
- package/src/hooks/useForm.ts +5 -3
- package/src/hooks/useRefQueryLookupHandler.ts +1 -1
- package/src/translations.ts +11 -5
- package/src/util/field.ts +8 -18
- package/dist/types/src/components/deprecated/Form.d.ts +0 -15
- package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/index.d.ts +0 -2
- package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
- package/src/components/deprecated/Form.stories.tsx +0 -62
- package/src/components/deprecated/Form.tsx +0 -119
- package/src/components/deprecated/FormInput.tsx +0 -71
- package/src/components/deprecated/index.ts +0 -5
|
@@ -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
|
|
|
@@ -48,20 +49,20 @@ export const ViewEditor = ({
|
|
|
48
49
|
schema,
|
|
49
50
|
view,
|
|
50
51
|
registry,
|
|
51
|
-
readonly
|
|
52
|
+
readonly,
|
|
52
53
|
showHeading = false,
|
|
53
54
|
onTypenameChanged,
|
|
54
55
|
onDelete,
|
|
55
56
|
outerSpacing = true,
|
|
56
57
|
}: ViewEditorProps) => {
|
|
58
|
+
const schemaReadonly = !isMutable(schema);
|
|
57
59
|
const { t } = useTranslation(translationKey);
|
|
58
|
-
const
|
|
60
|
+
const projectionModel = useMemo(() => {
|
|
59
61
|
// Use reactive and mutable version of json schema when schema is mutable.
|
|
60
62
|
const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
|
|
61
63
|
return new ProjectionModel(jsonSchema, view.projection);
|
|
62
64
|
}, [schema, view.projection]);
|
|
63
|
-
const [
|
|
64
|
-
const readonly = _readonly || !isMutable(schema);
|
|
65
|
+
const [expandedField, setExpandedField] = useState<FieldType['id']>();
|
|
65
66
|
|
|
66
67
|
// TODO(burdon): Should be reactive.
|
|
67
68
|
const viewValues = useMemo(() => {
|
|
@@ -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
|
);
|
|
@@ -86,9 +84,9 @@ export const ViewEditor = ({
|
|
|
86
84
|
|
|
87
85
|
const handleAdd = useCallback(() => {
|
|
88
86
|
invariant(!readonly);
|
|
89
|
-
const field =
|
|
90
|
-
|
|
91
|
-
}, [schema,
|
|
87
|
+
const field = projectionModel.createFieldProjection();
|
|
88
|
+
setExpandedField(field.id);
|
|
89
|
+
}, [schema, projectionModel, readonly]);
|
|
92
90
|
|
|
93
91
|
const handleUpdate = useCallback(
|
|
94
92
|
({ typename }: ViewMetaType) => {
|
|
@@ -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,34 +124,33 @@ export const ViewEditor = ({
|
|
|
126
124
|
[view.projection.fields, readonly],
|
|
127
125
|
);
|
|
128
126
|
|
|
129
|
-
const handleClose = useCallback(() =>
|
|
130
|
-
|
|
131
|
-
const hiddenProperties = projection.getHiddenProperties();
|
|
127
|
+
const handleClose = useCallback(() => setExpandedField(undefined), []);
|
|
132
128
|
|
|
133
129
|
const handleHide = useCallback(
|
|
134
130
|
(fieldId: string) => {
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
setExpandedField(undefined);
|
|
132
|
+
projectionModel.hideFieldProjection(fieldId);
|
|
137
133
|
},
|
|
138
|
-
[
|
|
134
|
+
[projectionModel],
|
|
139
135
|
);
|
|
140
136
|
|
|
141
137
|
const handleShow = useCallback(
|
|
142
138
|
(property: string) => {
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
setExpandedField(undefined);
|
|
140
|
+
projectionModel.showFieldProjection(property as JsonProp);
|
|
145
141
|
},
|
|
146
|
-
[
|
|
142
|
+
[projectionModel],
|
|
147
143
|
);
|
|
148
144
|
|
|
149
145
|
return (
|
|
150
146
|
<div role='none' className={mx(classNames)}>
|
|
151
|
-
{
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
{schemaReadonly && (
|
|
148
|
+
<Callout.Root
|
|
149
|
+
valence='info'
|
|
150
|
+
classNames={['is-full mlb-cardSpacingBlock', outerSpacing && 'mli-cardSpacingInline']}
|
|
151
|
+
>
|
|
152
|
+
<Callout.Title>{t('system schema description')}</Callout.Title>
|
|
153
|
+
</Callout.Root>
|
|
157
154
|
)}
|
|
158
155
|
|
|
159
156
|
{/* TODO(burdon): Is the form read-only or just the schema? */}
|
|
@@ -162,98 +159,84 @@ export const ViewEditor = ({
|
|
|
162
159
|
autoSave
|
|
163
160
|
schema={ViewMetaSchema}
|
|
164
161
|
values={viewValues}
|
|
165
|
-
readonly={readonly}
|
|
162
|
+
readonly={readonly ? 'disabled-input' : false}
|
|
166
163
|
onSave={handleUpdate}
|
|
167
164
|
outerSpacing={outerSpacing}
|
|
168
165
|
/>
|
|
169
166
|
|
|
170
167
|
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
171
|
-
<
|
|
168
|
+
<h2 className={mx(inputTextLabel)}>{t('fields label')}</h2>
|
|
172
169
|
|
|
173
170
|
<List.Root<FieldType>
|
|
174
171
|
items={view.projection.fields}
|
|
175
172
|
isItem={Schema.is(FieldSchema)}
|
|
176
173
|
getId={(field) => field.id}
|
|
177
|
-
readonly={readonly}
|
|
178
174
|
onMove={readonly ? undefined : handleMove}
|
|
175
|
+
readonly={readonly}
|
|
179
176
|
>
|
|
180
177
|
{({ items: fields }) => (
|
|
181
178
|
<>
|
|
182
|
-
{showHeading && (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
179
|
+
{showHeading && <h3 className='text-sm'>{t('field path label')}</h3>}
|
|
180
|
+
<div role='list' className={listGrid}>
|
|
181
|
+
{fields?.map((field) => {
|
|
182
|
+
const hidden = field.visible === false;
|
|
183
|
+
return (
|
|
184
|
+
<List.Item<FieldType>
|
|
185
|
+
key={field.id}
|
|
186
|
+
item={field}
|
|
187
|
+
classNames={listItemGrid}
|
|
188
|
+
aria-expanded={expandedField === field.id}
|
|
189
|
+
>
|
|
190
|
+
<div role='none' className={mx(subtleHover, listItemGrid, 'rounded-sm cursor-pointer min-bs-10')}>
|
|
191
|
+
<List.ItemDragHandle disabled={readonly} />
|
|
192
|
+
<List.ItemTitle classNames={hidden && 'text-subdued'} onClick={() => handleToggleField(field)}>
|
|
193
|
+
{field.path}
|
|
194
|
+
</List.ItemTitle>
|
|
195
|
+
<List.ItemButton
|
|
196
|
+
label={t(hidden ? 'show field label' : 'hide field label')}
|
|
197
|
+
data-testid={hidden ? 'show-field-button' : 'hide-field-button'}
|
|
198
|
+
icon={hidden ? 'ph--eye-closed--regular' : 'ph--eye--regular'}
|
|
199
|
+
autoHide={false}
|
|
200
|
+
disabled={readonly || (!hidden && projectionModel.fields.length <= 1)}
|
|
201
|
+
onClick={() => (hidden ? handleShow(field.path) : handleHide(field.id))}
|
|
202
|
+
/>
|
|
203
|
+
{/* TODO(burdon): Remove unless implement undo. */}
|
|
204
|
+
<List.ItemDeleteButton
|
|
205
|
+
label={t('delete field label')}
|
|
206
|
+
autoHide={false}
|
|
207
|
+
disabled={readonly || schemaReadonly || view.projection.fields.length <= 1}
|
|
208
|
+
onClick={() => handleDelete(field.id)}
|
|
209
|
+
data-testid='field.delete'
|
|
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 mbs-1 mbe-1 border border-separator rounded-md'>
|
|
221
|
+
<FieldEditor
|
|
222
|
+
readonly={readonly || schemaReadonly ? 'disabled-input' : false}
|
|
223
|
+
projection={projectionModel}
|
|
224
|
+
field={field}
|
|
225
|
+
registry={registry}
|
|
226
|
+
onSave={handleClose}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</List.Item>
|
|
231
|
+
);
|
|
232
|
+
})}
|
|
214
233
|
</div>
|
|
215
234
|
</>
|
|
216
235
|
)}
|
|
217
236
|
</List.Root>
|
|
218
|
-
|
|
219
|
-
{hiddenProperties.length > 0 && (
|
|
220
|
-
<>
|
|
221
|
-
<label className={mx(inputTextLabel)}>{t('hidden fields label')}</label>
|
|
222
|
-
|
|
223
|
-
<List.Root<string>
|
|
224
|
-
items={hiddenProperties}
|
|
225
|
-
isItem={(item): item is string => typeof item === 'string'}
|
|
226
|
-
getId={(property) => property}
|
|
227
|
-
>
|
|
228
|
-
{({ items: properties }) => (
|
|
229
|
-
<div role='list' className='flex flex-col is-full'>
|
|
230
|
-
{properties?.map((property) => (
|
|
231
|
-
<List.Item<string>
|
|
232
|
-
key={property}
|
|
233
|
-
item={property}
|
|
234
|
-
classNames={mx(grid, ghostHover, !readonly && 'cursor-pointer')}
|
|
235
|
-
>
|
|
236
|
-
<List.ItemDragHandle disabled />
|
|
237
|
-
<List.ItemTitle>{property}</List.ItemTitle>
|
|
238
|
-
<List.ItemButton
|
|
239
|
-
icon='ph--eye--regular'
|
|
240
|
-
onClick={() => handleShow(property)}
|
|
241
|
-
data-testid='show-field-button'
|
|
242
|
-
/>
|
|
243
|
-
</List.Item>
|
|
244
|
-
))}
|
|
245
|
-
</div>
|
|
246
|
-
)}
|
|
247
|
-
</List.Root>
|
|
248
|
-
</>
|
|
249
|
-
)}
|
|
250
237
|
</div>
|
|
251
238
|
|
|
252
|
-
{
|
|
253
|
-
<FieldEditor key={field.id} projection={projection} field={field} registry={registry} onSave={handleClose} />
|
|
254
|
-
)}
|
|
255
|
-
|
|
256
|
-
{!readonly && !field && (
|
|
239
|
+
{!readonly && !expandedField && (
|
|
257
240
|
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
258
241
|
<IconButton
|
|
259
242
|
icon='ph--plus--regular'
|
|
@@ -261,7 +244,7 @@ export const ViewEditor = ({
|
|
|
261
244
|
onClick={readonly ? undefined : handleAdd}
|
|
262
245
|
// TODO(burdon): Show field limit in ux (not tooltip).
|
|
263
246
|
disabled={view.projection.fields.length >= VIEW_FIELD_LIMIT}
|
|
264
|
-
classNames='
|
|
247
|
+
classNames='is-full'
|
|
265
248
|
/>
|
|
266
249
|
</div>
|
|
267
250
|
)}
|
package/src/components/index.ts
CHANGED
|
@@ -9,16 +9,22 @@ import { Card } from '@dxos/react-ui-stack';
|
|
|
9
9
|
import { JsonFilter } from '@dxos/react-ui-syntax-highlighter';
|
|
10
10
|
import { mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
<div className='w-full h-full grid grid-cols-[1fr_1fr] gap-2 overflow-hidden'>
|
|
14
|
-
<div className={mx('p-4 justify-center overflow-y-auto', classNames)}>{children}</div>
|
|
15
|
-
<JsonFilter data={json} classNames='w-full text-xs' testId='debug' />
|
|
16
|
-
</div>
|
|
17
|
-
);
|
|
12
|
+
type TestLayoutProps = ThemedClassName<PropsWithChildren<{ json?: any }>>;
|
|
18
13
|
|
|
19
|
-
export const
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
export const TestLayout = ({ classNames, children, json }: TestLayoutProps) => {
|
|
15
|
+
return (
|
|
16
|
+
<div className='w-full h-full grid grid-cols-[1fr_1fr] gap-2 overflow-hidden'>
|
|
17
|
+
<div className={mx('p-4 justify-center overflow-y-auto', classNames)}>{children}</div>
|
|
18
|
+
<JsonFilter data={json} classNames='w-full text-xs' testId='debug' />
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type TestPanelProps = ThemedClassName<PropsWithChildren>;
|
|
24
|
+
|
|
25
|
+
export const TestPanel = ({ classNames, children }: TestPanelProps) => {
|
|
26
|
+
return <Card.StaticRoot classNames={[textBlockWidth, classNames]}>{children}</Card.StaticRoot>;
|
|
27
|
+
};
|
|
22
28
|
|
|
23
29
|
// Symbol for accessing debug objects in tests.
|
|
24
30
|
export const VIEW_EDITOR_DEBUG_SYMBOL = Symbol('viewEditorDebug');
|
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,11 @@ 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
|
+
'show field label': 'Show field',
|
|
29
|
+
'hide field label': 'Hide field',
|
|
30
|
+
'delete field label': 'Delete field',
|
|
31
|
+
'ref field placeholder_one': 'Select…',
|
|
32
|
+
'ref field placeholder_other': 'Select items…',
|
|
28
33
|
|
|
29
34
|
// TODO(burdon): Factor out?
|
|
30
35
|
'button cancel': 'Cancel',
|
|
@@ -60,9 +65,9 @@ export const translations = [
|
|
|
60
65
|
'format url': 'URL',
|
|
61
66
|
'format uuid': 'UUID',
|
|
62
67
|
|
|
63
|
-
//
|
|
64
|
-
'
|
|
65
|
-
'
|
|
68
|
+
// GeoPointInput.
|
|
69
|
+
'latitude label': 'Latitude',
|
|
70
|
+
'longitude label': 'Longitude',
|
|
66
71
|
|
|
67
72
|
// SelectOptionsInput.
|
|
68
73
|
'select option label': 'Label',
|
|
@@ -72,8 +77,9 @@ export const translations = [
|
|
|
72
77
|
'select option add': 'Add option',
|
|
73
78
|
|
|
74
79
|
// System schema message.
|
|
75
|
-
'system schema title': 'System
|
|
76
|
-
'system schema description':
|
|
80
|
+
'system schema title': 'System record type',
|
|
81
|
+
'system schema description':
|
|
82
|
+
'This is a system record type and cannot be modified, though the view can still be configured.',
|
|
77
83
|
},
|
|
78
84
|
},
|
|
79
85
|
},
|
package/src/util/field.ts
CHANGED
|
@@ -112,24 +112,7 @@ export type CellClassesForFieldTypeProps = {
|
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
export const cellClassesForFieldType = ({ type, format }: CellClassesForFieldTypeProps): string[] | undefined => {
|
|
115
|
-
|
|
116
|
-
switch (type) {
|
|
117
|
-
case TypeEnum.Number:
|
|
118
|
-
return ['text-right', 'font-mono'];
|
|
119
|
-
case TypeEnum.Boolean:
|
|
120
|
-
return ['text-right', 'font-mono'];
|
|
121
|
-
case TypeEnum.String:
|
|
122
|
-
return undefined;
|
|
123
|
-
case TypeEnum.Ref:
|
|
124
|
-
return undefined;
|
|
125
|
-
|
|
126
|
-
default: {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
switch (format) {
|
|
115
|
+
switch (format || type) {
|
|
133
116
|
case FormatEnum.Markdown:
|
|
134
117
|
return undefined;
|
|
135
118
|
case FormatEnum.Time:
|
|
@@ -143,6 +126,13 @@ export const cellClassesForFieldType = ({ type, format }: CellClassesForFieldTyp
|
|
|
143
126
|
case FormatEnum.JSON:
|
|
144
127
|
case FormatEnum.DID:
|
|
145
128
|
return ['font-mono'];
|
|
129
|
+
case TypeEnum.Number:
|
|
130
|
+
case TypeEnum.Boolean:
|
|
131
|
+
return ['text-right', 'font-mono'];
|
|
132
|
+
case TypeEnum.String:
|
|
133
|
+
return undefined;
|
|
134
|
+
case TypeEnum.Ref:
|
|
135
|
+
return undefined;
|
|
146
136
|
default:
|
|
147
137
|
return undefined;
|
|
148
138
|
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type Schema } from 'effect';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
4
|
-
import { type Projection } from '@dxos/schema';
|
|
5
|
-
export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
|
|
6
|
-
projection: Projection;
|
|
7
|
-
object: T;
|
|
8
|
-
schema?: Schema.Schema<T>;
|
|
9
|
-
readonly?: boolean;
|
|
10
|
-
}>;
|
|
11
|
-
/**
|
|
12
|
-
* Schema-based object form.
|
|
13
|
-
*/
|
|
14
|
-
export declare const DeprecatedForm: <T extends {} = {}>({ classNames, projection, object, schema, readonly, }: DeprecatedFormProps<T>) => React.JSX.Element;
|
|
15
|
-
//# sourceMappingURL=Form.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,EAAS,KAAK,eAAe,EAA+C,MAAM,gBAAgB,CAAC;AAE1G,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE7E,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,eAAe,CAAC;IACnE,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,uDAM/C,mBAAmB,CAAC,CAAC,CAAC,sBA0DxB,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import '@dxos-theme';
|
|
2
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { type TestType } from '@dxos/schema/testing';
|
|
5
|
-
import { type DeprecatedFormProps } from './Form';
|
|
6
|
-
declare const DefaultStory: (props: DeprecatedFormProps) => React.JSX.Element;
|
|
7
|
-
declare const meta: Meta<typeof DefaultStory>;
|
|
8
|
-
export default meta;
|
|
9
|
-
type Story = StoryObj<DeprecatedFormProps<TestType>>;
|
|
10
|
-
export declare const Default: Story;
|
|
11
|
-
export declare const Empty: Story;
|
|
12
|
-
export declare const Readonly: Story;
|
|
13
|
-
//# sourceMappingURL=Form.stories.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAIlE,QAAA,MAAM,YAAY,GAAI,OAAO,mBAAmB,sBAM/C,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAQnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAErD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type JSX, type PropsWithChildren } from 'react';
|
|
2
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
/**
|
|
4
|
-
* @deprecated
|
|
5
|
-
*/
|
|
6
|
-
export declare const DeprecatedFormContainer: ({ children, classNames }: ThemedClassName<PropsWithChildren>) => JSX.Element;
|
|
7
|
-
export type DeprecatedFormInputProps = {
|
|
8
|
-
label: string;
|
|
9
|
-
description?: JSX.Element;
|
|
10
|
-
secondary?: JSX.Element;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated
|
|
14
|
-
*/
|
|
15
|
-
export declare const DeprecatedFormInput: ({ label, description, secondary, children, }: PropsWithChildren<DeprecatedFormInputProps>) => JSX.Element;
|
|
16
|
-
//# sourceMappingURL=FormInput.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/FormInput.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,GAAG,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG7D;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,gBAQnG,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,8CAKjC,iBAAiB,CAAC,wBAAwB,CAAC,gBAkC7C,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import React from 'react';
|
|
9
|
-
|
|
10
|
-
import { live } from '@dxos/live-object';
|
|
11
|
-
import { testData, TestSchema, testView, type TestType } from '@dxos/schema/testing';
|
|
12
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
|
-
|
|
14
|
-
import { DeprecatedForm, type DeprecatedFormProps } from './Form';
|
|
15
|
-
import { translations } from '../../translations';
|
|
16
|
-
import { TestLayout, TestPanel } from '../testing';
|
|
17
|
-
|
|
18
|
-
const DefaultStory = (props: DeprecatedFormProps) => (
|
|
19
|
-
<TestLayout json={{ props }}>
|
|
20
|
-
<TestPanel>
|
|
21
|
-
<DeprecatedForm {...props} />
|
|
22
|
-
</TestPanel>
|
|
23
|
-
</TestLayout>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const meta: Meta<typeof DefaultStory> = {
|
|
27
|
-
title: 'ui/react-ui-form/DeprecatedForm',
|
|
28
|
-
component: DeprecatedForm,
|
|
29
|
-
render: DefaultStory,
|
|
30
|
-
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
31
|
-
parameters: {
|
|
32
|
-
translations,
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default meta;
|
|
37
|
-
|
|
38
|
-
type Story = StoryObj<DeprecatedFormProps<TestType>>;
|
|
39
|
-
|
|
40
|
-
export const Default: Story = {
|
|
41
|
-
args: {
|
|
42
|
-
object: live(testData),
|
|
43
|
-
projection: live(testView).projection,
|
|
44
|
-
schema: TestSchema,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const Empty: Story = {
|
|
49
|
-
args: {
|
|
50
|
-
projection: live(testView).projection,
|
|
51
|
-
schema: TestSchema,
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const Readonly: Story = {
|
|
56
|
-
args: {
|
|
57
|
-
object: live(testData),
|
|
58
|
-
projection: live(testView).projection,
|
|
59
|
-
schema: TestSchema,
|
|
60
|
-
readonly: true,
|
|
61
|
-
},
|
|
62
|
-
};
|