@dxos/react-ui-form 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d
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 +223 -205
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +223 -205
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +5 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +5 -5
- package/dist/types/src/components/Form/ArrayField.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 +47 -33
- 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.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +3 -3
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +2 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +1 -0
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +5 -5
- package/dist/types/src/components/Form/factory.d.ts +2 -2
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +6 -5
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +11 -9
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +8 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +4 -4
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +1 -0
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +5 -5
- package/dist/types/src/util/field.d.ts +3 -3
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts +2 -2
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -31
- package/src/components/ControlSection/ControlSection.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
- package/src/components/FieldEditor/FieldEditor.tsx +3 -3
- package/src/components/Form/ArrayField.tsx +3 -1
- package/src/components/Form/Defaults.tsx +1 -1
- package/src/components/Form/Form.stories.tsx +24 -24
- package/src/components/Form/Form.tsx +8 -5
- package/src/components/Form/FormActions.tsx +7 -2
- package/src/components/Form/FormContent.tsx +53 -13
- package/src/components/Form/FormContext.tsx +4 -4
- package/src/components/Form/Input.tsx +2 -2
- package/src/components/Form/RefField.tsx +16 -2
- package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
- package/src/components/Form/factory.tsx +7 -4
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +25 -25
- package/src/components/ObjectPicker/ObjectPicker.tsx +14 -14
- package/src/components/ViewEditor/ViewEditor.stories.tsx +46 -32
- package/src/components/ViewEditor/ViewEditor.test.tsx +4 -3
- package/src/components/ViewEditor/ViewEditor.tsx +41 -97
- package/src/hooks/useForm.ts +8 -7
- package/src/hooks/useQueryRefOptions.ts +6 -3
- package/src/hooks/useRefQueryLookupHandler.ts +5 -5
- package/src/translations.ts +5 -5
- package/src/util/field.ts +25 -25
- package/src/util/formatting.ts +22 -22
- package/src/util/refs.ts +6 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-form",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.66e292d",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -32,50 +32,51 @@
|
|
|
32
32
|
"@radix-ui/react-context": "1.1.1",
|
|
33
33
|
"date-fns": "^3.3.1",
|
|
34
34
|
"jsonpath": "^1.1.1",
|
|
35
|
-
"@dxos/async": "0.8.4-main.
|
|
36
|
-
"@dxos/debug": "0.8.4-main.
|
|
37
|
-
"@dxos/echo": "0.8.4-main.
|
|
38
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
39
|
-
"@dxos/echo-query": "0.8.4-main.
|
|
40
|
-
"@dxos/effect": "0.8.4-main.
|
|
41
|
-
"@dxos/invariant": "0.8.4-main.
|
|
42
|
-
"@dxos/keys": "0.8.4-main.
|
|
43
|
-
"@dxos/lit-ui": "0.8.4-main.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/react-ui-
|
|
48
|
-
"@dxos/react-ui-
|
|
49
|
-
"@dxos/react-ui-
|
|
50
|
-
"@dxos/react-ui-
|
|
51
|
-
"@dxos/react-ui-
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
35
|
+
"@dxos/async": "0.8.4-main.66e292d",
|
|
36
|
+
"@dxos/debug": "0.8.4-main.66e292d",
|
|
37
|
+
"@dxos/echo": "0.8.4-main.66e292d",
|
|
38
|
+
"@dxos/echo-db": "0.8.4-main.66e292d",
|
|
39
|
+
"@dxos/echo-query": "0.8.4-main.66e292d",
|
|
40
|
+
"@dxos/effect": "0.8.4-main.66e292d",
|
|
41
|
+
"@dxos/invariant": "0.8.4-main.66e292d",
|
|
42
|
+
"@dxos/keys": "0.8.4-main.66e292d",
|
|
43
|
+
"@dxos/lit-ui": "0.8.4-main.66e292d",
|
|
44
|
+
"@dxos/log": "0.8.4-main.66e292d",
|
|
45
|
+
"@dxos/live-object": "0.8.4-main.66e292d",
|
|
46
|
+
"@dxos/react-client": "0.8.4-main.66e292d",
|
|
47
|
+
"@dxos/react-ui-components": "0.8.4-main.66e292d",
|
|
48
|
+
"@dxos/react-ui-editor": "0.8.4-main.66e292d",
|
|
49
|
+
"@dxos/react-ui-list": "0.8.4-main.66e292d",
|
|
50
|
+
"@dxos/react-ui-pickers": "0.8.4-main.66e292d",
|
|
51
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.66e292d",
|
|
52
|
+
"@dxos/react-ui-stack": "0.8.4-main.66e292d",
|
|
53
|
+
"@dxos/react-ui-types": "0.8.4-main.66e292d",
|
|
54
|
+
"@dxos/schema": "0.8.4-main.66e292d",
|
|
55
|
+
"@dxos/util": "0.8.4-main.66e292d",
|
|
56
|
+
"@dxos/types": "0.8.4-main.66e292d"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@types/jsonpath": "^0.2.4",
|
|
59
60
|
"@types/react": "~19.2.2",
|
|
60
|
-
"@types/react-dom": "~19.2.
|
|
61
|
+
"@types/react-dom": "~19.2.2",
|
|
61
62
|
"effect": "3.18.3",
|
|
62
63
|
"fast-check": "^3.19.0",
|
|
63
64
|
"react": "~19.2.0",
|
|
64
65
|
"react-dom": "~19.2.0",
|
|
65
66
|
"vite": "7.1.9",
|
|
66
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
67
|
-
"@dxos/random": "0.8.4-main.
|
|
68
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/react-ui
|
|
67
|
+
"@dxos/echo-signals": "0.8.4-main.66e292d",
|
|
68
|
+
"@dxos/random": "0.8.4-main.66e292d",
|
|
69
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.66e292d",
|
|
70
|
+
"@dxos/storybook-utils": "0.8.4-main.66e292d",
|
|
71
|
+
"@dxos/react-ui-theme": "0.8.4-main.66e292d",
|
|
72
|
+
"@dxos/react-ui": "0.8.4-main.66e292d"
|
|
72
73
|
},
|
|
73
74
|
"peerDependencies": {
|
|
74
75
|
"effect": "^3.13.3",
|
|
75
76
|
"react": "^19.0.0",
|
|
76
77
|
"react-dom": "^19.0.0",
|
|
77
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
78
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
78
|
+
"@dxos/react-ui": "0.8.4-main.66e292d",
|
|
79
|
+
"@dxos/react-ui-theme": "0.8.4-main.66e292d"
|
|
79
80
|
},
|
|
80
81
|
"publishConfig": {
|
|
81
82
|
"access": "public"
|
|
@@ -9,7 +9,7 @@ import { createEchoSchema } from '@dxos/echo/testing';
|
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
import { ProjectionModel } from '@dxos/schema';
|
|
12
|
-
import {
|
|
12
|
+
import { Example, testView } from '@dxos/schema/testing';
|
|
13
13
|
|
|
14
14
|
import { translations } from '../../translations';
|
|
15
15
|
import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout, TestPanel } from '../testing';
|
|
@@ -71,7 +71,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
71
71
|
|
|
72
72
|
export const Default: Story = {
|
|
73
73
|
args: {
|
|
74
|
-
projection: new ProjectionModel(createEchoSchema(
|
|
74
|
+
projection: new ProjectionModel(createEchoSchema(Example).jsonSchema, testView.projection),
|
|
75
75
|
field: testView.projection.fields[0],
|
|
76
76
|
},
|
|
77
77
|
parameters: { controls: { disabled: true } },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type EchoSchema,
|
|
7
|
+
import { type EchoSchema, Format, FormatEnums, formatToType } from '@dxos/echo/internal';
|
|
8
8
|
import { type SchemaRegistry } from '@dxos/echo-db';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
|
|
@@ -140,7 +140,7 @@ export const FieldEditor = ({
|
|
|
140
140
|
['format' satisfies keyof PropertyType]: (props) => (
|
|
141
141
|
<SelectInput
|
|
142
142
|
{...props}
|
|
143
|
-
options={FormatEnums.filter((value) => value !==
|
|
143
|
+
options={FormatEnums.filter((value) => value !== Format.TypeFormat.None).map((value) => ({
|
|
144
144
|
value,
|
|
145
145
|
label: t(`format ${value}`),
|
|
146
146
|
}))}
|
|
@@ -159,7 +159,7 @@ export const FieldEditor = ({
|
|
|
159
159
|
{...props}
|
|
160
160
|
options={
|
|
161
161
|
referenceSchema
|
|
162
|
-
? getSchemaProperties(referenceSchema.ast)
|
|
162
|
+
? getSchemaProperties(referenceSchema.ast, {}, { form: true })
|
|
163
163
|
.sort(sortProperties)
|
|
164
164
|
.map((p) => ({ value: p.name }))
|
|
165
165
|
: []
|
|
@@ -46,7 +46,9 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
46
46
|
return {};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
return Object.fromEntries(
|
|
49
|
+
return Object.fromEntries(
|
|
50
|
+
getSchemaProperties(typeLiteral, {}, { form: true }).map((prop) => [prop.name, prop.defaultValue]),
|
|
51
|
+
);
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
const getDefaultValue = () =>
|
|
@@ -192,7 +192,7 @@ export const MarkdownInput = ({
|
|
|
192
192
|
disabled={!!readonly}
|
|
193
193
|
placeholder={placeholder}
|
|
194
194
|
value={getValue() ?? ''}
|
|
195
|
-
classNames={'min-bs-auto max-
|
|
195
|
+
classNames={'min-bs-auto max-bs-40 overflow-auto'}
|
|
196
196
|
onChange={(event) => onValueChange(type, event.target.value)}
|
|
197
197
|
onBlur={onBlur}
|
|
198
198
|
style={{ resize: 'none' }}
|
|
@@ -6,13 +6,11 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import React, { useCallback, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import { type
|
|
11
|
-
import { type BaseObject, Expando, Format, Ref, type TypeAnnotation, getObjectDXN } from '@dxos/echo/internal';
|
|
12
|
-
import { live } from '@dxos/echo/internal';
|
|
9
|
+
import { Format, Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
+
import { type AnyProperties, type TypeAnnotation } from '@dxos/echo/internal';
|
|
13
11
|
import { Tooltip } from '@dxos/react-ui';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
12
|
+
import { withLayoutVariants, withTheme } from '@dxos/react-ui/testing';
|
|
13
|
+
import { TestSchema } from '@dxos/schema/testing';
|
|
16
14
|
|
|
17
15
|
import { translations } from '../../translations';
|
|
18
16
|
import { TestLayout, TestPanel } from '../testing';
|
|
@@ -20,12 +18,12 @@ import { TestLayout, TestPanel } from '../testing';
|
|
|
20
18
|
import { SelectInput } from './Defaults';
|
|
21
19
|
import { Form, type FormProps } from './Form';
|
|
22
20
|
|
|
23
|
-
type StoryProps<T extends
|
|
21
|
+
type StoryProps<T extends AnyProperties> = {
|
|
24
22
|
debug?: boolean;
|
|
25
|
-
schema:
|
|
23
|
+
schema: Schema.Schema.AnyNoContext;
|
|
26
24
|
} & FormProps<T>;
|
|
27
25
|
|
|
28
|
-
const DefaultStory = <T extends
|
|
26
|
+
const DefaultStory = <T extends AnyProperties = any>({
|
|
29
27
|
debug,
|
|
30
28
|
schema,
|
|
31
29
|
values: initialValues,
|
|
@@ -51,13 +49,13 @@ const DefaultStory = <T extends BaseObject = any>({
|
|
|
51
49
|
return <Form<T> schema={schema} values={values} onSave={handleSave} {...props} />;
|
|
52
50
|
};
|
|
53
51
|
|
|
54
|
-
const RefStory = <T extends
|
|
52
|
+
const RefStory = <T extends AnyProperties = any>(props: StoryProps<T>) => {
|
|
55
53
|
const onQueryRefOptions = useCallback((typeInfo: TypeAnnotation) => {
|
|
56
54
|
switch (typeInfo.typename) {
|
|
57
|
-
case
|
|
55
|
+
case TestSchema.Person.typename:
|
|
58
56
|
return [
|
|
59
|
-
{ dxn:
|
|
60
|
-
{ dxn:
|
|
57
|
+
{ dxn: Obj.getDXN(contact1), label: 'Alice' },
|
|
58
|
+
{ dxn: Obj.getDXN(contact2), label: 'Bob' },
|
|
61
59
|
];
|
|
62
60
|
default:
|
|
63
61
|
return [];
|
|
@@ -88,7 +86,7 @@ const meta = {
|
|
|
88
86
|
title: 'ui/react-ui-form/Form',
|
|
89
87
|
component: Form as any,
|
|
90
88
|
render: DefaultStory,
|
|
91
|
-
decorators: [withTheme,
|
|
89
|
+
decorators: [withTheme, withLayoutVariants()],
|
|
92
90
|
parameters: {
|
|
93
91
|
layout: 'fullscreen',
|
|
94
92
|
translations,
|
|
@@ -122,7 +120,7 @@ export const Default: Story = {
|
|
|
122
120
|
export const Organization: Story = {
|
|
123
121
|
args: {
|
|
124
122
|
debug: true,
|
|
125
|
-
schema:
|
|
123
|
+
schema: TestSchema.OrganizationSchema,
|
|
126
124
|
values: {
|
|
127
125
|
name: 'DXOS',
|
|
128
126
|
website: 'https://dxos.org',
|
|
@@ -134,7 +132,7 @@ export const Organization: Story = {
|
|
|
134
132
|
export const OrganizationAutoSave: Story = {
|
|
135
133
|
args: {
|
|
136
134
|
debug: true,
|
|
137
|
-
schema:
|
|
135
|
+
schema: TestSchema.OrganizationSchema,
|
|
138
136
|
values: {
|
|
139
137
|
name: 'DXOS',
|
|
140
138
|
website: 'https://dxos.org',
|
|
@@ -147,10 +145,10 @@ export const OrganizationAutoSave: Story = {
|
|
|
147
145
|
// TODO(burdon): Type issue with employer reference.
|
|
148
146
|
// TODO(burdon): Test table/form with compound values (e.g., address).
|
|
149
147
|
// Property name is missing in type EncodedReference but required in type
|
|
150
|
-
export const
|
|
148
|
+
export const Person: Story = {
|
|
151
149
|
args: {
|
|
152
150
|
debug: true,
|
|
153
|
-
schema:
|
|
151
|
+
schema: TestSchema.Person,
|
|
154
152
|
values: {
|
|
155
153
|
name: 'Bot',
|
|
156
154
|
},
|
|
@@ -278,16 +276,18 @@ export const Enum: StoryObj<StoryProps<ColorType>> = {
|
|
|
278
276
|
//
|
|
279
277
|
|
|
280
278
|
const RefSchema = Schema.Struct({
|
|
281
|
-
contact: Ref(
|
|
282
|
-
optionalContact: Schema.optional(Ref(
|
|
283
|
-
refArray: Schema.optional(Schema.Array(Ref(
|
|
284
|
-
unknownExpando: Schema.optional(
|
|
279
|
+
contact: Type.Ref(TestSchema.Person).annotations({ title: 'Contact Reference' }),
|
|
280
|
+
optionalContact: Schema.optional(Type.Ref(TestSchema.Person).annotations({ title: 'Optional Contact Reference' })),
|
|
281
|
+
refArray: Schema.optional(Schema.Array(Type.Ref(TestSchema.Person))),
|
|
282
|
+
unknownExpando: Schema.optional(
|
|
283
|
+
Type.Ref(Type.Expando).annotations({ title: 'Optional Ref to an Expando (DXN Input)' }),
|
|
284
|
+
),
|
|
285
285
|
});
|
|
286
286
|
|
|
287
287
|
type RefSchema = Schema.Schema.Type<typeof RefSchema>;
|
|
288
288
|
|
|
289
|
-
const contact1 =
|
|
290
|
-
const contact2 =
|
|
289
|
+
const contact1 = Obj.make(TestSchema.Person, { name: 'Alice' });
|
|
290
|
+
const contact2 = Obj.make(TestSchema.Person, { name: 'Bob' });
|
|
291
291
|
|
|
292
292
|
export const Refs: StoryObj<StoryProps<RefSchema>> = {
|
|
293
293
|
render: RefStory,
|
|
@@ -6,7 +6,7 @@ import { useFocusFinders } from '@fluentui/react-tabster';
|
|
|
6
6
|
import type * as Schema from 'effect/Schema';
|
|
7
7
|
import React, { type ReactElement, useCallback, useEffect, useRef } from 'react';
|
|
8
8
|
|
|
9
|
-
import { type
|
|
9
|
+
import { type AnyProperties, type PropertyKey } from '@dxos/echo/internal';
|
|
10
10
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
11
11
|
import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
|
|
12
12
|
import { type ProjectionModel, type SchemaProperty } from '@dxos/schema';
|
|
@@ -18,7 +18,7 @@ import { FormFields, type FormFieldsProps } from './FormContent';
|
|
|
18
18
|
import { FormProvider } from './FormContext';
|
|
19
19
|
import { type InputComponent, type InputProps } from './Input';
|
|
20
20
|
|
|
21
|
-
export type PropsFilter<T extends
|
|
21
|
+
export type PropsFilter<T extends AnyProperties> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
22
22
|
|
|
23
23
|
export type ComponentLookup = (args: {
|
|
24
24
|
prop: string;
|
|
@@ -28,7 +28,8 @@ export type ComponentLookup = (args: {
|
|
|
28
28
|
|
|
29
29
|
export type CustomInputMap = Partial<Record<string, InputComponent>>;
|
|
30
30
|
|
|
31
|
-
export type FormProps<T extends
|
|
31
|
+
export type FormProps<T extends AnyProperties> = ThemedClassName<{
|
|
32
|
+
id?: string;
|
|
32
33
|
values: Partial<T>;
|
|
33
34
|
// TODO(burdon): Autofocus first input.
|
|
34
35
|
autoFocus?: boolean;
|
|
@@ -41,10 +42,12 @@ export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
|
41
42
|
onCancel?: () => void;
|
|
42
43
|
}> &
|
|
43
44
|
Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'> &
|
|
45
|
+
// TODO(wittjosiah): This needs to support different ref field options per field.
|
|
44
46
|
FormFieldsProps;
|
|
45
47
|
|
|
46
|
-
export const Form = <T extends
|
|
48
|
+
export const Form = <T extends AnyProperties>({
|
|
47
49
|
classNames,
|
|
50
|
+
id,
|
|
48
51
|
testId,
|
|
49
52
|
values: initialValues,
|
|
50
53
|
autoFocus,
|
|
@@ -92,7 +95,7 @@ export const Form = <T extends BaseObject>({
|
|
|
92
95
|
onValid={handleValid}
|
|
93
96
|
onSave={onSave}
|
|
94
97
|
>
|
|
95
|
-
<div role='none' className='contents' data-testid={testId}>
|
|
98
|
+
<div role='none' className='contents' {...(id && { 'data-object-id': id })} data-testid={testId}>
|
|
96
99
|
<FormFields
|
|
97
100
|
{...props}
|
|
98
101
|
ref={formRef}
|
|
@@ -37,7 +37,12 @@ export const FormActions = ({ readonly, onCancel, outerSpacing = true }: FormAct
|
|
|
37
37
|
)}
|
|
38
38
|
>
|
|
39
39
|
{onCancel && !readonly && (
|
|
40
|
-
<IconButton
|
|
40
|
+
<IconButton
|
|
41
|
+
data-testid='cancel-button'
|
|
42
|
+
icon='ph--x--regular'
|
|
43
|
+
label={t('cancel button label')}
|
|
44
|
+
onClick={onCancel}
|
|
45
|
+
/>
|
|
41
46
|
)}
|
|
42
47
|
{handleSave && (
|
|
43
48
|
<IconButton
|
|
@@ -45,7 +50,7 @@ export const FormActions = ({ readonly, onCancel, outerSpacing = true }: FormAct
|
|
|
45
50
|
data-testid='save-button'
|
|
46
51
|
disabled={!canSave}
|
|
47
52
|
icon='ph--check--regular'
|
|
48
|
-
label={t('button
|
|
53
|
+
label={t('save button label')}
|
|
49
54
|
onClick={handleSave}
|
|
50
55
|
/>
|
|
51
56
|
)}
|
|
@@ -6,7 +6,7 @@ import * as Function from 'effect/Function';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
8
8
|
import * as StringEffect from 'effect/String';
|
|
9
|
-
import React, { forwardRef, useMemo } from 'react';
|
|
9
|
+
import React, { Component, type PropsWithChildren, type ReactNode, forwardRef, useMemo } from 'react';
|
|
10
10
|
|
|
11
11
|
import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
|
|
12
12
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
@@ -264,7 +264,7 @@ export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
|
264
264
|
) => {
|
|
265
265
|
const values = useFormValues(path);
|
|
266
266
|
const properties = useMemo(() => {
|
|
267
|
-
const props = getSchemaProperties(schema.ast, values);
|
|
267
|
+
const props = getSchemaProperties(schema.ast, values, { form: true });
|
|
268
268
|
|
|
269
269
|
// Use projection-based field management when view and projection are available
|
|
270
270
|
if (projection) {
|
|
@@ -302,17 +302,18 @@ export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
|
302
302
|
{properties
|
|
303
303
|
.map((property) => {
|
|
304
304
|
return (
|
|
305
|
-
<
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
305
|
+
<ErrorBoundary key={property.name} path={[...(path ?? []), property.name]}>
|
|
306
|
+
<FormField
|
|
307
|
+
property={property}
|
|
308
|
+
path={[...(path ?? []), property.name]}
|
|
309
|
+
readonly={readonly}
|
|
310
|
+
projection={projection}
|
|
311
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
312
|
+
lookupComponent={lookupComponent}
|
|
313
|
+
Custom={Custom}
|
|
314
|
+
{...props}
|
|
315
|
+
/>
|
|
316
|
+
</ErrorBoundary>
|
|
316
317
|
);
|
|
317
318
|
})
|
|
318
319
|
.filter(isTruthy)}
|
|
@@ -320,3 +321,42 @@ export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
|
|
|
320
321
|
);
|
|
321
322
|
},
|
|
322
323
|
);
|
|
324
|
+
|
|
325
|
+
type ErrorState = {
|
|
326
|
+
error: Error | undefined;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
type ErrorBoundaryProps = PropsWithChildren<{
|
|
330
|
+
path?: (string | number)[];
|
|
331
|
+
}>;
|
|
332
|
+
|
|
333
|
+
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorState> {
|
|
334
|
+
static getDerivedStateFromError(error: Error): { error: Error } {
|
|
335
|
+
return { error };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
override state = { error: undefined };
|
|
339
|
+
|
|
340
|
+
override componentDidUpdate(prevProps: ErrorBoundaryProps): void {
|
|
341
|
+
if (prevProps.path !== this.props.path) {
|
|
342
|
+
this.resetError();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
override render(): ReactNode {
|
|
347
|
+
if (this.state.error) {
|
|
348
|
+
return (
|
|
349
|
+
<div className='flex gap-2 border border-roseFill font-mono text-sm'>
|
|
350
|
+
<span className='bg-roseFill text-surfaceText pli-1 font-thin'>ERROR</span>
|
|
351
|
+
{String(this.props.path?.join('.'))}
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return this.props.children;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private resetError(): void {
|
|
360
|
+
this.setState({ error: undefined });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -14,23 +14,23 @@ import React, {
|
|
|
14
14
|
} from 'react';
|
|
15
15
|
|
|
16
16
|
import { raise } from '@dxos/debug';
|
|
17
|
-
import { type
|
|
17
|
+
import { type AnyProperties, getValue } from '@dxos/echo/internal';
|
|
18
18
|
import { type SimpleType, createJsonPath } from '@dxos/effect';
|
|
19
19
|
|
|
20
20
|
import { type FormHandler, type FormOptions, useForm } from '../../hooks';
|
|
21
21
|
|
|
22
|
-
type FormContextValue<T extends
|
|
22
|
+
type FormContextValue<T extends AnyProperties> = FormHandler<T>;
|
|
23
23
|
|
|
24
24
|
const FormContext = createContext<FormContextValue<any> | undefined>(undefined);
|
|
25
25
|
|
|
26
|
-
export const useFormContext = <T extends
|
|
26
|
+
export const useFormContext = <T extends AnyProperties>(): FormContextValue<T> => {
|
|
27
27
|
return useContext(FormContext) ?? raise(new Error('Missing FormContext'));
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export const useFormValues = (path: (string | number)[] = []): any => {
|
|
31
31
|
const { values: formValues } = useFormContext();
|
|
32
32
|
const jsonPath = createJsonPath(path);
|
|
33
|
-
return getValue(formValues, jsonPath) as
|
|
33
|
+
return getValue(formValues, jsonPath) as AnyProperties;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export type FormInputStateProps = {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type FC } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
7
|
+
import { type Format } from '@dxos/echo/internal';
|
|
8
8
|
import { type SimpleType } from '@dxos/effect';
|
|
9
9
|
import { Icon, Input, Tooltip } from '@dxos/react-ui';
|
|
10
10
|
import { labelSpacing } from '@dxos/react-ui-stack';
|
|
@@ -17,7 +17,7 @@ import { type FormInputStateProps } from './FormContext';
|
|
|
17
17
|
*/
|
|
18
18
|
export type InputProps = {
|
|
19
19
|
type: SimpleType;
|
|
20
|
-
format?:
|
|
20
|
+
format?: Format.TypeFormat;
|
|
21
21
|
label: string;
|
|
22
22
|
placeholder?: string;
|
|
23
23
|
inputOnly?: boolean;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
6
|
+
|
|
5
7
|
import type * as Schema from 'effect/Schema';
|
|
6
8
|
import type * as SchemaAST from 'effect/SchemaAST';
|
|
7
9
|
import React, { useCallback, useMemo } from 'react';
|
|
@@ -70,7 +72,11 @@ export const RefField = ({
|
|
|
70
72
|
() => (ast ? findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId) : undefined),
|
|
71
73
|
[ast],
|
|
72
74
|
);
|
|
73
|
-
const {
|
|
75
|
+
const {
|
|
76
|
+
options: availableOptions,
|
|
77
|
+
update: updateOptions,
|
|
78
|
+
loading: _loading,
|
|
79
|
+
} = useQueryRefOptions({ refTypeInfo, onQueryRefOptions });
|
|
74
80
|
|
|
75
81
|
if ((refTypeInfo && refTypeInfo?.typename === getTypeAnnotation(Expando)?.typename) || !onQueryRefOptions) {
|
|
76
82
|
// If ref type is expando, fall back to taking a DXN in string format.
|
|
@@ -135,6 +141,14 @@ export const RefField = ({
|
|
|
135
141
|
[availableOptions, array, onValueChange],
|
|
136
142
|
);
|
|
137
143
|
|
|
144
|
+
const handleCreate = useCallback(
|
|
145
|
+
(values: any) => {
|
|
146
|
+
onCreate?.(values);
|
|
147
|
+
updateOptions();
|
|
148
|
+
},
|
|
149
|
+
[onCreate, updateOptions],
|
|
150
|
+
);
|
|
151
|
+
|
|
138
152
|
if (!refTypeInfo) {
|
|
139
153
|
return null;
|
|
140
154
|
}
|
|
@@ -211,7 +225,7 @@ export const RefField = ({
|
|
|
211
225
|
createOptionLabel={createOptionLabel}
|
|
212
226
|
createOptionIcon={createOptionIcon}
|
|
213
227
|
createInitialValuePath={createInitialValuePath}
|
|
214
|
-
onCreate={
|
|
228
|
+
onCreate={handleCreate}
|
|
215
229
|
/>
|
|
216
230
|
</ObjectPicker.Root>
|
|
217
231
|
)}
|
|
@@ -121,7 +121,7 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
|
|
|
121
121
|
readonly={!!readonly}
|
|
122
122
|
>
|
|
123
123
|
{({ items }) => (
|
|
124
|
-
<div role='list' className='
|
|
124
|
+
<div role='list' className='is-full overflow-auto'>
|
|
125
125
|
{items?.map((item) => (
|
|
126
126
|
<div key={item.id}>
|
|
127
127
|
<List.Item
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Format } from '@dxos/echo/internal';
|
|
6
6
|
import { type SimpleType } from '@dxos/effect';
|
|
7
7
|
|
|
8
8
|
import { GeoPointInput } from './custom';
|
|
@@ -12,11 +12,14 @@ import { type InputComponent } from './Input';
|
|
|
12
12
|
/**
|
|
13
13
|
* Get property input component.
|
|
14
14
|
*/
|
|
15
|
-
export const getInputComponent = (
|
|
15
|
+
export const getInputComponent = (
|
|
16
|
+
type: SimpleType,
|
|
17
|
+
format?: Format.TypeFormat | string,
|
|
18
|
+
): InputComponent | undefined => {
|
|
16
19
|
switch (format) {
|
|
17
|
-
case
|
|
20
|
+
case Format.TypeFormat.GeoPoint:
|
|
18
21
|
return GeoPointInput;
|
|
19
|
-
case
|
|
22
|
+
case Format.TypeFormat.Markdown:
|
|
20
23
|
return MarkdownInput;
|
|
21
24
|
}
|
|
22
25
|
|