@dxos/react-ui-form 0.8.4-main.5ea62a8 → 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 +534 -348
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +534 -348
- 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 +8 -7
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +8 -7
- 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 +6 -5
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +52 -37
- 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 +5 -5
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +4 -4
- 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 +10 -6
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +8 -7
- 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.d.ts +41 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +82 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +20 -6
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +13 -11
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +5 -5
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +9 -3
- 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 +7 -6
- 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/src/util/refs.d.ts +1 -1
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -37
- package/src/components/ControlSection/ControlSection.stories.tsx +4 -5
- package/src/components/ControlSection/ControlSection.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.stories.tsx +8 -7
- package/src/components/FieldEditor/FieldEditor.tsx +7 -4
- package/src/components/Form/ArrayField.tsx +7 -4
- package/src/components/Form/Defaults.tsx +1 -1
- package/src/components/Form/Form.stories.tsx +52 -52
- package/src/components/Form/Form.tsx +19 -9
- package/src/components/Form/FormActions.tsx +7 -2
- package/src/components/Form/FormContent.tsx +83 -23
- package/src/components/Form/FormContext.tsx +6 -6
- package/src/components/Form/Input.tsx +2 -2
- package/src/components/Form/RefField.tsx +50 -66
- package/src/components/Form/custom/GeoPointInput.tsx +1 -1
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +5 -6
- package/src/components/Form/custom/SelectOptionsInput.tsx +2 -2
- package/src/components/Form/factory.tsx +7 -4
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +161 -0
- package/src/components/ObjectPicker/index.ts +5 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +112 -49
- package/src/components/ViewEditor/ViewEditor.test.tsx +6 -5
- package/src/components/ViewEditor/ViewEditor.tsx +277 -208
- package/src/components/index.ts +1 -0
- package/src/components/testing.tsx +2 -2
- package/src/hooks/useForm.ts +11 -8
- package/src/hooks/useQueryRefOptions.ts +15 -6
- package/src/hooks/useRefQueryLookupHandler.ts +5 -5
- package/src/translations.ts +7 -6
- package/src/util/field.ts +25 -25
- package/src/util/formatting.ts +22 -22
- package/src/util/refs.ts +7 -7
- package/src/util/schema.ts +2 -4
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",
|
|
@@ -28,52 +28,55 @@
|
|
|
28
28
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
|
29
29
|
"@fluentui/react-tabster": "^9.24.2",
|
|
30
30
|
"@preact-signals/safe-react": "^0.9.0",
|
|
31
|
+
"@preact/signals-react": "^3.2.0",
|
|
31
32
|
"@radix-ui/react-context": "1.1.1",
|
|
32
33
|
"date-fns": "^3.3.1",
|
|
33
34
|
"jsonpath": "^1.1.1",
|
|
34
|
-
"@dxos/async": "0.8.4-main.
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
38
|
-
"@dxos/echo-
|
|
39
|
-
"@dxos/effect": "0.8.4-main.
|
|
40
|
-
"@dxos/invariant": "0.8.4-main.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/react-client": "0.8.4-main.
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/react-ui-
|
|
48
|
-
"@dxos/react-ui-
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/react-ui-
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/react-ui-
|
|
53
|
-
"@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"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|
|
56
59
|
"@types/jsonpath": "^0.2.4",
|
|
57
|
-
"@types/react": "~
|
|
58
|
-
"@types/react-dom": "~
|
|
59
|
-
"effect": "3.
|
|
60
|
+
"@types/react": "~19.2.2",
|
|
61
|
+
"@types/react-dom": "~19.2.2",
|
|
62
|
+
"effect": "3.18.3",
|
|
60
63
|
"fast-check": "^3.19.0",
|
|
61
|
-
"react": "~
|
|
62
|
-
"react-dom": "~
|
|
63
|
-
"vite": "7.1.
|
|
64
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
65
|
-
"@dxos/random": "0.8.4-main.
|
|
66
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
69
|
-
"@dxos/
|
|
64
|
+
"react": "~19.2.0",
|
|
65
|
+
"react-dom": "~19.2.0",
|
|
66
|
+
"vite": "7.1.9",
|
|
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"
|
|
70
73
|
},
|
|
71
74
|
"peerDependencies": {
|
|
72
75
|
"effect": "^3.13.3",
|
|
73
|
-
"react": "
|
|
74
|
-
"react-dom": "
|
|
75
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
76
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
76
|
+
"react": "^19.0.0",
|
|
77
|
+
"react-dom": "^19.0.0",
|
|
78
|
+
"@dxos/react-ui": "0.8.4-main.66e292d",
|
|
79
|
+
"@dxos/react-ui-theme": "0.8.4-main.66e292d"
|
|
77
80
|
},
|
|
78
81
|
"publishConfig": {
|
|
79
82
|
"access": "public"
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
10
|
-
import {
|
|
8
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
9
|
|
|
12
10
|
import { translations } from '../../translations';
|
|
13
11
|
|
|
@@ -17,12 +15,13 @@ const meta = {
|
|
|
17
15
|
title: 'ui/react-ui-form/ControlSection',
|
|
18
16
|
component: ControlSection,
|
|
19
17
|
render: (args) => (
|
|
20
|
-
<ControlPage classNames='
|
|
18
|
+
<ControlPage classNames='is-[40rem]'>
|
|
21
19
|
<ControlSection {...args} />
|
|
22
20
|
</ControlPage>
|
|
23
21
|
),
|
|
24
|
-
decorators: [
|
|
22
|
+
decorators: [withTheme],
|
|
25
23
|
parameters: {
|
|
24
|
+
layout: 'centered',
|
|
26
25
|
translations,
|
|
27
26
|
},
|
|
28
27
|
} satisfies Meta<typeof ControlSection>;
|
|
@@ -22,7 +22,7 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
|
22
22
|
|
|
23
23
|
export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
|
|
24
24
|
return (
|
|
25
|
-
<StackItem.Content classNames='
|
|
25
|
+
<StackItem.Content scrollable classNames='[--control-spacing:var(--dx-trimMd)]'>
|
|
26
26
|
<div role='none' className={mx('pli-cardSpacingInline pbe-trimLg', classNames)} {...props}>
|
|
27
27
|
{children}
|
|
28
28
|
</div>
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React, { useEffect, useMemo } from 'react';
|
|
9
7
|
|
|
10
|
-
import { createEchoSchema } from '@dxos/
|
|
8
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
9
|
import { log } from '@dxos/log';
|
|
10
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
11
|
import { ProjectionModel } from '@dxos/schema';
|
|
13
|
-
import {
|
|
14
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
import { Example, testView } from '@dxos/schema/testing';
|
|
15
13
|
|
|
16
14
|
import { translations } from '../../translations';
|
|
17
15
|
import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout, TestPanel } from '../testing';
|
|
@@ -31,6 +29,8 @@ const DefaultStory = (props: FieldEditorProps) => {
|
|
|
31
29
|
|
|
32
30
|
// Expose objects on window for test access.
|
|
33
31
|
useEffect(() => {
|
|
32
|
+
props.projection.normalizeView();
|
|
33
|
+
|
|
34
34
|
if (typeof window !== 'undefined') {
|
|
35
35
|
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
|
|
36
36
|
props,
|
|
@@ -58,8 +58,9 @@ const meta = {
|
|
|
58
58
|
title: 'ui/react-ui-form/FieldEditor',
|
|
59
59
|
component: FieldEditor as any,
|
|
60
60
|
render: DefaultStory,
|
|
61
|
-
decorators: [
|
|
61
|
+
decorators: [withTheme],
|
|
62
62
|
parameters: {
|
|
63
|
+
layout: 'fullscreen',
|
|
63
64
|
translations,
|
|
64
65
|
},
|
|
65
66
|
} satisfies Meta<typeof DefaultStory>;
|
|
@@ -70,7 +71,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
70
71
|
|
|
71
72
|
export const Default: Story = {
|
|
72
73
|
args: {
|
|
73
|
-
projection: new ProjectionModel(createEchoSchema(
|
|
74
|
+
projection: new ProjectionModel(createEchoSchema(Example).jsonSchema, testView.projection),
|
|
74
75
|
field: testView.projection.fields[0],
|
|
75
76
|
},
|
|
76
77
|
parameters: { controls: { disabled: true } },
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
+
import { type EchoSchema, Format, FormatEnums, formatToType } from '@dxos/echo/internal';
|
|
7
8
|
import { type SchemaRegistry } from '@dxos/echo-db';
|
|
8
|
-
import { type EchoSchema, FormatEnum, FormatEnums, formatToType } from '@dxos/echo-schema';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
|
|
11
11
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type ProjectionModel,
|
|
14
14
|
type PropertyType,
|
|
15
15
|
type SchemaProperty,
|
|
16
|
+
formatToAdditionalPropertyAttributes,
|
|
16
17
|
getFormatSchema,
|
|
17
18
|
getSchemaProperties,
|
|
18
19
|
sortProperties,
|
|
@@ -94,8 +95,10 @@ export const FieldEditor = ({
|
|
|
94
95
|
|
|
95
96
|
setProps((props) => {
|
|
96
97
|
const type = formatToType[_props.format as keyof typeof formatToType];
|
|
98
|
+
const additionalProps =
|
|
99
|
+
formatToAdditionalPropertyAttributes[_props.format as keyof typeof formatToAdditionalPropertyAttributes];
|
|
97
100
|
if (props.type !== type) {
|
|
98
|
-
return { ...props, ..._props, type };
|
|
101
|
+
return { ...props, ..._props, ...additionalProps, type } as PropertyType;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
return _props as PropertyType;
|
|
@@ -137,7 +140,7 @@ export const FieldEditor = ({
|
|
|
137
140
|
['format' satisfies keyof PropertyType]: (props) => (
|
|
138
141
|
<SelectInput
|
|
139
142
|
{...props}
|
|
140
|
-
options={FormatEnums.filter((value) => value !==
|
|
143
|
+
options={FormatEnums.filter((value) => value !== Format.TypeFormat.None).map((value) => ({
|
|
141
144
|
value,
|
|
142
145
|
label: t(`format ${value}`),
|
|
143
146
|
}))}
|
|
@@ -156,7 +159,7 @@ export const FieldEditor = ({
|
|
|
156
159
|
{...props}
|
|
157
160
|
options={
|
|
158
161
|
referenceSchema
|
|
159
|
-
? getSchemaProperties(referenceSchema.ast)
|
|
162
|
+
? getSchemaProperties(referenceSchema.ast, {}, { form: true })
|
|
160
163
|
.sort(sortProperties)
|
|
161
164
|
.map((p) => ({ value: p.name }))
|
|
162
165
|
: []
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Function from 'effect/Function';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
|
+
import * as String from 'effect/String';
|
|
7
8
|
import React, { Fragment, useCallback } from 'react';
|
|
8
9
|
|
|
9
10
|
import { SimpleType, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
|
|
@@ -34,7 +35,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
34
35
|
// It doesn't become live until another field is touched, but that's better than the whole form crashing.
|
|
35
36
|
const values = (useFormValues(path ?? []) ?? []) as any[];
|
|
36
37
|
invariant(Array.isArray(values), `Values at path ${path?.join('.')} must be an array.`);
|
|
37
|
-
const label = title ?? pipe(name, capitalize);
|
|
38
|
+
const label = title ?? Function.pipe(name, String.capitalize);
|
|
38
39
|
|
|
39
40
|
const elementType = findArrayElementType(ast);
|
|
40
41
|
|
|
@@ -45,7 +46,9 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
45
46
|
return {};
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
return Object.fromEntries(
|
|
49
|
+
return Object.fromEntries(
|
|
50
|
+
getSchemaProperties(typeLiteral, {}, { form: true }).map((prop) => [prop.name, prop.defaultValue]),
|
|
51
|
+
);
|
|
49
52
|
};
|
|
50
53
|
|
|
51
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' }}
|
|
@@ -2,19 +2,15 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
9
7
|
import React, { useCallback, useState } from 'react';
|
|
10
8
|
|
|
11
|
-
import {
|
|
12
|
-
import { type
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { Testing } from '@dxos/schema/testing';
|
|
17
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
9
|
+
import { Format, Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
+
import { type AnyProperties, type TypeAnnotation } from '@dxos/echo/internal';
|
|
11
|
+
import { Tooltip } from '@dxos/react-ui';
|
|
12
|
+
import { withLayoutVariants, withTheme } from '@dxos/react-ui/testing';
|
|
13
|
+
import { TestSchema } from '@dxos/schema/testing';
|
|
18
14
|
|
|
19
15
|
import { translations } from '../../translations';
|
|
20
16
|
import { TestLayout, TestPanel } from '../testing';
|
|
@@ -22,12 +18,12 @@ import { TestLayout, TestPanel } from '../testing';
|
|
|
22
18
|
import { SelectInput } from './Defaults';
|
|
23
19
|
import { Form, type FormProps } from './Form';
|
|
24
20
|
|
|
25
|
-
type StoryProps<T extends
|
|
21
|
+
type StoryProps<T extends AnyProperties> = {
|
|
26
22
|
debug?: boolean;
|
|
27
|
-
schema:
|
|
23
|
+
schema: Schema.Schema.AnyNoContext;
|
|
28
24
|
} & FormProps<T>;
|
|
29
25
|
|
|
30
|
-
const DefaultStory = <T extends
|
|
26
|
+
const DefaultStory = <T extends AnyProperties = any>({
|
|
31
27
|
debug,
|
|
32
28
|
schema,
|
|
33
29
|
values: initialValues,
|
|
@@ -40,24 +36,26 @@ const DefaultStory = <T extends BaseObject = any>({
|
|
|
40
36
|
|
|
41
37
|
if (debug) {
|
|
42
38
|
return (
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
<Tooltip.Provider>
|
|
40
|
+
<TestLayout json={{ values, schema: schema.ast }}>
|
|
41
|
+
<TestPanel>
|
|
42
|
+
<Form<T> schema={schema} values={values} onSave={handleSave} {...props} />
|
|
43
|
+
</TestPanel>
|
|
44
|
+
</TestLayout>
|
|
45
|
+
</Tooltip.Provider>
|
|
48
46
|
);
|
|
49
47
|
}
|
|
50
48
|
|
|
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 [];
|
|
@@ -67,24 +65,6 @@ const RefStory = <T extends BaseObject = any>(props: StoryProps<T>) => {
|
|
|
67
65
|
return <DefaultStory<T> onQueryRefOptions={onQueryRefOptions} {...props} />;
|
|
68
66
|
};
|
|
69
67
|
|
|
70
|
-
const meta = {
|
|
71
|
-
title: 'ui/react-ui-form/Form',
|
|
72
|
-
component: Form as any,
|
|
73
|
-
render: DefaultStory,
|
|
74
|
-
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
75
|
-
parameters: {
|
|
76
|
-
translations,
|
|
77
|
-
},
|
|
78
|
-
argTypes: {
|
|
79
|
-
readonly: {
|
|
80
|
-
control: 'boolean',
|
|
81
|
-
description: 'Readonly',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
} satisfies Meta<StoryProps<any>>;
|
|
85
|
-
|
|
86
|
-
export default meta;
|
|
87
|
-
|
|
88
68
|
const AddressSchema = Schema.Struct({
|
|
89
69
|
street: Schema.optional(Schema.String.annotations({ title: 'Street' })),
|
|
90
70
|
city: Schema.optional(Schema.String.annotations({ title: 'City' })),
|
|
@@ -102,10 +82,28 @@ const ContactSchema = Schema.Struct({
|
|
|
102
82
|
|
|
103
83
|
type ContactSchema = Schema.Schema.Type<typeof ContactSchema>;
|
|
104
84
|
|
|
105
|
-
|
|
85
|
+
const meta = {
|
|
86
|
+
title: 'ui/react-ui-form/Form',
|
|
87
|
+
component: Form as any,
|
|
88
|
+
render: DefaultStory,
|
|
89
|
+
decorators: [withTheme, withLayoutVariants()],
|
|
90
|
+
parameters: {
|
|
91
|
+
layout: 'fullscreen',
|
|
92
|
+
translations,
|
|
93
|
+
},
|
|
94
|
+
argTypes: {
|
|
95
|
+
readonly: {
|
|
96
|
+
control: 'boolean',
|
|
97
|
+
description: 'Readonly',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
} satisfies Meta<StoryProps<any>>;
|
|
101
|
+
|
|
102
|
+
export default meta;
|
|
103
|
+
|
|
104
|
+
type Story = StoryObj<typeof meta>;
|
|
106
105
|
|
|
107
106
|
export const Default: Story = {
|
|
108
|
-
decorators: [withSurfaceVariantsLayout(), withTheme],
|
|
109
107
|
args: {
|
|
110
108
|
schema: ContactSchema,
|
|
111
109
|
values: {
|
|
@@ -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,
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { useFocusFinders } from '@fluentui/react-tabster';
|
|
6
|
-
import
|
|
7
|
-
import React, { type ReactElement,
|
|
6
|
+
import type * as Schema from 'effect/Schema';
|
|
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';
|
|
13
13
|
|
|
14
|
-
import { type FormOptions } from '../../hooks';
|
|
14
|
+
import { type FormHandler, type FormOptions } from '../../hooks';
|
|
15
15
|
|
|
16
16
|
import { FormActions, type FormOuterSpacing } from './FormActions';
|
|
17
17
|
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,
|
|
@@ -72,7 +75,14 @@ export const Form = <T extends BaseObject>({
|
|
|
72
75
|
}, [autoFocus]);
|
|
73
76
|
|
|
74
77
|
// TODO(burdon): Name?
|
|
75
|
-
const handleValid =
|
|
78
|
+
const handleValid = useCallback(
|
|
79
|
+
async (values: any, meta: { changed: FormHandler<T>['changed'] }) => {
|
|
80
|
+
if (autoSave) {
|
|
81
|
+
await onSave?.(values, meta);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
[autoSave, onSave],
|
|
85
|
+
);
|
|
76
86
|
|
|
77
87
|
return (
|
|
78
88
|
<FormProvider
|
|
@@ -85,7 +95,7 @@ export const Form = <T extends BaseObject>({
|
|
|
85
95
|
onValid={handleValid}
|
|
86
96
|
onSave={onSave}
|
|
87
97
|
>
|
|
88
|
-
<div role='none' className='contents' data-testid={testId}>
|
|
98
|
+
<div role='none' className='contents' {...(id && { 'data-object-id': id })} data-testid={testId}>
|
|
89
99
|
<FormFields
|
|
90
100
|
{...props}
|
|
91
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
|
)}
|