@dxos/react-ui-form 0.8.4-main.72ec0f3 → 0.8.4-main.7ace549
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 +82 -101
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +82 -101
- 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 -4
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +5 -4
- 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/Form.d.ts +4 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +46 -31
- 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/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/custom/SelectOptionsInput.stories.d.ts +5 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- 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.stories.d.ts +5 -4
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +6 -5
- 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/translations.d.ts +5 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- 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 +31 -31
- 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/Form.stories.tsx +21 -21
- package/src/components/Form/Form.tsx +4 -4
- package/src/components/Form/FormActions.tsx +7 -2
- package/src/components/Form/FormContent.tsx +1 -1
- package/src/components/Form/FormContext.tsx +4 -4
- package/src/components/Form/Input.tsx +2 -2
- package/src/components/Form/factory.tsx +7 -4
- package/src/components/ViewEditor/ViewEditor.stories.tsx +3 -6
- package/src/components/ViewEditor/ViewEditor.tsx +13 -24
- package/src/hooks/useForm.ts +5 -5
- package/src/hooks/useRefQueryLookupHandler.ts +3 -3
- package/src/translations.ts +5 -4
- package/src/util/field.ts +25 -25
- package/src/util/formatting.ts +22 -22
- package/src/util/refs.ts +2 -2
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.7ace549",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -32,28 +32,28 @@
|
|
|
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/
|
|
38
|
-
"@dxos/echo
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/react-
|
|
48
|
-
"@dxos/react-
|
|
49
|
-
"@dxos/react-ui-
|
|
50
|
-
"@dxos/react-ui-
|
|
51
|
-
"@dxos/react-ui-
|
|
52
|
-
"@dxos/schema": "0.8.4-main.
|
|
53
|
-
"@dxos/types": "0.8.4-main.
|
|
54
|
-
"@dxos/util": "0.8.4-main.
|
|
55
|
-
"@dxos/react-ui-
|
|
56
|
-
"@dxos/
|
|
35
|
+
"@dxos/async": "0.8.4-main.7ace549",
|
|
36
|
+
"@dxos/debug": "0.8.4-main.7ace549",
|
|
37
|
+
"@dxos/effect": "0.8.4-main.7ace549",
|
|
38
|
+
"@dxos/echo": "0.8.4-main.7ace549",
|
|
39
|
+
"@dxos/invariant": "0.8.4-main.7ace549",
|
|
40
|
+
"@dxos/keys": "0.8.4-main.7ace549",
|
|
41
|
+
"@dxos/lit-ui": "0.8.4-main.7ace549",
|
|
42
|
+
"@dxos/echo-query": "0.8.4-main.7ace549",
|
|
43
|
+
"@dxos/live-object": "0.8.4-main.7ace549",
|
|
44
|
+
"@dxos/log": "0.8.4-main.7ace549",
|
|
45
|
+
"@dxos/react-ui-components": "0.8.4-main.7ace549",
|
|
46
|
+
"@dxos/react-ui-editor": "0.8.4-main.7ace549",
|
|
47
|
+
"@dxos/react-client": "0.8.4-main.7ace549",
|
|
48
|
+
"@dxos/react-ui-list": "0.8.4-main.7ace549",
|
|
49
|
+
"@dxos/react-ui-pickers": "0.8.4-main.7ace549",
|
|
50
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.7ace549",
|
|
51
|
+
"@dxos/react-ui-stack": "0.8.4-main.7ace549",
|
|
52
|
+
"@dxos/schema": "0.8.4-main.7ace549",
|
|
53
|
+
"@dxos/types": "0.8.4-main.7ace549",
|
|
54
|
+
"@dxos/util": "0.8.4-main.7ace549",
|
|
55
|
+
"@dxos/react-ui-types": "0.8.4-main.7ace549",
|
|
56
|
+
"@dxos/echo-db": "0.8.4-main.7ace549"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/jsonpath": "^0.2.4",
|
|
@@ -64,19 +64,19 @@
|
|
|
64
64
|
"react": "~19.2.0",
|
|
65
65
|
"react-dom": "~19.2.0",
|
|
66
66
|
"vite": "7.1.9",
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/react-ui
|
|
70
|
-
"@dxos/react-ui-
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
67
|
+
"@dxos/echo-signals": "0.8.4-main.7ace549",
|
|
68
|
+
"@dxos/random": "0.8.4-main.7ace549",
|
|
69
|
+
"@dxos/react-ui": "0.8.4-main.7ace549",
|
|
70
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.7ace549",
|
|
71
|
+
"@dxos/react-ui-theme": "0.8.4-main.7ace549",
|
|
72
|
+
"@dxos/storybook-utils": "0.8.4-main.7ace549"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"effect": "^3.13.3",
|
|
76
76
|
"react": "^19.0.0",
|
|
77
77
|
"react-dom": "^19.0.0",
|
|
78
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
79
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
78
|
+
"@dxos/react-ui": "0.8.4-main.7ace549",
|
|
79
|
+
"@dxos/react-ui-theme": "0.8.4-main.7ace549"
|
|
80
80
|
},
|
|
81
81
|
"publishConfig": {
|
|
82
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 = () =>
|
|
@@ -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
12
|
import { withLayoutVariants, withTheme } from '@dxos/react-ui/testing';
|
|
15
|
-
import {
|
|
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 [];
|
|
@@ -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',
|
|
@@ -150,7 +148,7 @@ export const OrganizationAutoSave: Story = {
|
|
|
150
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,7 @@ 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
32
|
id?: string;
|
|
33
33
|
values: Partial<T>;
|
|
34
34
|
// TODO(burdon): Autofocus first input.
|
|
@@ -45,7 +45,7 @@ export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
|
45
45
|
// TODO(wittjosiah): This needs to support different ref field options per field.
|
|
46
46
|
FormFieldsProps;
|
|
47
47
|
|
|
48
|
-
export const Form = <T extends
|
|
48
|
+
export const Form = <T extends AnyProperties>({
|
|
49
49
|
classNames,
|
|
50
50
|
id,
|
|
51
51
|
testId,
|
|
@@ -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('save button')}
|
|
53
|
+
label={t('save button label')}
|
|
49
54
|
onClick={handleSave}
|
|
50
55
|
/>
|
|
51
56
|
)}
|
|
@@ -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) {
|
|
@@ -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,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
|
|
|
@@ -74,7 +74,6 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
74
74
|
name: 'Test',
|
|
75
75
|
query: Query.select(Filter.type(TestSchema)),
|
|
76
76
|
jsonSchema: Type.toJsonSchema(TestSchema),
|
|
77
|
-
presentation: Obj.make(Type.Expando, {}),
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
setSchema(testSchema);
|
|
@@ -88,7 +87,7 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
88
87
|
return;
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
if (props.mode === '
|
|
90
|
+
if (props.mode === 'tag') {
|
|
92
91
|
const queue = target && DXN.tryParse(target) ? target : undefined;
|
|
93
92
|
const query = queue ? Query.fromAst(newQuery).options({ queues: [queue] }) : Query.fromAst(newQuery);
|
|
94
93
|
view.query.ast = query.ast;
|
|
@@ -102,12 +101,10 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
102
101
|
const newView = View.make({
|
|
103
102
|
query,
|
|
104
103
|
jsonSchema: newSchema.jsonSchema,
|
|
105
|
-
presentation: Obj.make(Type.Expando, {}),
|
|
106
104
|
});
|
|
107
105
|
view.projection = Obj.getSnapshot(newView).projection;
|
|
108
106
|
setSchema(() => newSchema);
|
|
109
107
|
} else {
|
|
110
|
-
console.log('updateViewQuery', { newQuery });
|
|
111
108
|
view.query.ast = newQuery;
|
|
112
109
|
schema.updateTypename(getTypenameFromQuery(newQuery));
|
|
113
110
|
}
|
|
@@ -194,8 +191,8 @@ export const Readonly: Story = {
|
|
|
194
191
|
},
|
|
195
192
|
};
|
|
196
193
|
|
|
197
|
-
export const
|
|
194
|
+
export const TagQueryMode: Story = {
|
|
198
195
|
args: {
|
|
199
|
-
mode: '
|
|
196
|
+
mode: 'tag',
|
|
200
197
|
},
|
|
201
198
|
};
|
|
@@ -37,7 +37,7 @@ export type ViewEditorProps = ThemedClassName<
|
|
|
37
37
|
{
|
|
38
38
|
schema: Schema.Schema.AnyNoContext;
|
|
39
39
|
view: View.View;
|
|
40
|
-
mode?: 'schema' | '
|
|
40
|
+
mode?: 'schema' | 'tag';
|
|
41
41
|
registry?: SchemaRegistry;
|
|
42
42
|
readonly?: boolean;
|
|
43
43
|
showHeading?: boolean;
|
|
@@ -96,13 +96,9 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
96
96
|
: QueryAST.Query.annotations({ title: 'Query' }),
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
if (mode === '
|
|
100
|
-
const name = Schema.Struct({
|
|
101
|
-
name: Schema.optional(Schema.String.annotations({ title: 'Name' })),
|
|
102
|
-
});
|
|
103
|
-
|
|
99
|
+
if (mode === 'tag') {
|
|
104
100
|
return Schema.Struct({
|
|
105
|
-
...
|
|
101
|
+
...base.fields,
|
|
106
102
|
target: Schema.optional(Schema.String.annotations({ title: 'Target Queue' })),
|
|
107
103
|
}).pipe(Schema.mutable);
|
|
108
104
|
}
|
|
@@ -114,11 +110,10 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
114
110
|
// TODO(burdon): Settings should have domain name owned by user.
|
|
115
111
|
const viewValues = useMemo(
|
|
116
112
|
() => ({
|
|
117
|
-
name: view.name,
|
|
118
113
|
query: mode === 'schema' ? getTypenameFromQuery(view.query.ast) : view.query.ast,
|
|
119
114
|
target: queueTarget,
|
|
120
115
|
}),
|
|
121
|
-
[
|
|
116
|
+
[mode, view.query.ast, queueTarget],
|
|
122
117
|
);
|
|
123
118
|
|
|
124
119
|
const handleToggleField = useCallback(
|
|
@@ -138,18 +133,12 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
138
133
|
|
|
139
134
|
const handleUpdate = useCallback(
|
|
140
135
|
(values: any) => {
|
|
141
|
-
invariant(!readonly);
|
|
142
136
|
requestAnimationFrame(() => {
|
|
143
|
-
if ('name' in values && view.name !== values.name) {
|
|
144
|
-
view.name = values.name;
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
137
|
const query = mode === 'schema' ? Query.select(Filter.typename(values.query)).ast : values.query;
|
|
149
138
|
onQueryChanged?.(query, values.target);
|
|
150
139
|
});
|
|
151
140
|
},
|
|
152
|
-
[onQueryChanged,
|
|
141
|
+
[onQueryChanged, view, queueTarget, mode],
|
|
153
142
|
);
|
|
154
143
|
|
|
155
144
|
const handleDelete = useCallback(
|
|
@@ -194,11 +183,12 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
194
183
|
[projectionModel],
|
|
195
184
|
);
|
|
196
185
|
|
|
197
|
-
const custom = useMemo(() => (mode === '
|
|
186
|
+
const custom = useMemo(() => (mode === 'tag' ? customFields({ types, tags }) : undefined), [types, tags, mode]);
|
|
198
187
|
|
|
199
188
|
return (
|
|
200
189
|
<div role='none' className={mx(classNames)}>
|
|
201
|
-
{
|
|
190
|
+
{/* If readonlyProp is set, then the callout is not needed. */}
|
|
191
|
+
{schemaReadonly && !readonly && (
|
|
202
192
|
<Callout.Root valence='info' classNames={['mlb-cardSpacingBlock', outerSpacing && 'mli-cardSpacingInline']}>
|
|
203
193
|
<Callout.Title>{t('system schema description')}</Callout.Title>
|
|
204
194
|
</Callout.Root>
|
|
@@ -210,7 +200,6 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
210
200
|
autoSave
|
|
211
201
|
schema={viewSchema}
|
|
212
202
|
values={viewValues}
|
|
213
|
-
readonly={readonly ? 'disabled-input' : false}
|
|
214
203
|
onSave={handleUpdate}
|
|
215
204
|
outerSpacing={outerSpacing}
|
|
216
205
|
Custom={custom}
|
|
@@ -243,7 +232,7 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
243
232
|
role='none'
|
|
244
233
|
className={mx(subtleHover, listItemGrid, 'rounded-sm cursor-pointer min-bs-10')}
|
|
245
234
|
>
|
|
246
|
-
<List.ItemDragHandle disabled={readonly} />
|
|
235
|
+
<List.ItemDragHandle disabled={readonly || schemaReadonly} />
|
|
247
236
|
<List.ItemTitle
|
|
248
237
|
classNames={hidden && 'text-subdued'}
|
|
249
238
|
onClick={() => handleToggleField(field)}
|
|
@@ -258,7 +247,7 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
258
247
|
disabled={readonly || (!hidden && projectionModel.fields.length <= 1)}
|
|
259
248
|
onClick={() => (hidden ? handleShow(field.path) : handleHide(field.id))}
|
|
260
249
|
/>
|
|
261
|
-
{
|
|
250
|
+
{!readonly && (
|
|
262
251
|
<>
|
|
263
252
|
<List.ItemDeleteButton
|
|
264
253
|
label={t('delete field label')}
|
|
@@ -279,7 +268,7 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
279
268
|
</>
|
|
280
269
|
)}
|
|
281
270
|
</div>
|
|
282
|
-
{expandedField === field.id &&
|
|
271
|
+
{expandedField === field.id && !readonly && (
|
|
283
272
|
<div role='none' className='col-span-5 mbs-1 mbe-1 border border-separator rounded-md'>
|
|
284
273
|
<FieldEditor
|
|
285
274
|
readonly={readonly || schemaReadonly ? 'disabled-input' : false}
|
|
@@ -299,11 +288,11 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
299
288
|
</List.Root>
|
|
300
289
|
</div>
|
|
301
290
|
|
|
302
|
-
{!readonly && !expandedField &&
|
|
291
|
+
{!readonly && !expandedField && (
|
|
303
292
|
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
304
293
|
<IconButton
|
|
305
294
|
icon='ph--plus--regular'
|
|
306
|
-
label={t('add property button')}
|
|
295
|
+
label={t('add property button label')}
|
|
307
296
|
onClick={readonly ? undefined : handleAdd}
|
|
308
297
|
// TODO(burdon): Show field limit in ux (not tooltip).
|
|
309
298
|
disabled={view.projection.fields.length >= VIEW_FIELD_LIMIT}
|
package/src/hooks/useForm.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import { type
|
|
9
|
+
import { type AnyProperties, getValue, setValue } from '@dxos/echo/internal';
|
|
10
10
|
import { type JsonPath, type SimpleType, createJsonPath, fromEffectValidationPath } from '@dxos/effect';
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
@@ -16,7 +16,7 @@ import { type MaybePromise } from '@dxos/util';
|
|
|
16
16
|
/**
|
|
17
17
|
* Return type from `useForm` hook.
|
|
18
18
|
*/
|
|
19
|
-
export type FormHandler<T extends
|
|
19
|
+
export type FormHandler<T extends AnyProperties> = {
|
|
20
20
|
//
|
|
21
21
|
// Form state management.
|
|
22
22
|
//
|
|
@@ -44,7 +44,7 @@ export type FormHandler<T extends BaseObject> = {
|
|
|
44
44
|
/**
|
|
45
45
|
* Hook options.
|
|
46
46
|
*/
|
|
47
|
-
export interface FormOptions<T extends
|
|
47
|
+
export interface FormOptions<T extends AnyProperties> {
|
|
48
48
|
/**
|
|
49
49
|
* Effect schema (Type literal).
|
|
50
50
|
*/
|
|
@@ -85,7 +85,7 @@ export interface FormOptions<T extends BaseObject> {
|
|
|
85
85
|
* Creates a hook for managing form state, including values, validation, and submission.
|
|
86
86
|
* Deeply integrated with `@dxos/schema` for schema-based validation.
|
|
87
87
|
*/
|
|
88
|
-
export const useForm = <T extends
|
|
88
|
+
export const useForm = <T extends AnyProperties>({
|
|
89
89
|
schema,
|
|
90
90
|
initialValues,
|
|
91
91
|
onValuesChanged,
|
|
@@ -276,7 +276,7 @@ export const useForm = <T extends BaseObject>({
|
|
|
276
276
|
);
|
|
277
277
|
};
|
|
278
278
|
|
|
279
|
-
const createKeySet = <T extends
|
|
279
|
+
const createKeySet = <T extends AnyProperties, V>(obj: T, value: V): Record<JsonPath, V> => {
|
|
280
280
|
invariant(obj);
|
|
281
281
|
return Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: value }), {} as Record<JsonPath, V>);
|
|
282
282
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Obj } from '@dxos/echo';
|
|
8
|
-
import { type TypeAnnotation
|
|
8
|
+
import { type TypeAnnotation } from '@dxos/echo/internal';
|
|
9
9
|
import { Filter, type Space } from '@dxos/react-client/echo';
|
|
10
10
|
import { isNonNullable } from '@dxos/util';
|
|
11
11
|
|
|
@@ -19,12 +19,12 @@ export const useRefQueryLookupHandler = ({ space }: UseRefQueryLookupProps): Que
|
|
|
19
19
|
if (!space) {
|
|
20
20
|
return [];
|
|
21
21
|
}
|
|
22
|
+
|
|
22
23
|
const query = space.db.query(Filter.typename(typeInfo.typename));
|
|
23
24
|
const { objects } = await query.run();
|
|
24
|
-
|
|
25
25
|
return objects
|
|
26
26
|
.map((object) => {
|
|
27
|
-
const dxn =
|
|
27
|
+
const dxn = Obj.getDXN(object);
|
|
28
28
|
if (!dxn) {
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
package/src/translations.ts
CHANGED
|
@@ -15,7 +15,7 @@ export const translations = [
|
|
|
15
15
|
'empty readonly ref field label': '(none)',
|
|
16
16
|
|
|
17
17
|
// TODO(burdon): Standardize field/property.
|
|
18
|
-
'add property button': 'Add property',
|
|
18
|
+
'add property button label': 'Add property',
|
|
19
19
|
'field property label': 'Property',
|
|
20
20
|
'field property placeholder': 'Property name',
|
|
21
21
|
'field format label': 'Type',
|
|
@@ -33,7 +33,8 @@ export const translations = [
|
|
|
33
33
|
'ref field placeholder_other': 'Select items…',
|
|
34
34
|
|
|
35
35
|
// TODO(burdon): Factor out?
|
|
36
|
-
'
|
|
36
|
+
'cancel button label': 'Cancel',
|
|
37
|
+
'save button label': 'Save',
|
|
37
38
|
|
|
38
39
|
// FormatEnum
|
|
39
40
|
'format boolean': 'Boolean',
|
|
@@ -77,9 +78,9 @@ export const translations = [
|
|
|
77
78
|
'select option add': 'Add option',
|
|
78
79
|
|
|
79
80
|
// System schema message.
|
|
80
|
-
'system schema title': 'System
|
|
81
|
+
'system schema title': 'System type',
|
|
81
82
|
'system schema description':
|
|
82
|
-
'This is a system
|
|
83
|
+
'This is a system type and cannot be modified, though the view can still be configured.',
|
|
83
84
|
},
|
|
84
85
|
},
|
|
85
86
|
},
|