@dxos/react-ui-form 0.8.2-main.36232bc → 0.8.2-main.5885341
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 +194 -70
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +248 -126
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +194 -70
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Form/Form.d.ts +3 -1
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -0
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +13 -10
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +23 -0
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/package.json +25 -25
- package/src/components/Form/Form.stories.tsx +44 -1
- package/src/components/Form/Form.tsx +4 -0
- package/src/components/Form/FormContent.tsx +59 -15
- package/src/components/Form/RefField.tsx +152 -0
- package/src/components/ViewEditor/ViewEditor.tsx +3 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type AST,
|
|
9
|
+
Expando,
|
|
10
|
+
getTypeAnnotation,
|
|
11
|
+
Ref,
|
|
12
|
+
ReferenceAnnotationId,
|
|
13
|
+
type ReferenceAnnotationValue,
|
|
14
|
+
type TypeAnnotation,
|
|
15
|
+
} from '@dxos/echo-schema';
|
|
16
|
+
import { findAnnotation, type SimpleType } from '@dxos/effect';
|
|
17
|
+
import { DXN } from '@dxos/keys';
|
|
18
|
+
import { refFromDXN } from '@dxos/live-object';
|
|
19
|
+
import { log } from '@dxos/log';
|
|
20
|
+
import { type MaybePromise } from '@dxos/util';
|
|
21
|
+
|
|
22
|
+
import { SelectInput, TextInput } from './Defaults';
|
|
23
|
+
import { type FormInputStateProps } from './FormContext';
|
|
24
|
+
|
|
25
|
+
export type QueryRefOptions = (type: TypeAnnotation) => MaybePromise<{ dxn: DXN; label?: string }[]>;
|
|
26
|
+
|
|
27
|
+
type RefFieldProps = {
|
|
28
|
+
ast: AST.AST;
|
|
29
|
+
type: SimpleType;
|
|
30
|
+
label: string;
|
|
31
|
+
readonly?: boolean;
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
inline?: boolean;
|
|
34
|
+
onQueryRefOptions?: QueryRefOptions;
|
|
35
|
+
inputProps: FormInputStateProps;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const RefField = ({
|
|
39
|
+
ast,
|
|
40
|
+
type,
|
|
41
|
+
label,
|
|
42
|
+
readonly,
|
|
43
|
+
placeholder,
|
|
44
|
+
inline,
|
|
45
|
+
onQueryRefOptions,
|
|
46
|
+
inputProps,
|
|
47
|
+
}: RefFieldProps) => {
|
|
48
|
+
const { getValue, onValueChange, ...restInputProps } = inputProps;
|
|
49
|
+
const refTypeInfo = findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId);
|
|
50
|
+
const [refOptions, setRefOptions] = useState<Array<{ value: string; label?: string }>>([]);
|
|
51
|
+
const [loading, setLoading] = useState(false);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!refTypeInfo || !onQueryRefOptions) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const fetchOptions = async () => {
|
|
59
|
+
setLoading(true);
|
|
60
|
+
try {
|
|
61
|
+
const options = await onQueryRefOptions(refTypeInfo);
|
|
62
|
+
setRefOptions(
|
|
63
|
+
options.map((option) => ({
|
|
64
|
+
label: option.label,
|
|
65
|
+
value: option.dxn.toString(),
|
|
66
|
+
})),
|
|
67
|
+
);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
log.error('Failed to fetch ref options:', error);
|
|
70
|
+
setRefOptions([]);
|
|
71
|
+
} finally {
|
|
72
|
+
setLoading(false);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
void fetchOptions();
|
|
77
|
+
}, [refTypeInfo, onQueryRefOptions]);
|
|
78
|
+
|
|
79
|
+
if (!refTypeInfo) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// If ref type is expando, fall back to taking a DXN in string format.
|
|
84
|
+
if (refTypeInfo.typename === getTypeAnnotation(Expando)?.typename || !onQueryRefOptions) {
|
|
85
|
+
const handleOnValueChange = (_type: any, dxnString: string) => {
|
|
86
|
+
const dxn = DXN.tryParse(dxnString);
|
|
87
|
+
if (dxn) {
|
|
88
|
+
onValueChange?.('object', refFromDXN(dxn));
|
|
89
|
+
} else if (dxnString === '') {
|
|
90
|
+
onValueChange?.('object', undefined);
|
|
91
|
+
} else {
|
|
92
|
+
onValueChange?.('string', dxnString);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleGetValue = () => {
|
|
97
|
+
const formValue = getValue();
|
|
98
|
+
if (typeof formValue === 'string') {
|
|
99
|
+
return formValue;
|
|
100
|
+
}
|
|
101
|
+
if (Ref.isRef(formValue)) {
|
|
102
|
+
return formValue.dxn.toString();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<TextInput
|
|
110
|
+
type={type}
|
|
111
|
+
label={label}
|
|
112
|
+
disabled={readonly}
|
|
113
|
+
placeholder={placeholder}
|
|
114
|
+
inputOnly={inline}
|
|
115
|
+
getValue={handleGetValue as <V>() => V | undefined}
|
|
116
|
+
onValueChange={handleOnValueChange}
|
|
117
|
+
{...restInputProps}
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleGetValue = () => {
|
|
123
|
+
const formValue = getValue();
|
|
124
|
+
|
|
125
|
+
if (Ref.isRef(formValue)) {
|
|
126
|
+
return formValue.dxn.toString();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return undefined;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleValueChanged = (_type: any, dxnString: string) => {
|
|
133
|
+
const dxn = DXN.parse(dxnString);
|
|
134
|
+
const ref = refFromDXN(dxn);
|
|
135
|
+
|
|
136
|
+
onValueChange('object', ref);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<SelectInput
|
|
141
|
+
type={type}
|
|
142
|
+
label={label}
|
|
143
|
+
disabled={readonly || loading}
|
|
144
|
+
placeholder={loading ? 'Loading options...' : placeholder}
|
|
145
|
+
inputOnly={inline}
|
|
146
|
+
getValue={handleGetValue as <V>() => V | undefined}
|
|
147
|
+
onValueChange={handleValueChanged}
|
|
148
|
+
options={refOptions}
|
|
149
|
+
{...restInputProps}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
@@ -139,15 +139,16 @@ export const ViewEditor = ({
|
|
|
139
139
|
);
|
|
140
140
|
|
|
141
141
|
return (
|
|
142
|
-
<div role='none' className={mx('
|
|
142
|
+
<div role='none' className={mx('grid grid-rows-[1fr_1fr] min-bs-0', classNames)}>
|
|
143
143
|
<Form<ViewMetaType>
|
|
144
144
|
autoSave
|
|
145
145
|
schema={ViewMetaSchema}
|
|
146
146
|
values={viewValues}
|
|
147
147
|
onSave={schema.readonly ? undefined : handleUpdate}
|
|
148
|
+
classNames='min-bs-0 overflow-y-auto'
|
|
148
149
|
/>
|
|
149
150
|
|
|
150
|
-
<div>
|
|
151
|
+
<div role='none' className='border-bs border-separator min-bs-0 overflow-y-auto'>
|
|
151
152
|
{/* TODO(burdon): Clean up common form ux. */}
|
|
152
153
|
<div role='none' className='p-2'>
|
|
153
154
|
<label className={mx(inputTextLabel)}>{t('fields label')}</label>
|