@dxos/react-ui-form 0.8.4-main.d05673bc65 → 0.8.4-main.d9fc60f731
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 +2008 -840
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +83 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +2008 -840
- 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 +85 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +64 -61
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +71 -17
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +148 -86
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +24 -8
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +26 -13
- 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 +11 -9
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts +19 -0
- package/dist/types/src/components/Form/FormTooltipsContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.d.ts +50 -0
- package/dist/types/src/components/Form/Layout/FormLayout.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts +119 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/annotation.d.ts +30 -0
- package/dist/types/src/components/Form/Layout/annotation.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/index.d.ts +4 -0
- package/dist/types/src/components/Form/Layout/index.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/parser.d.ts +31 -0
- package/dist/types/src/components/Form/Layout/parser.d.ts.map +1 -0
- package/dist/types/src/components/Form/Layout/parser.test.d.ts +2 -0
- package/dist/types/src/components/Form/Layout/parser.test.d.ts.map +1 -0
- 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/DateField.d.ts +4 -0
- package/dist/types/src/components/Form/fields/DateField.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts +1 -1
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/InlineRefField.d.ts +13 -0
- package/dist/types/src/components/Form/fields/InlineRefField.d.ts.map +1 -0
- 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 +85 -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 +12 -6
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectField.d.ts +4 -1
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +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 +64 -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 +1 -1
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/index.d.ts +2 -0
- package/dist/types/src/components/Form/fields/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/index.d.ts +3 -0
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/meta-tags.d.ts +30 -0
- package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -0
- package/dist/types/src/components/Form/meta-tags.test.d.ts +2 -0
- package/dist/types/src/components/Form/meta-tags.test.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +8 -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 +28 -6
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +64 -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 +128 -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/ObjectTree/ObjectTree.d.ts +9 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.d.ts.map +1 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts +87 -0
- package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectTree/index.d.ts +2 -0
- package/dist/types/src/components/ObjectTree/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 +65 -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 +9 -11
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +68 -66
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +13 -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 +64 -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 +18 -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 +48 -41
- package/src/components/FieldEditor/FieldEditor.stories.tsx +4 -4
- package/src/components/FieldEditor/FieldEditor.test.tsx +4 -2
- package/src/components/FieldEditor/FieldEditor.tsx +23 -17
- package/src/components/Form/AUDIT.md +17 -0
- package/src/components/Form/Form.stories.tsx +46 -65
- package/src/components/Form/Form.tsx +176 -60
- package/src/components/Form/FormField.tsx +107 -38
- package/src/components/Form/FormFieldComponent.tsx +77 -20
- package/src/components/Form/FormFieldSet.tsx +128 -78
- package/src/components/Form/FormTooltipsContext.ts +25 -0
- package/src/components/Form/Layout/FormLayout.stories.test.tsx +51 -0
- package/src/components/Form/Layout/FormLayout.stories.tsx +422 -0
- package/src/components/Form/Layout/FormLayout.test.ts +59 -0
- package/src/components/Form/Layout/FormLayout.tsx +251 -0
- package/src/components/Form/Layout/annotation.ts +38 -0
- package/src/components/Form/Layout/index.ts +7 -0
- package/src/components/Form/Layout/parser.test.ts +120 -0
- package/src/components/Form/Layout/parser.ts +167 -0
- package/src/components/Form/fields/ArrayField.tsx +64 -35
- package/src/components/Form/fields/DateField.tsx +101 -0
- package/src/components/Form/fields/GeoPointField.tsx +12 -8
- package/src/components/Form/fields/InlineRefField.tsx +146 -0
- 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 +83 -42
- package/src/components/Form/fields/SelectField.tsx +17 -5
- package/src/components/Form/fields/SelectOptionField.stories.tsx +8 -6
- package/src/components/Form/fields/SelectOptionField.tsx +14 -13
- package/src/components/Form/fields/TextAreaField.tsx +1 -1
- package/src/components/Form/fields/TextField.tsx +2 -2
- package/src/components/Form/fields/TupleField.tsx +3 -2
- package/src/components/Form/fields/index.ts +2 -0
- package/src/components/Form/index.ts +3 -0
- package/src/components/Form/meta-tags.test.ts +47 -0
- package/src/components/Form/meta-tags.ts +33 -0
- package/src/components/ObjectForm/ObjectForm.tsx +109 -0
- package/src/components/ObjectForm/index.ts +5 -0
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +13 -11
- package/src/components/ObjectPicker/ObjectPicker.tsx +93 -66
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +350 -0
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +111 -0
- package/src/components/ObjectProperties/ObjectProperties.tsx +144 -0
- package/src/components/ObjectProperties/index.ts +5 -0
- package/src/components/ObjectTree/ObjectTree.stories.tsx +134 -0
- package/src/components/ObjectTree/ObjectTree.tsx +190 -0
- package/src/components/ObjectTree/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 +32 -181
- 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 +45 -51
- package/src/components/ViewEditor/ViewEditor.test.tsx +11 -9
- package/src/components/ViewEditor/ViewEditor.tsx +45 -55
- package/src/components/index.ts +3 -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 +67 -63
- package/src/util/field-type.ts +60 -0
- package/src/util/index.ts +1 -0
- package/src/util/properties.test.ts +121 -0
- package/src/util/properties.ts +47 -4
- package/src/util/refs.ts +7 -3
- package/src/vite-env.d.ts +10 -0
|
@@ -2,77 +2,167 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useCallback, useMemo } from 'react';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { type Database, Obj, Ref } from '@dxos/echo';
|
|
9
|
+
import { AtomRef } from '@dxos/echo-atom';
|
|
10
|
+
import { createDocAccessor } from '@dxos/echo-db';
|
|
11
|
+
import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { Editor, useBasicMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
13
|
+
import { Text } from '@dxos/schema';
|
|
14
|
+
import { createDataExtensions } from '@dxos/ui-editor';
|
|
15
|
+
|
|
16
|
+
import { translationKey } from '#translations';
|
|
8
17
|
|
|
9
18
|
import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
|
|
10
19
|
|
|
20
|
+
const editorClassNames =
|
|
21
|
+
'transition-colors bg-input-surface focus-within:bg-focus-surface border border-separator rounded-xs p-1 px-2';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Form field that edits a markdown value in a CodeMirror editor.
|
|
25
|
+
* Supports two backings:
|
|
26
|
+
* - `Schema.String` annotated with `Format.TypeFormat.Markdown`: edited as plain text via `getValue`/`onValueChange`.
|
|
27
|
+
* - `Ref<Text>` annotated with `Format.TypeFormat.Markdown`: edited in-place via an Automerge doc accessor.
|
|
28
|
+
* When the ref is empty and a database is available, a create button is shown to allocate a new Text.
|
|
29
|
+
*/
|
|
11
30
|
export const MarkdownField = ({
|
|
12
31
|
type,
|
|
13
32
|
readonly,
|
|
14
33
|
label,
|
|
34
|
+
jsonPath,
|
|
15
35
|
placeholder,
|
|
16
36
|
layout,
|
|
37
|
+
db,
|
|
17
38
|
getStatus,
|
|
18
39
|
getValue,
|
|
19
40
|
onValueChange,
|
|
20
|
-
onBlur,
|
|
21
41
|
}: FormFieldComponentProps) => {
|
|
22
42
|
const { status, error } = getStatus();
|
|
23
|
-
const
|
|
43
|
+
const isRef = Ref.isRefType(type);
|
|
44
|
+
const value = getValue();
|
|
45
|
+
|
|
46
|
+
if ((readonly || layout === 'static') && value == null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const renderEditor = () => {
|
|
51
|
+
if (isRef) {
|
|
52
|
+
const reference = value as Ref.Ref<any> | undefined;
|
|
53
|
+
if (reference) {
|
|
54
|
+
return <RefMarkdownEditor reference={reference} placeholder={placeholder} />;
|
|
55
|
+
}
|
|
24
56
|
|
|
25
|
-
|
|
26
|
-
|
|
57
|
+
if (readonly) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
27
60
|
|
|
28
|
-
|
|
29
|
-
const textarea = textareaRef.current;
|
|
30
|
-
if (textarea) {
|
|
31
|
-
textarea.style.blockSize = 'auto'; // For measurement.
|
|
32
|
-
textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
|
|
61
|
+
return <CreateTextButton db={db} disabled={!db} onCreate={(ref) => onValueChange(type, ref)} />;
|
|
33
62
|
}
|
|
34
|
-
}, []);
|
|
35
63
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
return (
|
|
65
|
+
<StringMarkdownEditor
|
|
66
|
+
value={(value as string | undefined) ?? ''}
|
|
67
|
+
placeholder={placeholder}
|
|
68
|
+
readonly={!!readonly}
|
|
69
|
+
onChange={(next) => onValueChange(type, next)}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
74
|
+
const renderStatic = () => {
|
|
75
|
+
if (isRef) {
|
|
76
|
+
const reference = value as Ref.Ref<any> | undefined;
|
|
77
|
+
return reference ? <RefStaticText reference={reference} /> : null;
|
|
78
|
+
}
|
|
79
|
+
return <p className='whitespace-pre-wrap'>{(value as string | undefined) ?? ''}</p>;
|
|
80
|
+
};
|
|
45
81
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
return (
|
|
83
|
+
<Input.Root validationValence={status}>
|
|
84
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
|
|
85
|
+
{layout === 'static' ? renderStatic() : renderEditor()}
|
|
86
|
+
{layout === 'full' && <Input.Validation>{error}</Input.Validation>}
|
|
87
|
+
</Input.Root>
|
|
49
88
|
);
|
|
89
|
+
};
|
|
50
90
|
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
/** Read-only static rendering for a `Ref<Text>` value: resolve the ref and print its content. */
|
|
92
|
+
const RefStaticText = ({ reference }: { reference: Ref.Ref<any> }) => {
|
|
93
|
+
const text = useAtomValue(useMemo(() => AtomRef.make(reference), [reference]));
|
|
94
|
+
if (!text?.content) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return <p className='whitespace-pre-wrap'>{text.content}</p>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
type RefMarkdownEditorProps = {
|
|
101
|
+
reference: Ref.Ref<any>;
|
|
102
|
+
placeholder?: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const RefMarkdownEditor = ({ reference, placeholder }: RefMarkdownEditorProps) => {
|
|
106
|
+
const text = useAtomValue(useMemo(() => AtomRef.make(reference), [reference]));
|
|
107
|
+
const dataExtensions = useMemo(
|
|
108
|
+
() => (text ? [createDataExtensions({ id: reference.uri, text: createDocAccessor(text, ['content']) })] : []),
|
|
109
|
+
[text, reference],
|
|
110
|
+
);
|
|
111
|
+
const extensions = useBasicMarkdownExtensions({ placeholder, extensions: dataExtensions });
|
|
112
|
+
if (!text) {
|
|
53
113
|
return null;
|
|
54
114
|
}
|
|
55
115
|
|
|
56
|
-
// TODO(burdon): Use Markdown Editor.
|
|
57
116
|
return (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
117
|
+
<Editor.Root>
|
|
118
|
+
<Editor.View classNames={editorClassNames} extensions={extensions} />
|
|
119
|
+
</Editor.Root>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type StringMarkdownEditorProps = {
|
|
124
|
+
value: string;
|
|
125
|
+
placeholder?: string;
|
|
126
|
+
readonly?: boolean;
|
|
127
|
+
onChange: (value: string) => void;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const StringMarkdownEditor = ({ value, placeholder, readonly, onChange }: StringMarkdownEditorProps) => {
|
|
131
|
+
const extensions = useBasicMarkdownExtensions({ placeholder });
|
|
132
|
+
const handleChange = useCallback((next: string) => onChange(next), [onChange]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<Editor.Root>
|
|
136
|
+
<Editor.View
|
|
137
|
+
classNames={editorClassNames}
|
|
138
|
+
extensions={extensions}
|
|
139
|
+
value={value}
|
|
140
|
+
onChange={readonly ? undefined : handleChange}
|
|
141
|
+
/>
|
|
142
|
+
</Editor.Root>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
type CreateTextButtonProps = {
|
|
147
|
+
db?: Database.Database;
|
|
148
|
+
disabled?: boolean;
|
|
149
|
+
onCreate: (ref: Ref.Ref<Text.Text>) => void;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const CreateTextButton = ({ db, disabled, onCreate }: CreateTextButtonProps) => {
|
|
153
|
+
const { t } = useTranslation(translationKey);
|
|
154
|
+
const handleClick = useCallback(() => {
|
|
155
|
+
if (!db) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const text = db.add(Obj.make(Text.Text, { content: '' }));
|
|
159
|
+
onCreate(Ref.make(text));
|
|
160
|
+
}, [db, onCreate]);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<Button onClick={handleClick} disabled={disabled}>
|
|
164
|
+
<Icon icon='ph--plus--regular' size={4} />
|
|
165
|
+
<span>{t('create-text.label')}</span>
|
|
166
|
+
</Button>
|
|
77
167
|
);
|
|
78
168
|
};
|
|
@@ -2,23 +2,22 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '
|
|
6
|
-
|
|
7
|
-
import React, { useCallback, useMemo } from 'react';
|
|
5
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
8
6
|
|
|
7
|
+
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
9
8
|
import { type Database, Entity, Filter, Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { useQuery, useType as defaultUseType } from '@dxos/echo-react';
|
|
10
|
+
import { ANY_OBJECT_TYPENAME, ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/internal';
|
|
12
11
|
import { findAnnotation } from '@dxos/effect';
|
|
13
|
-
import {
|
|
12
|
+
import { URI } from '@dxos/keys';
|
|
14
13
|
import { DxAnchor } from '@dxos/lit-ui/react';
|
|
15
14
|
import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
|
|
16
15
|
import { ParentLabelAnnotationId } from '@dxos/schema';
|
|
17
|
-
import { mx } from '@dxos/ui-theme';
|
|
18
16
|
|
|
19
|
-
import { translationKey } from '
|
|
17
|
+
import { translationKey } from '#translations';
|
|
18
|
+
|
|
20
19
|
import { ObjectPicker, type ObjectPickerContentProps, type RefOption } from '../../ObjectPicker';
|
|
21
|
-
import { omitId } from '../Form';
|
|
20
|
+
import { omitHiddenFormFields, omitId } from '../Form';
|
|
22
21
|
import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
|
|
23
22
|
|
|
24
23
|
// TODO(burdon): Factor out.
|
|
@@ -28,30 +27,40 @@ const isRefSnapshot = (val: any): val is { '/': string } => {
|
|
|
28
27
|
|
|
29
28
|
const defaultGetOptions: NonNullable<RefFieldProps['getOptions']> = (results, { parentLabel } = {}) =>
|
|
30
29
|
results.map((result) => {
|
|
31
|
-
const id = Entity.
|
|
30
|
+
const id = Entity.getURI(result);
|
|
32
31
|
const parent = parentLabel ? Obj.getParent(result as Obj.Unknown) : undefined;
|
|
33
32
|
const label = parent ? Entity.getLabel(parent) : Entity.getLabel(result);
|
|
34
33
|
return { id, label: label ?? id };
|
|
35
34
|
});
|
|
36
35
|
|
|
37
|
-
const
|
|
36
|
+
const defaultUseResults: NonNullable<RefFieldProps['useResults']> = (db, typename) =>
|
|
38
37
|
useQuery(
|
|
39
38
|
db,
|
|
40
39
|
typename
|
|
41
40
|
? // For Ref.Ref(Obj.Unknown) we want to show all objects.
|
|
42
|
-
typename ===
|
|
41
|
+
typename === ANY_OBJECT_TYPENAME
|
|
43
42
|
? Filter.everything()
|
|
44
43
|
: Filter.typename(typename)
|
|
45
44
|
: Filter.nothing(),
|
|
46
45
|
);
|
|
47
46
|
|
|
48
47
|
export type RefFieldProps = FormFieldComponentProps &
|
|
49
|
-
Pick<
|
|
48
|
+
Pick<
|
|
49
|
+
ObjectPickerContentProps,
|
|
50
|
+
'createOptionLabel' | 'createOptionIcon' | 'createInitialValuePath' | 'createFieldMap'
|
|
51
|
+
> & {
|
|
50
52
|
db?: Database.Database;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
// TODO(burdon): Replace hooks with callbacks.
|
|
54
|
+
useType?: (db?: Database.Database, typename?: string) => Type.AnyEntity;
|
|
55
|
+
useResults?: (db?: Database.Database, typename?: string) => Entity.Any[];
|
|
53
56
|
getOptions?: (objects: Entity.Any[], options?: { parentLabel?: boolean }) => RefOption[];
|
|
54
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Persist a newly-created object. Called after the user fills out the
|
|
59
|
+
* inline create form and clicks Save. Should add the object to the
|
|
60
|
+
* database and return it (sync or async). The returned object is then
|
|
61
|
+
* wired into this slot's form value as a Ref.
|
|
62
|
+
*/
|
|
63
|
+
onCreate?: (schema: Type.AnyEntity, values: any) => Obj.Unknown | Promise<Obj.Unknown> | undefined | void;
|
|
55
64
|
};
|
|
56
65
|
|
|
57
66
|
export const RefField = (props: RefFieldProps) => {
|
|
@@ -59,6 +68,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
59
68
|
type,
|
|
60
69
|
readonly,
|
|
61
70
|
label,
|
|
71
|
+
jsonPath,
|
|
62
72
|
placeholder,
|
|
63
73
|
layout,
|
|
64
74
|
getStatus,
|
|
@@ -66,9 +76,10 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
66
76
|
createOptionLabel,
|
|
67
77
|
createOptionIcon,
|
|
68
78
|
createInitialValuePath,
|
|
79
|
+
createFieldMap,
|
|
69
80
|
db,
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
useType = defaultUseType,
|
|
82
|
+
useResults = defaultUseResults,
|
|
72
83
|
getOptions = defaultGetOptions,
|
|
73
84
|
onCreate,
|
|
74
85
|
onValueChange,
|
|
@@ -89,42 +100,66 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
89
100
|
|
|
90
101
|
const handleGetValue = useCallback(() => {
|
|
91
102
|
const formValue = getValue();
|
|
103
|
+
// Match form-value Refs against options by the bare entity id, not by full
|
|
104
|
+
// URI string. A just-created object's Ref (`Ref.make`) carries the LOCAL
|
|
105
|
+
// EID `echo:/<id>`, while options derived from `Entity.getURI` carry the
|
|
106
|
+
// qualified EID `echo://<spaceId>/<id>`; encoded snapshots use the DXN
|
|
107
|
+
// form `dxn:echo:<space|@>:<id>`. The entity id (a ULID) contains neither
|
|
108
|
+
// `:` nor `/`, so it is always the final delimiter-separated segment —
|
|
109
|
+
// splitting on both reconciles all three forms. Comparing full strings (or
|
|
110
|
+
// splitting on `:` alone, which leaves `/`-delimited EIDs intact) makes the
|
|
111
|
+
// just-created Ref's lookup fail and the slot render empty even though the
|
|
112
|
+
// underlying form value IS set.
|
|
113
|
+
const dxnToEntityId = (dxn: string): string => dxn.split(/[:/]/).filter(Boolean).pop() ?? dxn;
|
|
92
114
|
|
|
93
115
|
const unknownToRefOption = (value: unknown) => {
|
|
94
116
|
const isRef = Ref.isRef(value);
|
|
95
117
|
if (isRef || isRefSnapshot(value)) {
|
|
96
|
-
const dxnString = isRef ? value.
|
|
97
|
-
const
|
|
118
|
+
const dxnString = isRef ? value.uri : value['/'];
|
|
119
|
+
const objectId = dxnToEntityId(dxnString);
|
|
120
|
+
const matchingOption = options.find((option) => dxnToEntityId(option.id) === objectId);
|
|
98
121
|
if (matchingOption) {
|
|
99
122
|
return matchingOption;
|
|
100
123
|
}
|
|
101
124
|
}
|
|
125
|
+
|
|
102
126
|
return undefined;
|
|
103
127
|
};
|
|
104
128
|
|
|
105
129
|
return unknownToRefOption(formValue);
|
|
106
130
|
}, [options, getValue]);
|
|
107
131
|
|
|
108
|
-
const handleUpdate = useCallback(
|
|
109
|
-
(id: string | undefined) => {
|
|
110
|
-
const item = options.find((option) => option.id === id);
|
|
111
|
-
const ref = item ? Ref.fromDXN(DXN.parse(item.id)) : undefined;
|
|
112
|
-
onValueChange(type, ref);
|
|
113
|
-
},
|
|
114
|
-
[options, type, onValueChange],
|
|
115
|
-
);
|
|
116
|
-
|
|
117
132
|
const item = handleGetValue();
|
|
118
133
|
const selectedIds = useMemo(() => (item ? [item.id] : []), [item]);
|
|
119
|
-
const createSchema =
|
|
134
|
+
const createSchema = useType(db, typename);
|
|
135
|
+
|
|
136
|
+
// Lift the popover open state so we can dismiss it after a successful create.
|
|
137
|
+
const [open, setOpen] = useState(false);
|
|
120
138
|
|
|
121
139
|
const handleCreate = useCallback(
|
|
122
|
-
(values: any) => {
|
|
123
|
-
if (createSchema
|
|
124
|
-
|
|
140
|
+
async (values: any) => {
|
|
141
|
+
if (!createSchema || !onCreate) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const newObject = await onCreate(createSchema, values);
|
|
145
|
+
if (newObject) {
|
|
146
|
+
// Wire the newly-created object into this slot's form value. ArrayField
|
|
147
|
+
// owns the array; this RefField represents a single slot, so writing a
|
|
148
|
+
// Ref via `onValueChange` populates that slot.
|
|
149
|
+
onValueChange(type, Ref.make(newObject));
|
|
125
150
|
}
|
|
151
|
+
setOpen(false);
|
|
126
152
|
},
|
|
127
|
-
[createSchema, onCreate],
|
|
153
|
+
[createSchema, onCreate, type, onValueChange],
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const handleUpdate = useCallback(
|
|
157
|
+
(id: string | undefined) => {
|
|
158
|
+
const item = options.find((option) => option.id === id);
|
|
159
|
+
const ref = item ? Ref.fromURI(URI.make(item.id)) : undefined;
|
|
160
|
+
onValueChange(type, ref);
|
|
161
|
+
},
|
|
162
|
+
[options, type, onValueChange],
|
|
128
163
|
);
|
|
129
164
|
|
|
130
165
|
const handleSelect = useCallback(
|
|
@@ -144,29 +179,31 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
144
179
|
|
|
145
180
|
return (
|
|
146
181
|
<Input.Root validationValence={status}>
|
|
147
|
-
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} />}
|
|
182
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
|
|
148
183
|
<div>
|
|
149
184
|
{readonly ? (
|
|
150
185
|
!item ? (
|
|
151
|
-
<p className=
|
|
186
|
+
<p className='text-description mb-2'>{t('empty-readonly-ref-field.label')}</p>
|
|
152
187
|
) : (
|
|
153
188
|
<DxAnchor key={item.id} dxn={item.id} rootclassname='me-1'>
|
|
154
189
|
{item.label}
|
|
155
190
|
</DxAnchor>
|
|
156
191
|
)
|
|
157
192
|
) : (
|
|
158
|
-
<ObjectPicker.Root>
|
|
193
|
+
<ObjectPicker.Root open={open} onOpenChange={setOpen}>
|
|
159
194
|
<ObjectPicker.Trigger asChild classNames='p-0'>
|
|
160
195
|
{item ? (
|
|
161
|
-
<div className='flex gap-
|
|
196
|
+
<div className='flex gap-form-gap w-full'>
|
|
162
197
|
<Input.Root key={item.id}>
|
|
163
198
|
<Input.TextInput value={item.label} readOnly classNames='w-full' />
|
|
164
199
|
</Input.Root>
|
|
165
200
|
</div>
|
|
166
201
|
) : (
|
|
167
|
-
<Button classNames='w-full text-start gap-
|
|
168
|
-
<div
|
|
169
|
-
<span className='
|
|
202
|
+
<Button classNames='w-full text-start gap-form-gap'>
|
|
203
|
+
<div className='grow overflow-hidden'>
|
|
204
|
+
<span className='truncate text-description'>
|
|
205
|
+
{placeholder || label || t('ref-field.placeholder')}
|
|
206
|
+
</span>
|
|
170
207
|
</div>
|
|
171
208
|
<Icon size={3} icon='ph--caret-down--bold' />
|
|
172
209
|
</Button>
|
|
@@ -177,10 +214,14 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
177
214
|
classNames='dx-card-popover-width'
|
|
178
215
|
options={options}
|
|
179
216
|
selectedIds={selectedIds}
|
|
180
|
-
|
|
217
|
+
// Strip hidden (`FormInputAnnotation.set(false)`) fields so the
|
|
218
|
+
// form's validator doesn't reject required-but-hidden fields
|
|
219
|
+
// such as backing-object refs supplied by a `FactoryAnnotation`.
|
|
220
|
+
createSchema={createSchema && omitHiddenFormFields(omitId(Type.getSchema(createSchema)))}
|
|
181
221
|
createOptionLabel={createOptionLabel}
|
|
182
222
|
createOptionIcon={createOptionIcon}
|
|
183
223
|
createInitialValuePath={createInitialValuePath}
|
|
224
|
+
createFieldMap={createFieldMap}
|
|
184
225
|
onCreate={handleCreate}
|
|
185
226
|
onSelect={handleSelect}
|
|
186
227
|
/>
|
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Input, Select, type SelectRootProps } from '@dxos/react-ui';
|
|
7
|
+
import { Icon, Input, Select, type SelectRootProps } from '@dxos/react-ui';
|
|
8
|
+
import { getStyles } from '@dxos/ui-theme';
|
|
8
9
|
|
|
9
10
|
import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
|
|
10
11
|
|
|
11
12
|
export type SelectFieldOptions = FormFieldComponentProps & {
|
|
12
|
-
options?: Array<{ value: string | number; label?: string }>;
|
|
13
|
+
options?: Array<{ value: string | number; label?: string; secondaryLabel?: string; icon?: string; iconHue?: string }>;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export const SelectField = ({
|
|
16
17
|
type,
|
|
17
18
|
readonly,
|
|
18
19
|
label,
|
|
20
|
+
jsonPath,
|
|
19
21
|
layout,
|
|
20
22
|
placeholder,
|
|
21
23
|
options,
|
|
@@ -37,7 +39,7 @@ export const SelectField = ({
|
|
|
37
39
|
|
|
38
40
|
return (
|
|
39
41
|
<Input.Root validationValence={status}>
|
|
40
|
-
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} />}
|
|
42
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
|
|
41
43
|
{layout === 'static' ? (
|
|
42
44
|
<p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
|
|
43
45
|
) : (
|
|
@@ -47,10 +49,14 @@ export const SelectField = ({
|
|
|
47
49
|
<Select.Portal>
|
|
48
50
|
<Select.Content>
|
|
49
51
|
<Select.Viewport>
|
|
50
|
-
{options?.map(({ value, label }) => (
|
|
52
|
+
{options?.map(({ value, label, secondaryLabel, icon, iconHue }) => (
|
|
51
53
|
// NOTE: Numeric values are converted to and from strings.
|
|
52
54
|
<Select.Option key={String(value)} value={String(value)}>
|
|
53
|
-
|
|
55
|
+
<span className='flex items-center flex-row gap-2'>
|
|
56
|
+
{icon && <Icon icon={icon} classNames={getIconHueStyles(iconHue)} />}
|
|
57
|
+
{label ?? String(value)}
|
|
58
|
+
{secondaryLabel && <span className='text-subdued text-xs'>{secondaryLabel}</span>}
|
|
59
|
+
</span>
|
|
54
60
|
</Select.Option>
|
|
55
61
|
))}
|
|
56
62
|
</Select.Viewport>
|
|
@@ -63,3 +69,9 @@ export const SelectField = ({
|
|
|
63
69
|
</Input.Root>
|
|
64
70
|
);
|
|
65
71
|
};
|
|
72
|
+
|
|
73
|
+
const getIconHueStyles = (iconHue?: string): string | undefined => {
|
|
74
|
+
const styles = iconHue ? getStyles(iconHue) : undefined;
|
|
75
|
+
|
|
76
|
+
return styles?.fg;
|
|
77
|
+
};
|
|
@@ -9,10 +9,10 @@ import React, { useState } from 'react';
|
|
|
9
9
|
import { SelectOption } from '@dxos/echo/internal';
|
|
10
10
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
|
|
12
|
-
import { translations } from '
|
|
12
|
+
import { translations } from '#translations';
|
|
13
|
+
|
|
13
14
|
import { TestLayout } from '../../testing';
|
|
14
15
|
import { Form } from '../Form';
|
|
15
|
-
|
|
16
16
|
import { SelectOptionField } from './SelectOptionField';
|
|
17
17
|
|
|
18
18
|
const schema = Schema.Struct({
|
|
@@ -37,10 +37,12 @@ const DefaultStory = () => {
|
|
|
37
37
|
fieldMap={{ options: SelectOptionField }}
|
|
38
38
|
onSave={(values) => setValues(values)}
|
|
39
39
|
>
|
|
40
|
-
<Form.
|
|
41
|
-
<Form.
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
<Form.Viewport>
|
|
41
|
+
<Form.Content>
|
|
42
|
+
<Form.FieldSet />
|
|
43
|
+
<Form.Actions />
|
|
44
|
+
</Form.Content>
|
|
45
|
+
</Form.Viewport>
|
|
44
46
|
</Form.Root>
|
|
45
47
|
</TestLayout>
|
|
46
48
|
);
|
|
@@ -9,15 +9,17 @@ import { PublicKey } from '@dxos/keys';
|
|
|
9
9
|
import { type ChromaticPalette, IconButton, Input, Tag, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
import { List } from '@dxos/react-ui-list';
|
|
11
11
|
import { HuePicker } from '@dxos/react-ui-pickers';
|
|
12
|
-
import { hues, osTranslations
|
|
12
|
+
import { hues, osTranslations } from '@dxos/ui-theme';
|
|
13
|
+
|
|
14
|
+
import { translationKey } from '#translations';
|
|
13
15
|
|
|
14
|
-
import { translationKey } from '../../../translations';
|
|
15
16
|
import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
|
|
16
17
|
|
|
17
18
|
export const SelectOptionField = ({
|
|
18
19
|
type,
|
|
19
20
|
readonly,
|
|
20
21
|
label,
|
|
22
|
+
jsonPath,
|
|
21
23
|
getStatus,
|
|
22
24
|
getValue,
|
|
23
25
|
onValueChange,
|
|
@@ -118,8 +120,8 @@ export const SelectOptionField = ({
|
|
|
118
120
|
|
|
119
121
|
return (
|
|
120
122
|
<Input.Root validationValence={status}>
|
|
121
|
-
<FormFieldLabel error={error} readonly={readonly} label={label} />
|
|
122
|
-
<div
|
|
123
|
+
<FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />
|
|
124
|
+
<div>
|
|
123
125
|
{options && (
|
|
124
126
|
<List.Root
|
|
125
127
|
items={options}
|
|
@@ -134,7 +136,7 @@ export const SelectOptionField = ({
|
|
|
134
136
|
<List.Item
|
|
135
137
|
role='button'
|
|
136
138
|
item={item}
|
|
137
|
-
classNames=
|
|
139
|
+
classNames='flex flex-col cursor-pointer rounded-xs dx-hover'
|
|
138
140
|
aria-expanded={selected === item.id}
|
|
139
141
|
>
|
|
140
142
|
<div className='flex items-center'>
|
|
@@ -146,19 +148,18 @@ export const SelectOptionField = ({
|
|
|
146
148
|
<IconButton
|
|
147
149
|
iconOnly
|
|
148
150
|
variant='ghost'
|
|
149
|
-
label={t(selected === item.id ? 'collapse
|
|
151
|
+
label={t(selected === item.id ? 'collapse.label' : 'expand.label', { ns: osTranslations })}
|
|
150
152
|
icon={selected === item.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
|
|
151
153
|
onClick={() => handleClick(item.id)}
|
|
152
154
|
/>
|
|
153
155
|
</div>
|
|
154
156
|
{selected === item.id && (
|
|
155
|
-
<div className='flex flex-col p-form-padding gap-
|
|
156
|
-
{
|
|
157
|
-
<
|
|
158
|
-
<div className='grid grid-cols-[1fr_min-content_min-content] gap-1'>
|
|
157
|
+
<div className='flex flex-col p-form-padding gap-form-gap dx-density-md'>
|
|
158
|
+
<Input.Label classNames='text-sm'>{t('select-option.label')}</Input.Label>
|
|
159
|
+
<div className='grid grid-cols-[1fr_min-content_min-content] gap-form-gap'>
|
|
159
160
|
<Input.TextInput
|
|
160
161
|
disabled={!!readonly}
|
|
161
|
-
placeholder={t('select
|
|
162
|
+
placeholder={t('select-option-label.placeholder')}
|
|
162
163
|
ref={selected === item.id ? inputRef : undefined}
|
|
163
164
|
value={item.title}
|
|
164
165
|
onChange={handleTitleChange(item.id)}
|
|
@@ -170,7 +171,7 @@ export const SelectOptionField = ({
|
|
|
170
171
|
disabled={!!readonly}
|
|
171
172
|
icon='ph--trash--fill'
|
|
172
173
|
iconOnly
|
|
173
|
-
label={t('select
|
|
174
|
+
label={t('select-option-delete.button')}
|
|
174
175
|
onClick={() => handleDelete(item.id)}
|
|
175
176
|
/>
|
|
176
177
|
</div>
|
|
@@ -182,7 +183,7 @@ export const SelectOptionField = ({
|
|
|
182
183
|
<IconButton
|
|
183
184
|
variant='ghost'
|
|
184
185
|
icon='ph--plus--regular'
|
|
185
|
-
label={t('select
|
|
186
|
+
label={t('select-option-add.button')}
|
|
186
187
|
onClick={handleAdd}
|
|
187
188
|
disabled={!!readonly}
|
|
188
189
|
classNames='w-full'
|
|
@@ -12,8 +12,8 @@ export const TextField = ({
|
|
|
12
12
|
type,
|
|
13
13
|
readonly,
|
|
14
14
|
placeholder,
|
|
15
|
-
onValueChange,
|
|
16
15
|
onBlur,
|
|
16
|
+
onValueChange,
|
|
17
17
|
...props
|
|
18
18
|
}: FormFieldComponentProps<string>) => {
|
|
19
19
|
const handleChange = useCallback<NonNullable<TextInputProps['onChange']>>(
|
|
@@ -29,8 +29,8 @@ export const TextField = ({
|
|
|
29
29
|
disabled={!!readonly}
|
|
30
30
|
placeholder={placeholder}
|
|
31
31
|
value={value}
|
|
32
|
-
onChange={handleChange}
|
|
33
32
|
onBlur={onBlur}
|
|
33
|
+
onChange={handleChange}
|
|
34
34
|
/>
|
|
35
35
|
)}
|
|
36
36
|
</FormFieldWrapper>
|