@dxos/react-ui-form 0.8.4-main.c85a9c8dae → 0.8.4-main.d05539e30a
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/LICENSE +102 -5
- package/dist/lib/browser/index.mjs +1028 -625
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +82 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1028 -625
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +84 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +63 -61
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +40 -12
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +76 -74
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +11 -5
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +8 -7
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +5 -9
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/ArrayField.d.ts +4 -4
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +8 -1
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +84 -0
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +15 -7
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +63 -61
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TextField.d.ts +1 -1
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TupleField.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 +26 -4
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +63 -61
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- 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 +127 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.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 +28 -33
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -1
- package/dist/types/src/components/Settings/Settings.stories.d.ts +64 -61
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -1
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +2 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -4
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +65 -63
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- 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/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useKeyHandler.d.ts +2 -1
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +63 -61
- 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.map +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +7 -1
- package/dist/types/src/util/properties.d.ts.map +1 -1
- package/dist/types/src/util/properties.test.d.ts +2 -0
- package/dist/types/src/util/properties.test.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +1 -0
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -41
- package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
- package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
- package/src/components/FieldEditor/FieldEditor.tsx +4 -3
- package/src/components/Form/AUDIT.md +17 -0
- package/src/components/Form/Form.stories.tsx +25 -38
- package/src/components/Form/Form.tsx +77 -48
- package/src/components/Form/FormField.tsx +27 -15
- package/src/components/Form/FormFieldComponent.tsx +6 -4
- package/src/components/Form/FormFieldSet.tsx +98 -77
- package/src/components/Form/fields/ArrayField.tsx +14 -8
- package/src/components/Form/fields/GeoPointField.tsx +8 -7
- package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
- package/src/components/Form/fields/MarkdownField.tsx +135 -45
- package/src/components/Form/fields/RefField.tsx +79 -39
- package/src/components/Form/fields/SelectOptionField.stories.tsx +8 -6
- package/src/components/Form/fields/SelectOptionField.tsx +12 -12
- package/src/components/Form/fields/TextField.tsx +2 -2
- package/src/components/Form/fields/TupleField.tsx +1 -1
- package/src/components/ObjectForm/ObjectForm.tsx +114 -0
- package/src/components/ObjectForm/index.ts +5 -0
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +12 -10
- package/src/components/ObjectPicker/ObjectPicker.tsx +92 -65
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +362 -0
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +101 -0
- package/src/components/ObjectProperties/ObjectProperties.tsx +148 -0
- package/src/components/ObjectProperties/index.ts +5 -0
- package/src/components/Settings/AUDIT.md +51 -0
- package/src/components/Settings/Settings.stories.tsx +69 -21
- package/src/components/Settings/Settings.tsx +33 -183
- package/src/components/Settings/SettingsFieldSet.tsx +257 -0
- package/src/components/Settings/SettingsItem.tsx +58 -0
- package/src/components/Settings/SettingsSelectField.tsx +59 -0
- package/src/components/Settings/index.ts +2 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +14 -15
- package/src/components/ViewEditor/ViewEditor.test.tsx +0 -1
- package/src/components/ViewEditor/ViewEditor.tsx +40 -36
- package/src/components/index.ts +2 -0
- package/src/components/testing.tsx +19 -11
- package/src/hooks/useKeyHandler.tsx +7 -7
- package/src/index.ts +0 -4
- package/src/translations.ts +66 -63
- package/src/util/field-type.ts +60 -0
- package/src/util/index.ts +1 -0
- package/src/util/properties.test.ts +63 -0
- package/src/util/properties.ts +14 -3
- package/src/util/refs.ts +7 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Select } from '@dxos/react-ui';
|
|
8
|
+
|
|
9
|
+
import { type SettingsFieldProps } from './SettingsFieldSet';
|
|
10
|
+
|
|
11
|
+
export type SelectFieldOption = {
|
|
12
|
+
value: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CreateSelectFieldOptions = {
|
|
17
|
+
/** Options to display. Strings are used as both value and label. */
|
|
18
|
+
options: ReadonlyArray<string | SelectFieldOption>;
|
|
19
|
+
/** Label for the sentinel option that maps to `undefined`. Defaults to `'Default'`. Pass `null` to omit. */
|
|
20
|
+
defaultLabel?: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Factory for a `SettingsFieldSet` `fieldMap` entry that renders a Select control
|
|
25
|
+
* over a fixed list of options, with an optional sentinel for `undefined`.
|
|
26
|
+
*/
|
|
27
|
+
export const createSelectField = ({
|
|
28
|
+
options,
|
|
29
|
+
defaultLabel = 'Default',
|
|
30
|
+
}: CreateSelectFieldOptions): React.FC<SettingsFieldProps<string | undefined>> => {
|
|
31
|
+
const normalized = options.map((option) => (typeof option === 'string' ? { value: option, label: option } : option));
|
|
32
|
+
const hasDefault = defaultLabel !== null;
|
|
33
|
+
// Per-instance sentinel avoids collisions with real option values; empty string when the
|
|
34
|
+
// sentinel is omitted so `undefined` maps to a value Select.Root treats as "no selection".
|
|
35
|
+
const sentinel = hasDefault ? `__default__${Math.random().toString(36).slice(2)}` : '';
|
|
36
|
+
|
|
37
|
+
return ({ value, onChange, readonly }) => (
|
|
38
|
+
<Select.Root
|
|
39
|
+
disabled={readonly}
|
|
40
|
+
value={value ?? sentinel}
|
|
41
|
+
onValueChange={(next) => onChange(hasDefault && next === sentinel ? undefined : next)}
|
|
42
|
+
>
|
|
43
|
+
<Select.TriggerButton disabled={readonly} />
|
|
44
|
+
<Select.Portal>
|
|
45
|
+
<Select.Content>
|
|
46
|
+
<Select.Viewport>
|
|
47
|
+
{hasDefault && <Select.Option value={sentinel}>{defaultLabel}</Select.Option>}
|
|
48
|
+
{normalized.map((option) => (
|
|
49
|
+
<Select.Option key={option.value} value={option.value}>
|
|
50
|
+
{option.label ?? option.value}
|
|
51
|
+
</Select.Option>
|
|
52
|
+
))}
|
|
53
|
+
</Select.Viewport>
|
|
54
|
+
<Select.Arrow />
|
|
55
|
+
</Select.Content>
|
|
56
|
+
</Select.Portal>
|
|
57
|
+
</Select.Root>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -6,8 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Tag, Type } from '@dxos/echo';
|
|
10
|
-
import { type View } from '@dxos/echo';
|
|
9
|
+
import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Tag, Type, type View } from '@dxos/echo';
|
|
11
10
|
import { type EchoSchema, Format, type Mutable } from '@dxos/echo/internal';
|
|
12
11
|
import { useQuery } from '@dxos/react-client/echo';
|
|
13
12
|
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
@@ -16,9 +15,9 @@ import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
|
16
15
|
import { type ProjectionModel, ViewModel, getTypenameFromQuery } from '@dxos/schema';
|
|
17
16
|
import { Employer, Organization, Person, Pipeline } from '@dxos/types';
|
|
18
17
|
|
|
19
|
-
import { translations } from '
|
|
20
|
-
import { TestLayout, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
18
|
+
import { translations } from '#translations';
|
|
21
19
|
|
|
20
|
+
import { TestLayout, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
22
21
|
import { ViewEditor, type ViewEditorProps } from './ViewEditor';
|
|
23
22
|
|
|
24
23
|
const types = [
|
|
@@ -36,9 +35,9 @@ export type ViewEditorDebugObjects = {
|
|
|
36
35
|
projection: ProjectionModel;
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
type
|
|
38
|
+
type DefaultStoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
|
|
40
39
|
|
|
41
|
-
const DefaultStory = (props:
|
|
40
|
+
const DefaultStory = (props: DefaultStoryProps) => {
|
|
42
41
|
const { space } = useClientStory();
|
|
43
42
|
const [schema, setSchema] = useState<EchoSchema>();
|
|
44
43
|
const [view, setView] = useState<View.View>();
|
|
@@ -54,7 +53,7 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
54
53
|
salary: Format.Currency(),
|
|
55
54
|
}).pipe(
|
|
56
55
|
Type.object({
|
|
57
|
-
typename: 'example.
|
|
56
|
+
typename: 'com.example.type.test',
|
|
58
57
|
version: '0.1.0',
|
|
59
58
|
}),
|
|
60
59
|
);
|
|
@@ -65,7 +64,7 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
65
64
|
completed: Schema.Boolean,
|
|
66
65
|
}).pipe(
|
|
67
66
|
Type.object({
|
|
68
|
-
typename: 'example.
|
|
67
|
+
typename: 'com.example.type.alternate',
|
|
69
68
|
version: '0.1.0',
|
|
70
69
|
}),
|
|
71
70
|
);
|
|
@@ -90,9 +89,9 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
90
89
|
|
|
91
90
|
if (props.mode === 'tag') {
|
|
92
91
|
const queue = target && DXN.tryParse(target) ? target : undefined;
|
|
93
|
-
const query = queue ? Query.fromAst(newQuery).from({
|
|
94
|
-
Obj.
|
|
95
|
-
|
|
92
|
+
const query = queue ? Query.fromAst(newQuery).from({ feeds: [queue] }) : Query.fromAst(newQuery);
|
|
93
|
+
Obj.update(view, (view) => {
|
|
94
|
+
view.query.ast = query.ast as Mutable<typeof query.ast>;
|
|
96
95
|
});
|
|
97
96
|
|
|
98
97
|
const typename = getTypenameFromQuery(query.ast);
|
|
@@ -105,13 +104,13 @@ const DefaultStory = (props: StoryProps) => {
|
|
|
105
104
|
query,
|
|
106
105
|
jsonSchema: newSchema.jsonSchema,
|
|
107
106
|
});
|
|
108
|
-
Obj.
|
|
109
|
-
|
|
107
|
+
Obj.update(view, (view) => {
|
|
108
|
+
view.projection = Obj.getSnapshot(newView).projection as Mutable<typeof view.projection>;
|
|
110
109
|
});
|
|
111
110
|
setSchema(() => newSchema);
|
|
112
111
|
} else {
|
|
113
|
-
Obj.
|
|
114
|
-
|
|
112
|
+
Obj.update(view, (view) => {
|
|
113
|
+
view.query.ast = newQuery as Mutable<typeof newQuery>;
|
|
115
114
|
});
|
|
116
115
|
schema.updateTypename(getTypenameFromQuery(newQuery));
|
|
117
116
|
}
|
|
@@ -11,7 +11,6 @@ import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
|
|
|
11
11
|
import { ProjectionModel } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
14
|
-
|
|
15
14
|
import * as stories from './ViewEditor.stories';
|
|
16
15
|
import { type ViewEditorDebugObjects } from './ViewEditor.stories';
|
|
17
16
|
|
|
@@ -9,18 +9,24 @@ import * as Option from 'effect/Option';
|
|
|
9
9
|
import * as Schema from 'effect/Schema';
|
|
10
10
|
import React, { forwardRef, useCallback, useContext, useImperativeHandle, useMemo, useState } from 'react';
|
|
11
11
|
|
|
12
|
-
import { Entity, Feed, Filter, Format, Obj, Query, QueryAST, Ref, type SchemaRegistry } from '@dxos/echo';
|
|
13
|
-
import { View } from '@dxos/echo';
|
|
12
|
+
import { Entity, Feed, Filter, Format, Obj, Query, QueryAST, Ref, type SchemaRegistry, View } from '@dxos/echo';
|
|
14
13
|
import { EchoSchema, type JsonProp, isMutable, toJsonSchema } from '@dxos/echo/internal';
|
|
15
14
|
import { invariant } from '@dxos/invariant';
|
|
16
15
|
import { useObject, useQuery } from '@dxos/react-client/echo';
|
|
17
16
|
import { IconButton, Input, Message, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
18
17
|
import { QueryForm, type QueryFormProps } from '@dxos/react-ui-components';
|
|
19
18
|
import { List } from '@dxos/react-ui-list';
|
|
20
|
-
import {
|
|
21
|
-
|
|
19
|
+
import {
|
|
20
|
+
ParentLabelAnnotation,
|
|
21
|
+
ProjectionModel,
|
|
22
|
+
VIEW_FIELD_LIMIT,
|
|
23
|
+
createEchoChangeCallback,
|
|
24
|
+
getTypenameFromQuery,
|
|
25
|
+
} from '@dxos/schema';
|
|
26
|
+
import { mx, osTranslations } from '@dxos/ui-theme';
|
|
27
|
+
|
|
28
|
+
import { translationKey } from '#translations';
|
|
22
29
|
|
|
23
|
-
import { translationKey } from '../../translations';
|
|
24
30
|
import { FieldEditor } from '../FieldEditor';
|
|
25
31
|
import {
|
|
26
32
|
Form,
|
|
@@ -94,8 +100,8 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
94
100
|
if (from._tag !== 'scope') {
|
|
95
101
|
return undefined;
|
|
96
102
|
}
|
|
97
|
-
return Option.fromNullable(from.scope.
|
|
98
|
-
Option.flatMap((
|
|
103
|
+
return Option.fromNullable(from.scope.feeds).pipe(
|
|
104
|
+
Option.flatMap((feeds) => Array.head(feeds)),
|
|
99
105
|
Option.map(String),
|
|
100
106
|
Option.getOrUndefined,
|
|
101
107
|
);
|
|
@@ -124,7 +130,12 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
124
130
|
if (mode === 'tag') {
|
|
125
131
|
return Schema.Struct({
|
|
126
132
|
...base.fields,
|
|
127
|
-
|
|
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
|
+
),
|
|
128
139
|
}).pipe(Schema.mutable);
|
|
129
140
|
}
|
|
130
141
|
|
|
@@ -149,13 +160,13 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
149
160
|
const handleUpdate = useCallback(
|
|
150
161
|
(values: any) => {
|
|
151
162
|
const targetValue = values.target;
|
|
152
|
-
let
|
|
163
|
+
let feedDXN: string | undefined;
|
|
153
164
|
|
|
154
165
|
if (Ref.isRef(targetValue)) {
|
|
155
|
-
const
|
|
156
|
-
const feed = feeds.find((feed) => Obj.getDXN(feed).toString() ===
|
|
166
|
+
const targetDXN = targetValue.dxn.toString();
|
|
167
|
+
const feed = feeds.find((feed) => Obj.getDXN(feed).toString() === targetDXN);
|
|
157
168
|
if (feed) {
|
|
158
|
-
|
|
169
|
+
feedDXN = Feed.getQueueDxn(feed)?.toString();
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
|
|
@@ -164,7 +175,7 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
164
175
|
mode === 'schema'
|
|
165
176
|
? Query.select(Filter.typename(values.query)).ast
|
|
166
177
|
: JSON.parse(JSON.stringify(values.query));
|
|
167
|
-
onQueryChanged?.(query,
|
|
178
|
+
onQueryChanged?.(query, feedDXN);
|
|
168
179
|
},
|
|
169
180
|
[onQueryChanged, mode, feeds],
|
|
170
181
|
);
|
|
@@ -178,11 +189,11 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
178
189
|
);
|
|
179
190
|
|
|
180
191
|
return (
|
|
181
|
-
<div
|
|
192
|
+
<div className={mx(classNames)}>
|
|
182
193
|
{/* If readonly is set, then the callout is not needed. */}
|
|
183
194
|
{schemaReadonly && !readonly && (
|
|
184
195
|
<Message.Root valence='info' classNames='my-form-padding'>
|
|
185
|
-
<Message.Title>{t('system
|
|
196
|
+
<Message.Title>{t('system-schema.description')}</Message.Title>
|
|
186
197
|
</Message.Root>
|
|
187
198
|
)}
|
|
188
199
|
|
|
@@ -190,7 +201,7 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
|
|
|
190
201
|
<Form.Root schema={viewSchema} values={viewValues} fieldMap={fieldMap} db={db} onValuesChanged={handleUpdate}>
|
|
191
202
|
<Form.FieldSet />
|
|
192
203
|
|
|
193
|
-
<FormFieldLabel label={t('fields
|
|
204
|
+
<FormFieldLabel label={t('fields.label')} asChild />
|
|
194
205
|
<FieldList
|
|
195
206
|
schema={schema}
|
|
196
207
|
view={view}
|
|
@@ -263,12 +274,12 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
263
274
|
const handleMove = useCallback(
|
|
264
275
|
(fromIndex: number, toIndex: number) => {
|
|
265
276
|
invariant(!readonly);
|
|
266
|
-
Obj.
|
|
277
|
+
Obj.update(view, (view) => {
|
|
267
278
|
// NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
|
|
268
|
-
const fields = [...
|
|
279
|
+
const fields = [...view.projection.fields];
|
|
269
280
|
const [moved] = fields.splice(fromIndex, 1);
|
|
270
281
|
fields.splice(toIndex, 0, moved);
|
|
271
|
-
|
|
282
|
+
view.projection.fields = fields;
|
|
272
283
|
});
|
|
273
284
|
},
|
|
274
285
|
[view, readonly],
|
|
@@ -306,7 +317,7 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
306
317
|
>
|
|
307
318
|
{({ items: fields }) => (
|
|
308
319
|
<>
|
|
309
|
-
{showHeading && <h3 className='text-sm'>{t('field
|
|
320
|
+
{showHeading && <h3 className='text-sm'>{t('field-path.label')}</h3>}
|
|
310
321
|
<div role='list' className='grid grid-cols-[min-content_1fr_min-content_min-content_min-content]'>
|
|
311
322
|
{fields?.map((field) => {
|
|
312
323
|
const hidden = field.visible === false;
|
|
@@ -317,20 +328,13 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
317
328
|
classNames={'grid grid-cols-subgrid col-span-5'}
|
|
318
329
|
aria-expanded={expandedField === field.id}
|
|
319
330
|
>
|
|
320
|
-
<div
|
|
321
|
-
role='none'
|
|
322
|
-
className={mx(
|
|
323
|
-
subtleHover,
|
|
324
|
-
'grid grid-cols-subgrid col-span-5',
|
|
325
|
-
'rounded-xs cursor-pointer min-h-10',
|
|
326
|
-
)}
|
|
327
|
-
>
|
|
331
|
+
<div className='grid grid-cols-subgrid col-span-5 rounded-xs cursor-pointer min-h-10 dx-hover'>
|
|
328
332
|
<List.ItemDragHandle disabled={readonly || schemaReadonly} />
|
|
329
333
|
<List.ItemTitle classNames={hidden && 'text-subdued'} onClick={() => handleToggleField(field)}>
|
|
330
334
|
{field.path}
|
|
331
335
|
</List.ItemTitle>
|
|
332
|
-
<List.
|
|
333
|
-
label={t(hidden ? 'show
|
|
336
|
+
<List.ItemIconButton
|
|
337
|
+
label={t(hidden ? 'show-field.label' : 'hide-field.label')}
|
|
334
338
|
data-testid={hidden ? 'show-field-button' : 'hide-field-button'}
|
|
335
339
|
icon={hidden ? 'ph--eye-closed--regular' : 'ph--eye--regular'}
|
|
336
340
|
autoHide={false}
|
|
@@ -340,16 +344,16 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
340
344
|
{!readonly && (
|
|
341
345
|
<>
|
|
342
346
|
<List.ItemDeleteButton
|
|
343
|
-
label={t('delete
|
|
347
|
+
label={t('delete-field.label')}
|
|
344
348
|
autoHide={false}
|
|
345
349
|
disabled={readonly || schemaReadonly || viewSnapshot.projection.fields.length <= 1}
|
|
346
350
|
onClick={() => handleDelete(field.id)}
|
|
347
351
|
data-testid='field.delete'
|
|
348
352
|
/>
|
|
349
|
-
<
|
|
353
|
+
<List.ItemIconButton
|
|
350
354
|
iconOnly
|
|
351
355
|
variant='ghost'
|
|
352
|
-
label={t('toggle
|
|
356
|
+
label={t('toggle-expand.label', { ns: osTranslations })}
|
|
353
357
|
icon={expandedField === field.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
|
|
354
358
|
onClick={() => handleToggleField(field)}
|
|
355
359
|
data-testid='field.toggle'
|
|
@@ -358,7 +362,7 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
358
362
|
)}
|
|
359
363
|
</div>
|
|
360
364
|
{expandedField === field.id && !readonly && (
|
|
361
|
-
<div
|
|
365
|
+
<div className='col-span-full mt-1 mb-1 border border-separator rounded-md'>
|
|
362
366
|
<FieldEditor
|
|
363
367
|
readonly={readonly || schemaReadonly}
|
|
364
368
|
registry={registry}
|
|
@@ -373,10 +377,10 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
|
|
|
373
377
|
})}
|
|
374
378
|
</div>
|
|
375
379
|
{!readonly && !expandedField && (
|
|
376
|
-
<div
|
|
380
|
+
<div className='my-form-padding'>
|
|
377
381
|
<IconButton
|
|
378
382
|
icon='ph--plus--regular'
|
|
379
|
-
label={t('add
|
|
383
|
+
label={t('add-property-button.label')}
|
|
380
384
|
onClick={handleAdd}
|
|
381
385
|
disabled={viewSnapshot.projection.fields.length >= VIEW_FIELD_LIMIT}
|
|
382
386
|
classNames='w-full'
|
package/src/components/index.ts
CHANGED
|
@@ -5,17 +5,24 @@
|
|
|
5
5
|
import React, { type PropsWithChildren } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Syntax } from '@dxos/react-ui-syntax-highlighter';
|
|
9
|
+
import { composableProps, slottable } from '@dxos/ui-theme';
|
|
10
10
|
|
|
11
|
-
type TestLayoutProps =
|
|
11
|
+
type TestLayoutProps = PropsWithChildren<{ json?: any }>;
|
|
12
12
|
|
|
13
|
-
export const TestLayout = ({
|
|
13
|
+
export const TestLayout = ({ children, json }: TestLayoutProps) => {
|
|
14
14
|
return (
|
|
15
|
-
<div className='
|
|
16
|
-
<TestPanel
|
|
17
|
-
<TestPanel
|
|
18
|
-
<
|
|
15
|
+
<div className='dx-container grid grid-cols-[1fr_1fr] p-4 gap-4'>
|
|
16
|
+
<TestPanel>{children}</TestPanel>
|
|
17
|
+
<TestPanel>
|
|
18
|
+
<Syntax.Root data={json}>
|
|
19
|
+
<Syntax.Content>
|
|
20
|
+
<Syntax.Filter />
|
|
21
|
+
<Syntax.Viewport>
|
|
22
|
+
<Syntax.Code testId='debug' classNames='text-xs' />
|
|
23
|
+
</Syntax.Viewport>
|
|
24
|
+
</Syntax.Content>
|
|
25
|
+
</Syntax.Root>
|
|
19
26
|
</TestPanel>
|
|
20
27
|
</div>
|
|
21
28
|
);
|
|
@@ -23,14 +30,15 @@ export const TestLayout = ({ classNames, children, json }: TestLayoutProps) => {
|
|
|
23
30
|
|
|
24
31
|
type TestPanelProps = ThemedClassName<PropsWithChildren>;
|
|
25
32
|
|
|
26
|
-
const TestPanel = ({
|
|
33
|
+
const TestPanel = slottable<HTMLDivElement, TestPanelProps>(({ children }, forwardedRef) => {
|
|
27
34
|
return (
|
|
28
|
-
<div
|
|
35
|
+
<div {...composableProps({ classNames: 'dx-container bg-modal-surface rounded-md' })} ref={forwardedRef}>
|
|
29
36
|
{children}
|
|
30
37
|
</div>
|
|
31
38
|
);
|
|
32
|
-
};
|
|
39
|
+
});
|
|
33
40
|
|
|
34
41
|
// Symbol for accessing debug objects in tests.
|
|
35
42
|
export const VIEW_EDITOR_DEBUG_SYMBOL = Symbol('viewEditorDebug');
|
|
36
43
|
export const FIELD_EDITOR_DEBUG_SYMBOL = Symbol('fieldEditorDebug');
|
|
44
|
+
export const OBJECT_PROPERTIES_DEBUG_SYMBOL = Symbol('objectPropertiesDebug');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useCallback,
|
|
5
|
+
import { type RefObject, useCallback, useLayoutEffect } from 'react';
|
|
6
6
|
|
|
7
7
|
import { addEventListener } from '@dxos/async';
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ import { type FormHandler } from './useFormHandler';
|
|
|
11
11
|
/**
|
|
12
12
|
* Key handler.
|
|
13
13
|
*/
|
|
14
|
-
export const useKeyHandler = (
|
|
14
|
+
export const useKeyHandler = (elRef: RefObject<HTMLDivElement | null>, form: FormHandler<any>) => {
|
|
15
15
|
const handleKeyDown = useCallback(
|
|
16
16
|
(event: KeyboardEvent) => {
|
|
17
17
|
switch (event.key) {
|
|
@@ -26,12 +26,12 @@ export const useKeyHandler = (formElement: HTMLDivElement | null, form: FormHand
|
|
|
26
26
|
[form.isValid, form.canSave, form.onSave, form.autoSave],
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
useLayoutEffect(() => {
|
|
30
|
+
const el = elRef.current;
|
|
31
|
+
if (!el) {
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, [formElement, handleKeyDown]);
|
|
35
|
+
return addEventListener(el, 'keydown', handleKeyDown);
|
|
36
|
+
}, [elRef, handleKeyDown]);
|
|
37
37
|
};
|
package/src/index.ts
CHANGED
package/src/translations.ts
CHANGED
|
@@ -10,79 +10,82 @@ export const translations = [
|
|
|
10
10
|
{
|
|
11
11
|
'en-US': {
|
|
12
12
|
[translationKey]: {
|
|
13
|
-
'fields
|
|
14
|
-
'hidden
|
|
15
|
-
'empty
|
|
13
|
+
'fields.label': 'Fields',
|
|
14
|
+
'hidden-fields.label': 'Hidden Fields',
|
|
15
|
+
'empty-readonly-ref-field.label': '(none)',
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
'
|
|
19
|
-
'field
|
|
20
|
-
'field
|
|
21
|
-
'field format label': 'Type',
|
|
22
|
-
'field path label': 'Field path',
|
|
23
|
-
'add field': 'Add field',
|
|
24
|
-
'delete field': 'Delete field',
|
|
25
|
-
'field limit reached': 'Maximum number of fields reached',
|
|
26
|
-
'boolean input true value': 'Yes',
|
|
27
|
-
'boolean input false value': 'No',
|
|
28
|
-
'show field label': 'Show field',
|
|
29
|
-
'hide field label': 'Hide field',
|
|
30
|
-
'delete field label': 'Delete field',
|
|
31
|
-
'ref field combobox input placeholder': 'Search…',
|
|
32
|
-
'ref field placeholder_one': 'Select…',
|
|
33
|
-
'ref field placeholder_other': 'Select items…',
|
|
17
|
+
'field-property.label': 'Property',
|
|
18
|
+
'field-property.placeholder': 'Property name',
|
|
19
|
+
'field-format.label': 'Type',
|
|
20
|
+
'field-path.label': 'Field path',
|
|
34
21
|
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
22
|
+
'add-item.button': 'Add item',
|
|
23
|
+
'add-property-button.label': 'Add property',
|
|
24
|
+
'boolean-input-true.value': 'Yes',
|
|
25
|
+
'boolean-input-false.value': 'No',
|
|
26
|
+
'show-field.label': 'Show field',
|
|
27
|
+
'hide-field.label': 'Hide field',
|
|
28
|
+
'delete-field.label': 'Delete field',
|
|
29
|
+
'create-option.label': 'Create',
|
|
30
|
+
'add-tag.label': 'Add tag',
|
|
38
31
|
|
|
39
|
-
'
|
|
40
|
-
'
|
|
32
|
+
'ref-field-combobox-input.placeholder': 'Search…',
|
|
33
|
+
'ref-field.placeholder': 'Select…',
|
|
34
|
+
'ref-field.placeholder_one': 'Select…',
|
|
35
|
+
'ref-field.placeholder_other': 'Select items…',
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'format
|
|
52
|
-
'format
|
|
53
|
-
'format
|
|
54
|
-
'format
|
|
55
|
-
'format
|
|
56
|
-
'format
|
|
57
|
-
'format
|
|
58
|
-
'format
|
|
59
|
-
'format
|
|
60
|
-
'format
|
|
61
|
-
'format
|
|
62
|
-
'format
|
|
63
|
-
'format
|
|
64
|
-
'format
|
|
65
|
-
'format
|
|
66
|
-
'format
|
|
67
|
-
'format
|
|
68
|
-
'format
|
|
69
|
-
'format
|
|
70
|
-
'format
|
|
37
|
+
'example.placeholder': 'Example',
|
|
38
|
+
'latitude.placeholder': 'Latitude (e.g., 40.7128)',
|
|
39
|
+
'longitude.placeholder': 'Longitude (e.g., -74.0060)',
|
|
40
|
+
|
|
41
|
+
'cancel-button.label': 'Cancel',
|
|
42
|
+
'save-button.label': 'Save',
|
|
43
|
+
'create-text.label': 'Create text',
|
|
44
|
+
|
|
45
|
+
// TypeFormat
|
|
46
|
+
'format.boolean.label': 'Boolean',
|
|
47
|
+
'format.currency.label': 'Currency',
|
|
48
|
+
'format.date.label': 'Date',
|
|
49
|
+
'format.date-time.label': 'DateTime',
|
|
50
|
+
'format.did.label': 'DID',
|
|
51
|
+
'format.duration.label': 'Duration',
|
|
52
|
+
'format.dxn.label': 'DXN',
|
|
53
|
+
'format.email.label': 'Email',
|
|
54
|
+
'format.formula.label': 'Formula',
|
|
55
|
+
'format.hostname.label': 'Hostname',
|
|
56
|
+
'format.integer.label': 'Integer',
|
|
57
|
+
'format.json.label': 'JSON',
|
|
58
|
+
'format.lnglat.label': 'Geopoint',
|
|
59
|
+
'format.markdown.label': 'Markdown',
|
|
60
|
+
'format.percent.label': 'Percent',
|
|
61
|
+
'format.ref.label': 'Reference',
|
|
62
|
+
'format.regex.label': 'RegExp',
|
|
63
|
+
'format.string.label': 'String',
|
|
64
|
+
'format.single-select.label': 'Select',
|
|
65
|
+
'format.multi-select.label': 'Multi-select',
|
|
66
|
+
'format.text.label': 'Long text',
|
|
67
|
+
'format.time.label': 'Time',
|
|
68
|
+
'format.timestamp.label': 'Timestamp',
|
|
69
|
+
'format.user.label': 'User',
|
|
70
|
+
'format.number.label': 'Number',
|
|
71
|
+
'format.uri.label': 'URI',
|
|
72
|
+
'format.url.label': 'URL',
|
|
73
|
+
'format.uuid.label': 'UUID',
|
|
71
74
|
|
|
72
75
|
// GeoPointField.
|
|
73
|
-
'latitude
|
|
74
|
-
'longitude
|
|
76
|
+
'latitude.label': 'Latitude',
|
|
77
|
+
'longitude.label': 'Longitude',
|
|
75
78
|
|
|
76
79
|
// SelectOptionsField.
|
|
77
|
-
'select
|
|
78
|
-
'select
|
|
79
|
-
'select
|
|
80
|
-
'select
|
|
81
|
-
'select option add': 'Add option',
|
|
80
|
+
'select-option.label': 'Label',
|
|
81
|
+
'select-option-label.placeholder': 'Option label',
|
|
82
|
+
'select-option-delete.button': 'Delete',
|
|
83
|
+
'select-option-add.button': 'Add option',
|
|
82
84
|
|
|
83
85
|
// System schema message.
|
|
84
|
-
'system
|
|
85
|
-
'system
|
|
86
|
+
'system-schema.title': 'System type',
|
|
87
|
+
'system-schema.description': 'System type: cannot add or remove fields.',
|
|
88
|
+
'remove.button': 'Remove',
|
|
86
89
|
},
|
|
87
90
|
},
|
|
88
91
|
},
|