@dxos/react-ui-form 0.8.4-main.d05539e30a → 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/dist/lib/browser/index.mjs +1335 -561
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +2 -1
- package/dist/lib/browser/translations.mjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +1335 -561
- 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 +2 -1
- package/dist/lib/node-esm/translations.mjs.map +2 -2
- 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 +2 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +35 -9
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +76 -16
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +14 -4
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +22 -10
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +8 -2
- 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.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 +1 -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 +2 -1
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +1 -1
- 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 +2 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.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 +3 -4
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +2 -2
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +2 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +2 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/types/src/components/Settings/Settings.stories.d.ts +2 -1
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +7 -8
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +4 -4
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +9 -0
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +11 -0
- package/dist/types/src/util/properties.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +37 -32
- package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
- package/src/components/FieldEditor/FieldEditor.test.tsx +4 -1
- package/src/components/FieldEditor/FieldEditor.tsx +19 -14
- package/src/components/Form/Form.stories.tsx +21 -27
- package/src/components/Form/Form.tsx +106 -19
- package/src/components/Form/FormField.tsx +85 -28
- package/src/components/Form/FormFieldComponent.tsx +75 -20
- package/src/components/Form/FormFieldSet.tsx +32 -3
- 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 +61 -38
- package/src/components/Form/fields/DateField.tsx +101 -0
- package/src/components/Form/fields/GeoPointField.tsx +4 -1
- package/src/components/Form/fields/InlineRefField.tsx +146 -0
- package/src/components/Form/fields/MarkdownField.tsx +3 -3
- package/src/components/Form/fields/RefField.tsx +29 -23
- package/src/components/Form/fields/SelectField.tsx +17 -5
- package/src/components/Form/fields/SelectOptionField.tsx +4 -3
- package/src/components/Form/fields/TextAreaField.tsx +1 -1
- package/src/components/Form/fields/TupleField.tsx +2 -1
- 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 +21 -26
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +2 -2
- package/src/components/ObjectPicker/ObjectPicker.tsx +2 -2
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +14 -26
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +20 -10
- package/src/components/ObjectProperties/ObjectProperties.tsx +26 -30
- 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/Settings.tsx +2 -1
- package/src/components/Settings/SettingsItem.tsx +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +35 -40
- package/src/components/ViewEditor/ViewEditor.test.tsx +11 -8
- package/src/components/ViewEditor/ViewEditor.tsx +29 -32
- package/src/components/index.ts +1 -0
- package/src/components/testing.tsx +4 -4
- package/src/translations.ts +2 -1
- package/src/util/properties.test.ts +67 -9
- package/src/util/properties.ts +36 -4
- package/src/vite-env.d.ts +10 -0
|
@@ -6,14 +6,22 @@ import * as Option from 'effect/Option';
|
|
|
6
6
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
import React, { useCallback } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { Ref } from '@dxos/echo';
|
|
10
|
+
import {
|
|
11
|
+
createJsonPath,
|
|
12
|
+
findNode,
|
|
13
|
+
getArrayElementType,
|
|
14
|
+
getDiscriminatedType,
|
|
15
|
+
isDiscriminatedUnion,
|
|
16
|
+
isNestedType,
|
|
17
|
+
} from '@dxos/effect';
|
|
18
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
11
19
|
|
|
12
20
|
import { translationKey } from '#translations';
|
|
13
21
|
|
|
14
22
|
import { getFormProperties } from '../../../util';
|
|
15
23
|
import { useFormValues } from '../Form';
|
|
16
|
-
import { FormField, type FormFieldProps } from '../FormField';
|
|
24
|
+
import { FormField, IconBlock, type FormFieldProps } from '../FormField';
|
|
17
25
|
import { FormFieldLabel, type FormFieldStateProps } from '../FormFieldComponent';
|
|
18
26
|
|
|
19
27
|
export type ArrayFieldProps = {
|
|
@@ -55,7 +63,13 @@ export const ArrayField = ({
|
|
|
55
63
|
const handleAdd = useCallback(() => {
|
|
56
64
|
const defaultValue =
|
|
57
65
|
isNestedType(type) && elementType ? getDefaultObjectValue(elementType) : getDefaultValue(elementType);
|
|
58
|
-
values
|
|
66
|
+
// `values` is `undefined` on first render for arrays whose parent path
|
|
67
|
+
// hasn't been materialised in the form values yet (e.g. `package.repos`
|
|
68
|
+
// when the form starts with no `package`). `useFormValues`'s default-
|
|
69
|
+
// value effect would eventually backfill it to `[]`, but until then the
|
|
70
|
+
// old `values && ...` guard silently swallowed the click. Treat a
|
|
71
|
+
// missing array as empty so Add always produces `[defaultValue]`.
|
|
72
|
+
onValueChange(type, [...(values ?? []), defaultValue]);
|
|
59
73
|
}, [onValueChange, type, elementType, values]);
|
|
60
74
|
|
|
61
75
|
const handleDelete = useCallback(
|
|
@@ -72,53 +86,62 @@ export const ArrayField = ({
|
|
|
72
86
|
return null;
|
|
73
87
|
}
|
|
74
88
|
|
|
89
|
+
const renderItemAsObject = elementType && isNestedType(elementType) && !Ref.isRefType(elementType);
|
|
90
|
+
|
|
75
91
|
return (
|
|
76
92
|
<>
|
|
77
93
|
{(layout !== 'static' || (values && values.length > 0)) && (
|
|
78
|
-
<
|
|
94
|
+
<div className='flex items-center gap-2'>
|
|
95
|
+
<div className='flex-1 min-w-0'>
|
|
96
|
+
<FormFieldLabel readonly={readonly} label={label} path={createJsonPath(path ?? [])} asChild />
|
|
97
|
+
</div>
|
|
98
|
+
{!readonly && layout !== 'static' && (
|
|
99
|
+
<IconBlock inline icon='ph--plus--regular' label={t('add-item.button')} onClick={handleAdd} />
|
|
100
|
+
)}
|
|
101
|
+
</div>
|
|
79
102
|
)}
|
|
80
103
|
|
|
81
|
-
<div className='flex flex-col
|
|
104
|
+
<div className='flex flex-col'>
|
|
82
105
|
{values?.map((_, index) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
const isLast = index === values.length - 1;
|
|
107
|
+
|
|
108
|
+
// Object items: each row contains a recursively-rendered FormField
|
|
109
|
+
// (multiple sub-rows for the object's fields) wrapped in a border,
|
|
110
|
+
// with the delete button bottom-aligned next to it.
|
|
111
|
+
// Scalar items: a single-row inline FormField with a center-aligned
|
|
112
|
+
// delete button. Refs and primitive arrays use this layout.
|
|
113
|
+
const fieldField = (
|
|
114
|
+
<FormField
|
|
115
|
+
{...props}
|
|
116
|
+
autoFocus={isLast}
|
|
117
|
+
type={elementType}
|
|
118
|
+
// Suppress the per-item header for object items only — the recursive
|
|
119
|
+
// form already renders labels for each sub-field. Scalar items
|
|
120
|
+
// (refs, primitives) keep the parent name so inline-layout
|
|
121
|
+
// children (e.g. RefField) have a real label to use as a
|
|
122
|
+
// fallback placeholder.
|
|
123
|
+
{...(renderItemAsObject && { name: null })}
|
|
124
|
+
path={[...(path ?? []), index]}
|
|
125
|
+
readonly={readonly || layout === 'static'}
|
|
126
|
+
layout={renderItemAsObject ? (layout === 'static' ? 'static' : undefined) : 'inline'}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
93
129
|
|
|
130
|
+
return (
|
|
131
|
+
<div key={index} className='grid grid-cols-[1fr_min-content] items-center mb-1 last:mb-form-gap'>
|
|
132
|
+
{fieldField}
|
|
94
133
|
{!readonly && layout !== 'static' && (
|
|
95
|
-
<
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
label={t('remove.button')}
|
|
102
|
-
onClick={() => handleDelete(index)}
|
|
103
|
-
classNames='self-center'
|
|
104
|
-
/>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
134
|
+
<IconBlock
|
|
135
|
+
inline={!renderItemAsObject}
|
|
136
|
+
icon='ph--x--regular'
|
|
137
|
+
label={t('remove-item.button')}
|
|
138
|
+
onClick={() => handleDelete(index)}
|
|
139
|
+
/>
|
|
107
140
|
)}
|
|
108
141
|
</div>
|
|
109
142
|
);
|
|
110
143
|
})}
|
|
111
144
|
</div>
|
|
112
|
-
|
|
113
|
-
{/* TODO(burdon): Get label from schema. */}
|
|
114
|
-
{!readonly && layout !== 'static' && (
|
|
115
|
-
<IconButton
|
|
116
|
-
classNames='flex w-full'
|
|
117
|
-
icon='ph--plus--regular'
|
|
118
|
-
label={t('add-item.button')}
|
|
119
|
-
onClick={handleAdd}
|
|
120
|
-
/>
|
|
121
|
-
)}
|
|
122
145
|
</>
|
|
123
146
|
);
|
|
124
147
|
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { format as formatDate } from 'date-fns';
|
|
6
|
+
import React, { useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Format } from '@dxos/echo';
|
|
9
|
+
import { Input } from '@dxos/react-ui';
|
|
10
|
+
|
|
11
|
+
import { type FormFieldComponentProps, FormFieldWrapper } from '../FormFieldComponent';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stored value shapes:
|
|
15
|
+
* - `Format.DateTime` -> ISO 8601 (`2018-11-13T20:20:39.000Z`).
|
|
16
|
+
* - `Format.Date` -> `YYYY-MM-DD`.
|
|
17
|
+
* - `Format.Time` -> `HH:mm:ss`.
|
|
18
|
+
*
|
|
19
|
+
* Segmented input value shapes (react-aria-components-backed):
|
|
20
|
+
* - `Input.DateTime` -> `YYYY-MM-DDTHH:mm` in local time.
|
|
21
|
+
* - `Input.Date` -> `YYYY-MM-DD`.
|
|
22
|
+
* - `Input.Time` -> `HH:mm`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** ISO 8601 → `YYYY-MM-DDTHH:mm` in the user's local timezone. */
|
|
26
|
+
const isoToLocalDateTime = (value: string | undefined): string => {
|
|
27
|
+
if (!value) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
const date = new Date(value);
|
|
31
|
+
if (Number.isNaN(date.getTime())) {
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
return formatDate(date, "yyyy-MM-dd'T'HH:mm");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** `YYYY-MM-DDTHH:mm` (local) → ISO 8601 with timezone. */
|
|
38
|
+
const localDateTimeToIso = (value: string): string | undefined => {
|
|
39
|
+
if (!value) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// Treat the input as local time. `new Date('YYYY-MM-DDTHH:mm')` parses as local.
|
|
43
|
+
const date = new Date(value);
|
|
44
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const DateField = ({
|
|
48
|
+
type,
|
|
49
|
+
format,
|
|
50
|
+
readonly,
|
|
51
|
+
placeholder: _placeholder,
|
|
52
|
+
onValueChange,
|
|
53
|
+
onBlur: _onBlur,
|
|
54
|
+
...props
|
|
55
|
+
}: FormFieldComponentProps<string>) => {
|
|
56
|
+
const handleSimpleChange = useCallback((next: string) => onValueChange(type, next), [type, onValueChange]);
|
|
57
|
+
|
|
58
|
+
const handleDateTimeChange = useCallback(
|
|
59
|
+
(next: string) => {
|
|
60
|
+
const iso = localDateTimeToIso(next);
|
|
61
|
+
onValueChange(type, iso as string);
|
|
62
|
+
},
|
|
63
|
+
[type, onValueChange],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<FormFieldWrapper<string> readonly={readonly} format={format} {...props}>
|
|
68
|
+
{({ value }) => {
|
|
69
|
+
switch (format) {
|
|
70
|
+
case Format.TypeFormat.Date:
|
|
71
|
+
return (
|
|
72
|
+
<div className='grid grid-cols-[minmax(0,1fr)_min-content] items-stretch gap-1'>
|
|
73
|
+
<Input.Date
|
|
74
|
+
classNames='min-w-0 overflow-hidden'
|
|
75
|
+
disabled={!!readonly}
|
|
76
|
+
value={value ?? ''}
|
|
77
|
+
onValueChange={handleSimpleChange}
|
|
78
|
+
/>
|
|
79
|
+
<Input.TriggerIcon classNames='h-full' />
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
case Format.TypeFormat.Time:
|
|
83
|
+
return <Input.Time disabled={!!readonly} value={value ?? ''} onValueChange={handleSimpleChange} />;
|
|
84
|
+
case Format.TypeFormat.DateTime:
|
|
85
|
+
default:
|
|
86
|
+
return (
|
|
87
|
+
<div className='grid grid-cols-[minmax(0,1fr)_min-content] items-stretch gap-1'>
|
|
88
|
+
<Input.DateTime
|
|
89
|
+
classNames='min-w-0 overflow-hidden'
|
|
90
|
+
disabled={!!readonly}
|
|
91
|
+
value={isoToLocalDateTime(value)}
|
|
92
|
+
onValueChange={handleDateTimeChange}
|
|
93
|
+
/>
|
|
94
|
+
<Input.TriggerIcon classNames='h-full' />
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}}
|
|
99
|
+
</FormFieldWrapper>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
@@ -15,6 +15,7 @@ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldCompon
|
|
|
15
15
|
export const GeoPointField = ({
|
|
16
16
|
type,
|
|
17
17
|
label,
|
|
18
|
+
jsonPath,
|
|
18
19
|
readonly,
|
|
19
20
|
layout,
|
|
20
21
|
getStatus,
|
|
@@ -59,7 +60,9 @@ export const GeoPointField = ({
|
|
|
59
60
|
|
|
60
61
|
return (
|
|
61
62
|
<Input.Root validationValence={status}>
|
|
62
|
-
{layout !== 'inline' &&
|
|
63
|
+
{layout !== 'inline' && (
|
|
64
|
+
<FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} asChild />
|
|
65
|
+
)}
|
|
63
66
|
{layout === 'static' ? (
|
|
64
67
|
<LatLng {...value} />
|
|
65
68
|
) : (
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useCallback, useMemo } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type Database, Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
+
import { AtomRef } from '@dxos/echo-atom';
|
|
10
|
+
import { useType as defaultUseType } from '@dxos/echo-react';
|
|
11
|
+
import {
|
|
12
|
+
type JsonPath,
|
|
13
|
+
ReferenceAnnotationId,
|
|
14
|
+
type ReferenceAnnotationValue,
|
|
15
|
+
splitJsonPath,
|
|
16
|
+
} from '@dxos/echo/internal';
|
|
17
|
+
import { findAnnotation } from '@dxos/effect';
|
|
18
|
+
import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
|
|
19
|
+
|
|
20
|
+
import { translationKey } from '#translations';
|
|
21
|
+
|
|
22
|
+
import { Form, omitId } from '../Form';
|
|
23
|
+
import { Nesting } from '../FormField';
|
|
24
|
+
import { FormFieldLabel } from '../FormFieldComponent';
|
|
25
|
+
import { type RefFieldProps } from './RefField';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Renders a referenced object's own fields inline (a nested form bound to the
|
|
29
|
+
* target) rather than the {@link RefField} picker. Activated by
|
|
30
|
+
* `FormInlineAnnotation.set(true)` on the `Ref` property.
|
|
31
|
+
*
|
|
32
|
+
* The nested form edits the target object directly: each changed path is
|
|
33
|
+
* written back via `Obj.setValue` within an `Obj.update`, mirroring
|
|
34
|
+
* `ObjectProperties` (without the synthetic meta-tags row).
|
|
35
|
+
*/
|
|
36
|
+
export const InlineRefField = (props: RefFieldProps) => {
|
|
37
|
+
const {
|
|
38
|
+
type,
|
|
39
|
+
readonly,
|
|
40
|
+
label,
|
|
41
|
+
jsonPath,
|
|
42
|
+
layout,
|
|
43
|
+
db,
|
|
44
|
+
getValue,
|
|
45
|
+
onValueChange,
|
|
46
|
+
onCreate,
|
|
47
|
+
useType = defaultUseType,
|
|
48
|
+
} = props;
|
|
49
|
+
const { t } = useTranslation(translationKey);
|
|
50
|
+
|
|
51
|
+
const reference = getValue() as Ref.Ref<any> | undefined;
|
|
52
|
+
const typename = useMemo(
|
|
53
|
+
() => (type ? findAnnotation<ReferenceAnnotationValue>(type, ReferenceAnnotationId)?.typename : undefined),
|
|
54
|
+
[type],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Empty ref: offer to create the referenced object so the inline form has a target.
|
|
58
|
+
const createType = useType(db, typename);
|
|
59
|
+
const handleCreate = useCallback(async () => {
|
|
60
|
+
if (!createType || !onCreate) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const newObject = await onCreate(createType, {});
|
|
64
|
+
if (newObject) {
|
|
65
|
+
onValueChange(type, Ref.make(newObject));
|
|
66
|
+
}
|
|
67
|
+
}, [createType, onCreate, type, onValueChange]);
|
|
68
|
+
|
|
69
|
+
if (readonly && !reference) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Input.Root>
|
|
75
|
+
{layout !== 'inline' && <FormFieldLabel readonly={readonly} label={label} path={jsonPath} />}
|
|
76
|
+
{reference ? (
|
|
77
|
+
<InlineForm reference={reference} db={db} readonly={readonly} useType={useType} />
|
|
78
|
+
) : (
|
|
79
|
+
!readonly &&
|
|
80
|
+
onCreate && (
|
|
81
|
+
<Button classNames='w-full gap-form-gap' disabled={!createType || !db} onClick={() => void handleCreate()}>
|
|
82
|
+
<Icon icon='ph--plus--regular' size={4} />
|
|
83
|
+
<span>{label || t('ref-field.placeholder')}</span>
|
|
84
|
+
</Button>
|
|
85
|
+
)
|
|
86
|
+
)}
|
|
87
|
+
</Input.Root>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
type InlineFormProps = {
|
|
92
|
+
reference: Ref.Ref<any>;
|
|
93
|
+
db?: Database.Database;
|
|
94
|
+
readonly?: boolean;
|
|
95
|
+
// Accepts both the generic `defaultUseType` and `RefFieldProps['useType']`.
|
|
96
|
+
useType?: (db?: Database.Database, typename?: string) => Type.AnyEntity | undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const InlineForm = ({ reference, db, readonly, useType = defaultUseType }: InlineFormProps) => {
|
|
100
|
+
const target = useAtomValue(useMemo(() => AtomRef.make(reference), [reference]));
|
|
101
|
+
const typename = target ? (Obj.getTypename(target) ?? undefined) : undefined;
|
|
102
|
+
const typeFromRegistry = useType(db, typename);
|
|
103
|
+
const targetType = (target && Obj.getType(target)) || typeFromRegistry;
|
|
104
|
+
// Drop the ECHO `id` property (mirrors `ObjectProperties` via `withMetaTags`);
|
|
105
|
+
// otherwise the nested form renders an "Id" field. Hidden fields
|
|
106
|
+
// (`FormInputAnnotation.set(false)`) are already filtered by the field set.
|
|
107
|
+
const formSchema = useMemo(() => (targetType ? omitId(Type.getSchema(targetType)) : undefined), [targetType]);
|
|
108
|
+
const defaultValues = useMemo(() => (target ? { ...target } : {}), [target]);
|
|
109
|
+
|
|
110
|
+
// `formSchema` is resolved at runtime (the referenced object's schema), so the
|
|
111
|
+
// form's value type is not statically known here — `any` at this form-value
|
|
112
|
+
// boundary mirrors `ObjectProperties`. Each changed path is written back to the
|
|
113
|
+
// live target via `Obj.setValue`.
|
|
114
|
+
const handleChange = useCallback(
|
|
115
|
+
(values: any, { isValid, changed }: { isValid: boolean; changed: Record<JsonPath, boolean> }) => {
|
|
116
|
+
if (!isValid || !target) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const changedPaths = (Object.keys(changed) as JsonPath[]).filter((path) => changed[path]);
|
|
120
|
+
if (changedPaths.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
Obj.update(target, () => {
|
|
124
|
+
for (const path of changedPaths) {
|
|
125
|
+
const parts = splitJsonPath(path);
|
|
126
|
+
Obj.setValue(target, parts, Obj.getValue(values, parts));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
[target],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!target || !formSchema) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Nesting>
|
|
139
|
+
<Form.Root schema={formSchema} defaultValues={defaultValues as any} db={db} onValuesChanged={handleChange}>
|
|
140
|
+
<Form.Content>
|
|
141
|
+
<Form.FieldSet readonly={readonly} />
|
|
142
|
+
</Form.Content>
|
|
143
|
+
</Form.Root>
|
|
144
|
+
</Nesting>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
@@ -31,6 +31,7 @@ export const MarkdownField = ({
|
|
|
31
31
|
type,
|
|
32
32
|
readonly,
|
|
33
33
|
label,
|
|
34
|
+
jsonPath,
|
|
34
35
|
placeholder,
|
|
35
36
|
layout,
|
|
36
37
|
db,
|
|
@@ -80,7 +81,7 @@ export const MarkdownField = ({
|
|
|
80
81
|
|
|
81
82
|
return (
|
|
82
83
|
<Input.Root validationValence={status}>
|
|
83
|
-
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} />}
|
|
84
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
|
|
84
85
|
{layout === 'static' ? renderStatic() : renderEditor()}
|
|
85
86
|
{layout === 'full' && <Input.Validation>{error}</Input.Validation>}
|
|
86
87
|
</Input.Root>
|
|
@@ -104,8 +105,7 @@ type RefMarkdownEditorProps = {
|
|
|
104
105
|
const RefMarkdownEditor = ({ reference, placeholder }: RefMarkdownEditorProps) => {
|
|
105
106
|
const text = useAtomValue(useMemo(() => AtomRef.make(reference), [reference]));
|
|
106
107
|
const dataExtensions = useMemo(
|
|
107
|
-
() =>
|
|
108
|
-
text ? [createDataExtensions({ id: reference.dxn.toString(), text: createDocAccessor(text, ['content']) })] : [],
|
|
108
|
+
() => (text ? [createDataExtensions({ id: reference.uri, text: createDocAccessor(text, ['content']) })] : []),
|
|
109
109
|
[text, reference],
|
|
110
110
|
);
|
|
111
111
|
const extensions = useBasicMarkdownExtensions({ placeholder, extensions: dataExtensions });
|
|
@@ -6,10 +6,10 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
8
8
|
import { type Database, Entity, Filter, Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
-
import { useQuery,
|
|
10
|
-
import { ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/internal';
|
|
9
|
+
import { useQuery, useType as defaultUseType } from '@dxos/echo-react';
|
|
10
|
+
import { ANY_OBJECT_TYPENAME, ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/internal';
|
|
11
11
|
import { findAnnotation } from '@dxos/effect';
|
|
12
|
-
import {
|
|
12
|
+
import { URI } from '@dxos/keys';
|
|
13
13
|
import { DxAnchor } from '@dxos/lit-ui/react';
|
|
14
14
|
import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
|
|
15
15
|
import { ParentLabelAnnotationId } from '@dxos/schema';
|
|
@@ -27,7 +27,7 @@ const isRefSnapshot = (val: any): val is { '/': string } => {
|
|
|
27
27
|
|
|
28
28
|
const defaultGetOptions: NonNullable<RefFieldProps['getOptions']> = (results, { parentLabel } = {}) =>
|
|
29
29
|
results.map((result) => {
|
|
30
|
-
const id = Entity.
|
|
30
|
+
const id = Entity.getURI(result);
|
|
31
31
|
const parent = parentLabel ? Obj.getParent(result as Obj.Unknown) : undefined;
|
|
32
32
|
const label = parent ? Entity.getLabel(parent) : Entity.getLabel(result);
|
|
33
33
|
return { id, label: label ?? id };
|
|
@@ -38,7 +38,7 @@ const defaultUseResults: NonNullable<RefFieldProps['useResults']> = (db, typenam
|
|
|
38
38
|
db,
|
|
39
39
|
typename
|
|
40
40
|
? // For Ref.Ref(Obj.Unknown) we want to show all objects.
|
|
41
|
-
typename ===
|
|
41
|
+
typename === ANY_OBJECT_TYPENAME
|
|
42
42
|
? Filter.everything()
|
|
43
43
|
: Filter.typename(typename)
|
|
44
44
|
: Filter.nothing(),
|
|
@@ -51,7 +51,7 @@ export type RefFieldProps = FormFieldComponentProps &
|
|
|
51
51
|
> & {
|
|
52
52
|
db?: Database.Database;
|
|
53
53
|
// TODO(burdon): Replace hooks with callbacks.
|
|
54
|
-
|
|
54
|
+
useType?: (db?: Database.Database, typename?: string) => Type.AnyEntity;
|
|
55
55
|
useResults?: (db?: Database.Database, typename?: string) => Entity.Any[];
|
|
56
56
|
getOptions?: (objects: Entity.Any[], options?: { parentLabel?: boolean }) => RefOption[];
|
|
57
57
|
/**
|
|
@@ -68,6 +68,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
68
68
|
type,
|
|
69
69
|
readonly,
|
|
70
70
|
label,
|
|
71
|
+
jsonPath,
|
|
71
72
|
placeholder,
|
|
72
73
|
layout,
|
|
73
74
|
getStatus,
|
|
@@ -77,7 +78,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
77
78
|
createInitialValuePath,
|
|
78
79
|
createFieldMap,
|
|
79
80
|
db,
|
|
80
|
-
|
|
81
|
+
useType = defaultUseType,
|
|
81
82
|
useResults = defaultUseResults,
|
|
82
83
|
getOptions = defaultGetOptions,
|
|
83
84
|
onCreate,
|
|
@@ -99,21 +100,24 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
99
100
|
|
|
100
101
|
const handleGetValue = useCallback(() => {
|
|
101
102
|
const formValue = getValue();
|
|
102
|
-
// Match form-value Refs against options by the bare
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// form
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
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;
|
|
110
114
|
|
|
111
115
|
const unknownToRefOption = (value: unknown) => {
|
|
112
116
|
const isRef = Ref.isRef(value);
|
|
113
117
|
if (isRef || isRefSnapshot(value)) {
|
|
114
|
-
const dxnString = isRef ? value.
|
|
115
|
-
const objectId =
|
|
116
|
-
const matchingOption = options.find((option) =>
|
|
118
|
+
const dxnString = isRef ? value.uri : value['/'];
|
|
119
|
+
const objectId = dxnToEntityId(dxnString);
|
|
120
|
+
const matchingOption = options.find((option) => dxnToEntityId(option.id) === objectId);
|
|
117
121
|
if (matchingOption) {
|
|
118
122
|
return matchingOption;
|
|
119
123
|
}
|
|
@@ -127,7 +131,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
127
131
|
|
|
128
132
|
const item = handleGetValue();
|
|
129
133
|
const selectedIds = useMemo(() => (item ? [item.id] : []), [item]);
|
|
130
|
-
const createSchema =
|
|
134
|
+
const createSchema = useType(db, typename);
|
|
131
135
|
|
|
132
136
|
// Lift the popover open state so we can dismiss it after a successful create.
|
|
133
137
|
const [open, setOpen] = useState(false);
|
|
@@ -152,7 +156,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
152
156
|
const handleUpdate = useCallback(
|
|
153
157
|
(id: string | undefined) => {
|
|
154
158
|
const item = options.find((option) => option.id === id);
|
|
155
|
-
const ref = item ? Ref.
|
|
159
|
+
const ref = item ? Ref.fromURI(URI.make(item.id)) : undefined;
|
|
156
160
|
onValueChange(type, ref);
|
|
157
161
|
},
|
|
158
162
|
[options, type, onValueChange],
|
|
@@ -175,7 +179,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
175
179
|
|
|
176
180
|
return (
|
|
177
181
|
<Input.Root validationValence={status}>
|
|
178
|
-
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} />}
|
|
182
|
+
{layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
|
|
179
183
|
<div>
|
|
180
184
|
{readonly ? (
|
|
181
185
|
!item ? (
|
|
@@ -197,7 +201,9 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
197
201
|
) : (
|
|
198
202
|
<Button classNames='w-full text-start gap-form-gap'>
|
|
199
203
|
<div className='grow overflow-hidden'>
|
|
200
|
-
<span className='truncate text-description'>
|
|
204
|
+
<span className='truncate text-description'>
|
|
205
|
+
{placeholder || label || t('ref-field.placeholder')}
|
|
206
|
+
</span>
|
|
201
207
|
</div>
|
|
202
208
|
<Icon size={3} icon='ph--caret-down--bold' />
|
|
203
209
|
</Button>
|
|
@@ -211,7 +217,7 @@ export const RefField = (props: RefFieldProps) => {
|
|
|
211
217
|
// Strip hidden (`FormInputAnnotation.set(false)`) fields so the
|
|
212
218
|
// form's validator doesn't reject required-but-hidden fields
|
|
213
219
|
// such as backing-object refs supplied by a `FactoryAnnotation`.
|
|
214
|
-
createSchema={createSchema && omitHiddenFormFields(omitId(createSchema))}
|
|
220
|
+
createSchema={createSchema && omitHiddenFormFields(omitId(Type.getSchema(createSchema)))}
|
|
215
221
|
createOptionLabel={createOptionLabel}
|
|
216
222
|
createOptionIcon={createOptionIcon}
|
|
217
223
|
createInitialValuePath={createInitialValuePath}
|
|
@@ -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
|
+
};
|
|
@@ -19,6 +19,7 @@ export const SelectOptionField = ({
|
|
|
19
19
|
type,
|
|
20
20
|
readonly,
|
|
21
21
|
label,
|
|
22
|
+
jsonPath,
|
|
22
23
|
getStatus,
|
|
23
24
|
getValue,
|
|
24
25
|
onValueChange,
|
|
@@ -119,7 +120,7 @@ export const SelectOptionField = ({
|
|
|
119
120
|
|
|
120
121
|
return (
|
|
121
122
|
<Input.Root validationValence={status}>
|
|
122
|
-
<FormFieldLabel error={error} readonly={readonly} label={label} />
|
|
123
|
+
<FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />
|
|
123
124
|
<div>
|
|
124
125
|
{options && (
|
|
125
126
|
<List.Root
|
|
@@ -153,8 +154,8 @@ export const SelectOptionField = ({
|
|
|
153
154
|
/>
|
|
154
155
|
</div>
|
|
155
156
|
{selected === item.id && (
|
|
156
|
-
<div className='flex flex-col p-form-padding gap-form-gap dx-density-
|
|
157
|
-
<Input.Label classNames='text-
|
|
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>
|
|
158
159
|
<div className='grid grid-cols-[1fr_min-content_min-content] gap-form-gap'>
|
|
159
160
|
<Input.TextInput
|
|
160
161
|
disabled={!!readonly}
|