@dxos/react-ui-form 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5
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/README.md +0 -1
- package/dist/lib/browser/index.mjs +2758 -2113
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +2758 -2113
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +9 -9
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +85 -7
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +86 -25
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +129 -56
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +43 -0
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +77 -0
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts +18 -0
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts +16 -0
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +83 -0
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/index.d.ts +12 -0
- package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/index.d.ts +2 -4
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
- package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +54 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +86 -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/ObjectProperties/ObjectProperties.d.ts +9 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +83 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.d.ts +43 -0
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts +83 -0
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +38 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts +17 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +3 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +22 -10
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +86 -9
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +4 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +4 -4
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -3
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +64 -54
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field-type.d.ts +19 -0
- package/dist/types/src/util/field-type.d.ts.map +1 -0
- 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/index.d.ts +2 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +7 -0
- package/dist/types/src/util/properties.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +3 -3
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +46 -43
- package/src/components/FieldEditor/FieldEditor.stories.tsx +51 -31
- package/src/components/FieldEditor/FieldEditor.test.tsx +13 -13
- package/src/components/FieldEditor/FieldEditor.tsx +88 -94
- package/src/components/Form/AUDIT.md +17 -0
- package/src/components/Form/Form.stories.tsx +144 -221
- package/src/components/Form/Form.tsx +359 -95
- package/src/components/Form/FormField.tsx +304 -0
- package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
- package/src/components/Form/FormFieldComponent.tsx +171 -0
- package/src/components/Form/FormFieldSet.tsx +144 -0
- package/src/components/Form/README.md +3 -0
- package/src/components/Form/fields/ArrayField.tsx +154 -0
- package/src/components/Form/fields/BooleanField.tsx +22 -0
- package/src/components/Form/fields/GeoPointField.tsx +120 -0
- package/src/components/Form/fields/MarkdownField.tsx +78 -0
- package/src/components/Form/fields/NumberField.tsx +39 -0
- package/src/components/Form/fields/RefField.tsx +198 -0
- package/src/components/Form/fields/SelectField.tsx +65 -0
- package/src/components/Form/fields/SelectOptionField.stories.tsx +65 -0
- package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +49 -45
- package/src/components/Form/fields/TextAreaField.tsx +38 -0
- package/src/components/Form/fields/TextField.tsx +38 -0
- package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -10
- package/src/components/Form/fields/index.ts +15 -0
- package/src/components/Form/index.ts +3 -4
- package/src/components/ObjectForm/ObjectForm.tsx +113 -0
- package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +209 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +186 -0
- package/src/components/ObjectPicker/index.ts +5 -0
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +63 -0
- package/src/components/ObjectProperties/ObjectProperties.tsx +129 -0
- package/src/components/ObjectProperties/index.ts +5 -0
- package/src/components/Settings/Settings.stories.tsx +103 -0
- package/src/components/Settings/Settings.tsx +81 -0
- package/src/components/Settings/SettingsFieldSet.tsx +211 -0
- package/src/components/Settings/SettingsItem.tsx +62 -0
- package/src/components/Settings/index.ts +6 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +144 -61
- package/src/components/ViewEditor/ViewEditor.test.tsx +22 -22
- package/src/components/ViewEditor/ViewEditor.tsx +353 -218
- package/src/components/index.ts +4 -3
- package/src/components/testing.tsx +28 -12
- package/src/hooks/index.ts +2 -3
- package/src/hooks/useFormHandler.ts +380 -0
- package/src/hooks/useKeyHandler.tsx +37 -0
- package/src/translations.ts +67 -58
- package/src/util/field-type.ts +57 -0
- package/src/util/field.ts +33 -43
- package/src/util/formatting.ts +22 -22
- package/src/util/index.ts +2 -1
- package/src/util/properties.ts +18 -0
- package/src/util/refs.ts +17 -19
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/index.d.ts +0 -2
- package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
- package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
- package/dist/types/src/components/Form/Defaults.d.ts +0 -14
- package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormActions.d.ts +0 -9
- package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormContent.d.ts +0 -57
- package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
- package/dist/types/src/components/Form/FormContext.d.ts +0 -23
- package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
- package/dist/types/src/components/Form/Input.d.ts +0 -26
- package/dist/types/src/components/Form/Input.d.ts.map +0 -1
- package/dist/types/src/components/Form/RefField.d.ts +0 -12
- package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
- package/dist/types/src/components/Form/custom/index.d.ts +0 -4
- package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
- package/dist/types/src/components/Form/factory.d.ts +0 -8
- package/dist/types/src/components/Form/factory.d.ts.map +0 -1
- 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/dist/types/src/hooks/useForm.d.ts +0 -67
- package/dist/types/src/hooks/useForm.d.ts.map +0 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
- package/dist/types/src/util/schema.d.ts +0 -3
- package/dist/types/src/util/schema.d.ts.map +0 -1
- package/src/components/ControlSection/ControlSection.stories.tsx +0 -41
- package/src/components/ControlSection/ControlSection.tsx +0 -139
- package/src/components/Form/ArrayField.tsx +0 -124
- package/src/components/Form/Defaults.tsx +0 -203
- package/src/components/Form/FormActions.tsx +0 -53
- package/src/components/Form/FormContent.tsx +0 -240
- package/src/components/Form/FormContext.tsx +0 -110
- package/src/components/Form/Input.tsx +0 -51
- package/src/components/Form/RefField.tsx +0 -213
- package/src/components/Form/custom/GeoPointInput.tsx +0 -72
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +0 -61
- package/src/components/Form/custom/index.ts +0 -7
- package/src/components/Form/factory.tsx +0 -31
- 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
- package/src/hooks/useForm.ts +0 -291
- package/src/hooks/useQueryRefOptions.ts +0 -53
- package/src/hooks/useRefQueryLookupHandler.ts +0 -39
- package/src/util/schema.ts +0 -14
|
@@ -2,284 +2,419 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Array from 'effect/Array';
|
|
7
|
+
import * as Match from 'effect/Match';
|
|
8
|
+
import * as Option from 'effect/Option';
|
|
9
|
+
import * as Schema from 'effect/Schema';
|
|
10
|
+
import React, { forwardRef, useCallback, useContext, useImperativeHandle, useMemo, useState } from 'react';
|
|
7
11
|
|
|
8
|
-
import { type SchemaRegistry } from '@dxos/echo
|
|
9
|
-
import {
|
|
12
|
+
import { Entity, Feed, Filter, Format, Obj, Query, QueryAST, Ref, type SchemaRegistry } from '@dxos/echo';
|
|
13
|
+
import { View } from '@dxos/echo';
|
|
14
|
+
import { EchoSchema, type JsonProp, isMutable, toJsonSchema } from '@dxos/echo/internal';
|
|
10
15
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import {
|
|
16
|
+
import { useObject, useQuery } from '@dxos/react-client/echo';
|
|
17
|
+
import { IconButton, Input, Message, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
18
|
+
import { QueryForm, type QueryFormProps } from '@dxos/react-ui-components';
|
|
12
19
|
import { List } from '@dxos/react-ui-list';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
import {
|
|
21
|
+
ParentLabelAnnotation,
|
|
22
|
+
ProjectionModel,
|
|
23
|
+
VIEW_FIELD_LIMIT,
|
|
24
|
+
createEchoChangeCallback,
|
|
25
|
+
getTypenameFromQuery,
|
|
26
|
+
} from '@dxos/schema';
|
|
27
|
+
import { mx, osTranslations, subtleHover } from '@dxos/ui-theme';
|
|
16
28
|
|
|
17
29
|
import { translationKey } from '../../translations';
|
|
18
30
|
import { FieldEditor } from '../FieldEditor';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
typename: Format.URL.annotations({
|
|
28
|
-
title: 'Typename',
|
|
29
|
-
}),
|
|
30
|
-
}).pipe(Schema.mutable);
|
|
31
|
-
|
|
32
|
-
type ViewMetaType = Schema.Schema.Type<typeof ViewMetaSchema>;
|
|
31
|
+
import {
|
|
32
|
+
Form,
|
|
33
|
+
type FormFieldComponent,
|
|
34
|
+
type FormFieldComponentProps,
|
|
35
|
+
FormFieldLabel,
|
|
36
|
+
type FormFieldMap,
|
|
37
|
+
type FormRootProps,
|
|
38
|
+
} from '../Form';
|
|
33
39
|
|
|
34
40
|
export type ViewEditorProps = ThemedClassName<
|
|
35
41
|
{
|
|
36
42
|
schema: Schema.Schema.AnyNoContext;
|
|
37
|
-
view:
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
view: View.View;
|
|
44
|
+
mode?: 'schema' | 'tag';
|
|
45
|
+
registry?: SchemaRegistry.SchemaRegistry;
|
|
40
46
|
showHeading?: boolean;
|
|
41
|
-
|
|
47
|
+
onQueryChanged?: (query: QueryAST.Query, target?: string) => void;
|
|
42
48
|
onDelete?: (fieldId: string) => void;
|
|
43
|
-
} & Pick<
|
|
49
|
+
} & Pick<QueryFormProps, 'types' | 'tags'> &
|
|
50
|
+
Pick<FormRootProps<any>, 'readonly' | 'db'>
|
|
44
51
|
>;
|
|
45
52
|
|
|
46
53
|
/**
|
|
47
54
|
* Schema-based object form.
|
|
48
55
|
*/
|
|
49
|
-
export const ViewEditor = (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
57
|
+
(
|
|
58
|
+
{
|
|
59
|
+
classNames,
|
|
60
|
+
schema,
|
|
61
|
+
view,
|
|
62
|
+
mode = 'schema',
|
|
63
|
+
registry,
|
|
64
|
+
db,
|
|
65
|
+
readonly,
|
|
66
|
+
showHeading = false,
|
|
67
|
+
types,
|
|
68
|
+
tags,
|
|
69
|
+
onQueryChanged,
|
|
70
|
+
onDelete,
|
|
71
|
+
},
|
|
72
|
+
forwardedRef,
|
|
73
|
+
) => {
|
|
74
|
+
const atomRegistry = useContext(RegistryContext);
|
|
75
|
+
const schemaReadonly = !isMutable(schema);
|
|
76
|
+
const { t } = useTranslation(translationKey);
|
|
77
|
+
|
|
78
|
+
const projectionModel = useMemo(() => {
|
|
79
|
+
// Use reactive and mutable version of json schema when schema is mutable.
|
|
80
|
+
const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
|
|
81
|
+
|
|
82
|
+
// Always use createEchoChangeCallback since the view is ECHO-backed.
|
|
83
|
+
// Pass schema only when mutable to allow schema mutations.
|
|
84
|
+
const change = createEchoChangeCallback(view, schema instanceof EchoSchema ? schema : undefined);
|
|
85
|
+
|
|
86
|
+
const model = new ProjectionModel({
|
|
87
|
+
registry: atomRegistry,
|
|
88
|
+
view,
|
|
89
|
+
baseSchema: jsonSchema,
|
|
90
|
+
change,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return model;
|
|
94
|
+
}, [atomRegistry, schema, view]);
|
|
95
|
+
|
|
96
|
+
useImperativeHandle(forwardedRef, () => projectionModel, [projectionModel]);
|
|
97
|
+
|
|
98
|
+
const queueTarget = Match.value(view.query.ast).pipe(
|
|
99
|
+
Match.when({ type: 'from' }, ({ from }) => {
|
|
100
|
+
if (from._tag !== 'scope') {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
return Option.fromNullable(from.scope.queues).pipe(
|
|
104
|
+
Option.flatMap((queues) => Array.head(queues)),
|
|
105
|
+
Option.map(String),
|
|
106
|
+
Option.getOrUndefined,
|
|
107
|
+
);
|
|
108
|
+
}),
|
|
109
|
+
Match.orElse(() => undefined),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const feeds = useQuery(db, Filter.type(Feed.Feed));
|
|
113
|
+
|
|
114
|
+
const targetRef = useMemo(() => {
|
|
115
|
+
if (!queueTarget) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const feed = feeds.find((feed) => Feed.getQueueDxn(feed)?.toString() === queueTarget);
|
|
119
|
+
return feed ? Ref.fromDXN(Entity.getDXN(feed)) : undefined;
|
|
120
|
+
}, [queueTarget, feeds]);
|
|
121
|
+
|
|
122
|
+
const viewSchema = useMemo(() => {
|
|
123
|
+
const base = Schema.Struct({
|
|
124
|
+
query:
|
|
125
|
+
mode === 'schema'
|
|
126
|
+
? Format.URL.annotations({ title: 'Record type' })
|
|
127
|
+
: QueryAST.Query.annotations({ title: 'Query' }),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (mode === 'tag') {
|
|
131
|
+
return Schema.Struct({
|
|
132
|
+
...base.fields,
|
|
133
|
+
// TODO(wittjosiah): Replace Type.Feed with Dataset.Dataset when Ref.Ref supports unions.
|
|
134
|
+
target: Ref.Ref(Feed.Feed).pipe(
|
|
135
|
+
Schema.annotations({ title: 'Target Feed' }),
|
|
136
|
+
ParentLabelAnnotation.set(true),
|
|
137
|
+
Schema.optional,
|
|
138
|
+
),
|
|
139
|
+
}).pipe(Schema.mutable);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return base.pipe(Schema.mutable);
|
|
143
|
+
}, [mode]);
|
|
144
|
+
|
|
145
|
+
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
146
|
+
// TODO(burdon): Settings should have domain name owned by user.
|
|
147
|
+
const viewValues = useMemo(
|
|
148
|
+
() => ({
|
|
149
|
+
query: mode === 'schema' ? getTypenameFromQuery(view.query.ast) : view.query.ast,
|
|
150
|
+
target: targetRef,
|
|
151
|
+
}),
|
|
152
|
+
[mode, view.query.ast, targetRef],
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const fieldMap = useMemo<FormFieldMap | undefined>(
|
|
156
|
+
() => (mode === 'tag' ? customFields({ types, tags }) : undefined),
|
|
157
|
+
[mode, types, tags],
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const handleUpdate = useCallback(
|
|
161
|
+
(values: any) => {
|
|
162
|
+
const targetValue = values.target;
|
|
163
|
+
let queueDxn: string | undefined;
|
|
164
|
+
|
|
165
|
+
if (Ref.isRef(targetValue)) {
|
|
166
|
+
const feedDxn = targetValue.dxn.toString();
|
|
167
|
+
const feed = feeds.find((feed) => Obj.getDXN(feed).toString() === feedDxn);
|
|
168
|
+
if (feed) {
|
|
169
|
+
queueDxn = Feed.getQueueDxn(feed)?.toString();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// TODO(wittjosiah): Deep-clone the AST to plain JS or ECHO proxy arrays become objects with numeric keys.
|
|
174
|
+
const query =
|
|
175
|
+
mode === 'schema'
|
|
176
|
+
? Query.select(Filter.typename(values.query)).ast
|
|
177
|
+
: JSON.parse(JSON.stringify(values.query));
|
|
178
|
+
onQueryChanged?.(query, queueDxn);
|
|
179
|
+
},
|
|
180
|
+
[onQueryChanged, mode, feeds],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const handleDelete = useCallback(
|
|
184
|
+
(fieldId: string) => {
|
|
185
|
+
invariant(!readonly);
|
|
186
|
+
onDelete?.(fieldId);
|
|
187
|
+
},
|
|
188
|
+
[onDelete, readonly],
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div role='none' className={mx(classNames)}>
|
|
193
|
+
{/* If readonly is set, then the callout is not needed. */}
|
|
194
|
+
{schemaReadonly && !readonly && (
|
|
195
|
+
<Message.Root valence='info' classNames='my-form-padding'>
|
|
196
|
+
<Message.Title>{t('system-schema.description')}</Message.Title>
|
|
197
|
+
</Message.Root>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
{/* TODO(burdon): Is the form read-only or just the schema? */}
|
|
201
|
+
<Form.Root schema={viewSchema} values={viewValues} fieldMap={fieldMap} db={db} onValuesChanged={handleUpdate}>
|
|
202
|
+
<Form.FieldSet />
|
|
203
|
+
|
|
204
|
+
<FormFieldLabel label={t('fields.label')} asChild />
|
|
205
|
+
<FieldList
|
|
206
|
+
schema={schema}
|
|
207
|
+
view={view}
|
|
208
|
+
registry={registry}
|
|
209
|
+
readonly={readonly}
|
|
210
|
+
showHeading={showHeading}
|
|
211
|
+
onDelete={handleDelete}
|
|
212
|
+
/>
|
|
213
|
+
</Form.Root>
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
},
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
type FieldListProps = Pick<ViewEditorProps, 'schema' | 'view' | 'registry' | 'readonly' | 'showHeading' | 'onDelete'>;
|
|
220
|
+
|
|
221
|
+
const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDelete }: FieldListProps) => {
|
|
222
|
+
const atomRegistry = useContext(RegistryContext);
|
|
223
|
+
const schemaReadonly = !isMutable(schema);
|
|
60
224
|
const { t } = useTranslation(translationKey);
|
|
61
|
-
|
|
225
|
+
|
|
226
|
+
// Subscribe to view changes for reactivity.
|
|
227
|
+
const [viewSnapshot] = useObject(view);
|
|
228
|
+
|
|
229
|
+
const projectionModel = useMemo(() => {
|
|
62
230
|
// Use reactive and mutable version of json schema when schema is mutable.
|
|
63
231
|
const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
|
|
64
|
-
return new ViewProjection(jsonSchema, view);
|
|
65
|
-
}, [schema, view]);
|
|
66
|
-
const [field, setField] = useState<FieldType>();
|
|
67
|
-
const readonly = _readonly || !isMutable(schema);
|
|
68
|
-
|
|
69
|
-
// TODO(burdon): Should be reactive.
|
|
70
|
-
const viewValues = useMemo(() => {
|
|
71
|
-
return {
|
|
72
|
-
name: view.name,
|
|
73
|
-
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
74
|
-
// TODO(burdon): Settings should have domain name owned by user.
|
|
75
|
-
typename: view.query.typename,
|
|
76
|
-
};
|
|
77
|
-
}, [view]);
|
|
78
|
-
|
|
79
|
-
const handleSelect = useCallback(
|
|
80
|
-
(field: FieldType) => {
|
|
81
|
-
if (readonly) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
setField((f) => (f === field ? undefined : field));
|
|
85
|
-
},
|
|
86
|
-
[readonly],
|
|
87
|
-
);
|
|
88
232
|
|
|
89
|
-
|
|
233
|
+
// Always use createEchoChangeCallback since the view is ECHO-backed.
|
|
234
|
+
// Pass schema only when mutable to allow schema mutations.
|
|
235
|
+
const change = createEchoChangeCallback(view, schema instanceof EchoSchema ? schema : undefined);
|
|
236
|
+
|
|
237
|
+
const model = new ProjectionModel({
|
|
238
|
+
registry: atomRegistry,
|
|
239
|
+
view,
|
|
240
|
+
baseSchema: jsonSchema,
|
|
241
|
+
change,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return model;
|
|
245
|
+
}, [atomRegistry, schema, view]);
|
|
246
|
+
|
|
247
|
+
const [expandedField, setExpandedField] = useState<View.FieldType['id']>();
|
|
90
248
|
|
|
91
249
|
const handleAdd = useCallback(() => {
|
|
92
250
|
invariant(!readonly);
|
|
93
|
-
const field =
|
|
94
|
-
|
|
95
|
-
}, [
|
|
96
|
-
|
|
97
|
-
const handleUpdate = useCallback(
|
|
98
|
-
({ name, typename }: ViewMetaType) => {
|
|
99
|
-
invariant(!readonly);
|
|
100
|
-
requestAnimationFrame(() => {
|
|
101
|
-
if (view.name !== name) {
|
|
102
|
-
view.name = name;
|
|
103
|
-
}
|
|
251
|
+
const field = projectionModel.createFieldProjection();
|
|
252
|
+
setExpandedField(field.id);
|
|
253
|
+
}, [projectionModel, readonly]);
|
|
104
254
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
});
|
|
255
|
+
const handleToggleField = useCallback(
|
|
256
|
+
(field: View.FieldType) => {
|
|
257
|
+
setExpandedField((prevExpandedFieldId) => (prevExpandedFieldId === field.id ? undefined : field.id));
|
|
109
258
|
},
|
|
110
|
-
[
|
|
259
|
+
[readonly],
|
|
111
260
|
);
|
|
112
261
|
|
|
113
262
|
const handleDelete = useCallback(
|
|
114
263
|
(fieldId: string) => {
|
|
115
264
|
invariant(!readonly);
|
|
116
|
-
if (fieldId ===
|
|
117
|
-
|
|
265
|
+
if (fieldId === expandedField) {
|
|
266
|
+
setExpandedField(undefined);
|
|
118
267
|
}
|
|
119
268
|
|
|
120
269
|
onDelete?.(fieldId);
|
|
121
270
|
},
|
|
122
|
-
[
|
|
271
|
+
[expandedField, onDelete, readonly],
|
|
123
272
|
);
|
|
124
273
|
|
|
125
274
|
const handleMove = useCallback(
|
|
126
275
|
(fromIndex: number, toIndex: number) => {
|
|
127
276
|
invariant(!readonly);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
277
|
+
Obj.change(view, (view) => {
|
|
278
|
+
// NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
|
|
279
|
+
const fields = [...view.projection.fields];
|
|
280
|
+
const [moved] = fields.splice(fromIndex, 1);
|
|
281
|
+
fields.splice(toIndex, 0, moved);
|
|
282
|
+
view.projection.fields = fields;
|
|
283
|
+
});
|
|
133
284
|
},
|
|
134
|
-
[
|
|
285
|
+
[view, readonly],
|
|
135
286
|
);
|
|
136
287
|
|
|
137
|
-
const handleClose = useCallback(() =>
|
|
138
|
-
|
|
139
|
-
const hiddenProperties = projection.getHiddenProperties();
|
|
288
|
+
const handleClose = useCallback(() => setExpandedField(undefined), []);
|
|
140
289
|
|
|
141
290
|
const handleHide = useCallback(
|
|
142
291
|
(fieldId: string) => {
|
|
143
|
-
|
|
144
|
-
|
|
292
|
+
setExpandedField(undefined);
|
|
293
|
+
projectionModel.hideFieldProjection(fieldId);
|
|
145
294
|
},
|
|
146
|
-
[
|
|
295
|
+
[projectionModel],
|
|
147
296
|
);
|
|
148
297
|
|
|
149
298
|
const handleShow = useCallback(
|
|
150
299
|
(property: string) => {
|
|
151
|
-
|
|
152
|
-
|
|
300
|
+
setExpandedField(undefined);
|
|
301
|
+
projectionModel.showFieldProjection(property as JsonProp);
|
|
153
302
|
},
|
|
154
|
-
[
|
|
303
|
+
[projectionModel],
|
|
155
304
|
);
|
|
156
305
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
<div role='none' className={mx('is-full', outerSpacing && 'plb-cardSpacingBlock pli-cardSpacingInline')}>
|
|
161
|
-
<Callout.Root valence='info'>
|
|
162
|
-
<Callout.Title>{t('system schema description')}</Callout.Title>
|
|
163
|
-
</Callout.Root>
|
|
164
|
-
</div>
|
|
165
|
-
)}
|
|
306
|
+
if (!viewSnapshot) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
166
309
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)}
|
|
196
|
-
|
|
197
|
-
<div role='list' className='flex flex-col is-full'>
|
|
198
|
-
{fields?.map((field) => (
|
|
199
|
-
<List.Item<FieldType>
|
|
200
|
-
key={field.id}
|
|
201
|
-
item={field}
|
|
202
|
-
classNames={mx(grid, ghostHover, 'overflow-hidden', !readonly && 'cursor-pointer')}
|
|
310
|
+
return (
|
|
311
|
+
<List.Root<View.FieldType>
|
|
312
|
+
items={viewSnapshot.projection.fields as View.FieldType[]}
|
|
313
|
+
isItem={Schema.is(View.FieldSchema)}
|
|
314
|
+
getId={(field) => field.id}
|
|
315
|
+
onMove={readonly ? undefined : handleMove}
|
|
316
|
+
readonly={readonly}
|
|
317
|
+
>
|
|
318
|
+
{({ items: fields }) => (
|
|
319
|
+
<>
|
|
320
|
+
{showHeading && <h3 className='text-sm'>{t('field-path.label')}</h3>}
|
|
321
|
+
<div role='list' className='grid grid-cols-[min-content_1fr_min-content_min-content_min-content]'>
|
|
322
|
+
{fields?.map((field) => {
|
|
323
|
+
const hidden = field.visible === false;
|
|
324
|
+
return (
|
|
325
|
+
<List.Item<View.FieldType>
|
|
326
|
+
key={field.id}
|
|
327
|
+
item={field}
|
|
328
|
+
classNames={'grid grid-cols-subgrid col-span-5'}
|
|
329
|
+
aria-expanded={expandedField === field.id}
|
|
330
|
+
>
|
|
331
|
+
<div
|
|
332
|
+
role='none'
|
|
333
|
+
className={mx(
|
|
334
|
+
subtleHover,
|
|
335
|
+
'grid grid-cols-subgrid col-span-5',
|
|
336
|
+
'rounded-xs cursor-pointer min-h-10',
|
|
337
|
+
)}
|
|
203
338
|
>
|
|
204
|
-
<List.ItemDragHandle />
|
|
205
|
-
<List.ItemTitle onClick={() =>
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
339
|
+
<List.ItemDragHandle disabled={readonly || schemaReadonly} />
|
|
340
|
+
<List.ItemTitle classNames={hidden && 'text-subdued'} onClick={() => handleToggleField(field)}>
|
|
341
|
+
{field.path}
|
|
342
|
+
</List.ItemTitle>
|
|
343
|
+
<List.ItemIconButton
|
|
344
|
+
label={t(hidden ? 'show field label' : 'hide field label')}
|
|
345
|
+
data-testid={hidden ? 'show-field-button' : 'hide-field-button'}
|
|
346
|
+
icon={hidden ? 'ph--eye-closed--regular' : 'ph--eye--regular'}
|
|
347
|
+
autoHide={false}
|
|
348
|
+
disabled={readonly || (!hidden && projectionModel.getFields().length <= 1)}
|
|
349
|
+
onClick={() => (hidden ? handleShow(field.path) : handleHide(field.id))}
|
|
212
350
|
/>
|
|
213
|
-
{/* TODO(burdon): Remove unless implement undo. */}
|
|
214
351
|
{!readonly && (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
352
|
+
<>
|
|
353
|
+
<List.ItemDeleteButton
|
|
354
|
+
label={t('delete-field.label')}
|
|
355
|
+
autoHide={false}
|
|
356
|
+
disabled={readonly || schemaReadonly || viewSnapshot.projection.fields.length <= 1}
|
|
357
|
+
onClick={() => handleDelete(field.id)}
|
|
358
|
+
data-testid='field.delete'
|
|
359
|
+
/>
|
|
360
|
+
<List.ItemIconButton
|
|
361
|
+
iconOnly
|
|
362
|
+
variant='ghost'
|
|
363
|
+
label={t('toggle-expand.label', { ns: osTranslations })}
|
|
364
|
+
icon={expandedField === field.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
|
|
365
|
+
onClick={() => handleToggleField(field)}
|
|
366
|
+
data-testid='field.toggle'
|
|
367
|
+
/>
|
|
368
|
+
</>
|
|
219
369
|
)}
|
|
220
|
-
</
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
<label className={mx(inputTextLabel)}>{t('hidden fields label')}</label>
|
|
230
|
-
|
|
231
|
-
<List.Root<string>
|
|
232
|
-
items={hiddenProperties}
|
|
233
|
-
isItem={(item): item is string => typeof item === 'string'}
|
|
234
|
-
getId={(property) => property}
|
|
235
|
-
>
|
|
236
|
-
{({ items: properties }) => (
|
|
237
|
-
<div role='list' className='flex flex-col is-full'>
|
|
238
|
-
{properties?.map((property) => (
|
|
239
|
-
<List.Item<string>
|
|
240
|
-
key={property}
|
|
241
|
-
item={property}
|
|
242
|
-
classNames={mx(grid, ghostHover, !readonly && 'cursor-pointer')}
|
|
243
|
-
>
|
|
244
|
-
<List.ItemDragHandle disabled />
|
|
245
|
-
<List.ItemTitle>{property}</List.ItemTitle>
|
|
246
|
-
<List.ItemButton
|
|
247
|
-
icon='ph--eye--regular'
|
|
248
|
-
onClick={() => handleShow(property)}
|
|
249
|
-
data-testid='show-field-button'
|
|
370
|
+
</div>
|
|
371
|
+
{expandedField === field.id && !readonly && (
|
|
372
|
+
<div role='none' className='col-span-full mt-1 mb-1 border border-separator rounded-md'>
|
|
373
|
+
<FieldEditor
|
|
374
|
+
readonly={readonly || schemaReadonly}
|
|
375
|
+
registry={registry}
|
|
376
|
+
projection={projectionModel}
|
|
377
|
+
field={field}
|
|
378
|
+
onSave={handleClose}
|
|
250
379
|
/>
|
|
251
|
-
</
|
|
252
|
-
)
|
|
253
|
-
</
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)}
|
|
270
|
-
|
|
271
|
-
{!readonly && !field && (
|
|
272
|
-
<div role='none' className={outerSpacing ? cardSpacing : 'mlb-cardSpacingBlock'}>
|
|
273
|
-
<IconButton
|
|
274
|
-
icon='ph--plus--regular'
|
|
275
|
-
label={t('button add property')}
|
|
276
|
-
onClick={readonly ? undefined : handleAdd}
|
|
277
|
-
// TODO(burdon): Show field limit in ux (not tooltip).
|
|
278
|
-
disabled={view.fields.length >= VIEW_FIELD_LIMIT}
|
|
279
|
-
classNames='flex is-full'
|
|
280
|
-
/>
|
|
281
|
-
</div>
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
</List.Item>
|
|
383
|
+
);
|
|
384
|
+
})}
|
|
385
|
+
</div>
|
|
386
|
+
{!readonly && !expandedField && (
|
|
387
|
+
<div role='none' className='my-form-padding'>
|
|
388
|
+
<IconButton
|
|
389
|
+
icon='ph--plus--regular'
|
|
390
|
+
label={t('add-property-button.label')}
|
|
391
|
+
onClick={handleAdd}
|
|
392
|
+
disabled={viewSnapshot.projection.fields.length >= VIEW_FIELD_LIMIT}
|
|
393
|
+
classNames='w-full'
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
)}
|
|
397
|
+
</>
|
|
282
398
|
)}
|
|
283
|
-
</
|
|
399
|
+
</List.Root>
|
|
284
400
|
);
|
|
285
401
|
};
|
|
402
|
+
|
|
403
|
+
const customFields = ({
|
|
404
|
+
types,
|
|
405
|
+
tags,
|
|
406
|
+
}: Pick<ViewEditorProps, 'types' | 'tags'>): Record<string, FormFieldComponent> => ({
|
|
407
|
+
query: ({ type, readonly, label, getValue, onValueChange }: FormFieldComponentProps) => {
|
|
408
|
+
const handleChange = useCallback<NonNullable<QueryFormProps['onChange']>>(
|
|
409
|
+
(query) => onValueChange(type, query.ast),
|
|
410
|
+
[onValueChange],
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<Input.Root>
|
|
415
|
+
<FormFieldLabel readonly={readonly} label={label} />
|
|
416
|
+
<QueryForm initialQuery={getValue()} types={types} tags={tags} onChange={handleChange} />
|
|
417
|
+
</Input.Root>
|
|
418
|
+
);
|
|
419
|
+
},
|
|
420
|
+
});
|
package/src/components/index.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './deprecated';
|
|
6
|
-
|
|
7
|
-
export * from './ControlSection';
|
|
8
5
|
export * from './FieldEditor';
|
|
9
6
|
export * from './Form';
|
|
7
|
+
export * from './ObjectForm';
|
|
8
|
+
export * from './ObjectPicker';
|
|
9
|
+
export * from './ObjectProperties';
|
|
10
|
+
export * from './Settings';
|
|
10
11
|
export * from './ViewEditor';
|