@dxos/react-ui-form 0.7.5-feature-compute.4d9d99a → 0.7.5-labs.071a3e2
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 +717 -468
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +742 -508
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +717 -468
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +16 -0
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/Defaults.d.ts +5 -6
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +12 -21
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -1
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +7 -0
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContent.d.ts +26 -0
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContext.d.ts +20 -0
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Input.d.ts +5 -6
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +4 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +7 -0
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/index.d.ts +1 -0
- package/dist/types/src/components/Form/custom/index.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts +2 -2
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +5 -0
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +9 -10
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +6 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -24
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.tsx +7 -6
- package/src/components/Form/ArrayField.tsx +111 -0
- package/src/components/Form/Defaults.tsx +18 -33
- package/src/components/Form/Form.stories.tsx +15 -5
- package/src/components/Form/Form.tsx +47 -355
- package/src/components/Form/FormActions.tsx +41 -0
- package/src/components/Form/FormContent.tsx +173 -0
- package/src/components/Form/FormContext.tsx +48 -0
- package/src/components/Form/Input.tsx +5 -6
- package/src/components/Form/custom/GeoPointInput.tsx +6 -15
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
- package/src/components/Form/custom/SelectOptionsInput.tsx +192 -0
- package/src/components/Form/custom/TupleInput.tsx +5 -9
- package/src/components/Form/custom/index.ts +1 -0
- package/src/components/Form/factory.tsx +2 -5
- package/src/components/ViewEditor/ViewEditor.tsx +5 -2
- package/src/components/deprecated/FormInput.tsx +20 -7
- package/src/hooks/useForm.ts +54 -47
- package/src/translations.ts +8 -0
|
@@ -35,6 +35,7 @@ var translations_default = [
|
|
|
35
35
|
"format ref": "Reference",
|
|
36
36
|
"format regex": "RegExp",
|
|
37
37
|
"format string": "String",
|
|
38
|
+
"format single-select": "Select",
|
|
38
39
|
"format text": "Long text",
|
|
39
40
|
"format time": "Time",
|
|
40
41
|
"format timestamp": "Timestamp",
|
|
@@ -44,7 +45,13 @@ var translations_default = [
|
|
|
44
45
|
"format uuid": "UUID",
|
|
45
46
|
// Placeholders
|
|
46
47
|
"placeholder latitude": "Latitude",
|
|
47
|
-
"placeholder longitude": "Longitude"
|
|
48
|
+
"placeholder longitude": "Longitude",
|
|
49
|
+
// SelectOptionsInput
|
|
50
|
+
"select option label": "Label",
|
|
51
|
+
"select option label placeholder": "Option label",
|
|
52
|
+
"select option color": "Color",
|
|
53
|
+
"select option delete": "Delete",
|
|
54
|
+
"select option add": "Add option"
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
}
|
|
@@ -53,6 +60,16 @@ var translations_default = [
|
|
|
53
60
|
// packages/ui/react-ui-form/src/components/deprecated/FormInput.tsx
|
|
54
61
|
import React from "react";
|
|
55
62
|
import { Input } from "@dxos/react-ui";
|
|
63
|
+
import { mx, textBlockWidth } from "@dxos/react-ui-theme";
|
|
64
|
+
var DeprecatedFormContainer = ({ children, classNames }) => {
|
|
65
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
66
|
+
role: "none",
|
|
67
|
+
className: "w-full p-4 justify-center overflow-x-hidden overflow-y-auto"
|
|
68
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
69
|
+
role: "form",
|
|
70
|
+
className: mx("flex flex-col", textBlockWidth, classNames)
|
|
71
|
+
}, children));
|
|
72
|
+
};
|
|
56
73
|
var DeprecatedFormInput = ({ label, description, secondary, children }) => {
|
|
57
74
|
const primary = /* @__PURE__ */ React.createElement("div", {
|
|
58
75
|
role: "none",
|
|
@@ -61,30 +78,32 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
|
|
|
61
78
|
role: "none",
|
|
62
79
|
className: "flex flex-col w-full"
|
|
63
80
|
}, /* @__PURE__ */ React.createElement(Input.Label, {
|
|
64
|
-
classNames: "flex min-
|
|
81
|
+
classNames: "flex min-bs-[--rail-action] items-center",
|
|
82
|
+
style: {
|
|
83
|
+
fontSize: "unset"
|
|
84
|
+
}
|
|
65
85
|
}, label), description && /* @__PURE__ */ React.createElement(Input.DescriptionAndValidation, {
|
|
66
86
|
classNames: "mbs-0.5"
|
|
67
87
|
}, /* @__PURE__ */ React.createElement(Input.Description, null, description))), /* @__PURE__ */ React.createElement("div", {
|
|
68
88
|
role: "none"
|
|
69
89
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
70
90
|
role: "none",
|
|
71
|
-
className: "flex min-
|
|
91
|
+
className: "flex min-bs-[--rail-action] items-center"
|
|
72
92
|
}, children))));
|
|
73
93
|
if (secondary) {
|
|
74
94
|
return /* @__PURE__ */ React.createElement("div", {
|
|
75
|
-
role: "none"
|
|
76
|
-
className: "flex flex-col w-full"
|
|
95
|
+
role: "none"
|
|
77
96
|
}, primary, secondary);
|
|
78
97
|
}
|
|
79
98
|
return primary;
|
|
80
99
|
};
|
|
81
100
|
|
|
82
101
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
83
|
-
import
|
|
102
|
+
import React12, { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo5, useState as useState3 } from "react";
|
|
84
103
|
import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
|
|
85
|
-
import { log as
|
|
86
|
-
import { useTranslation as
|
|
87
|
-
import { getFormatSchema, getSchemaProperties as
|
|
104
|
+
import { log as log2 } from "@dxos/log";
|
|
105
|
+
import { useTranslation as useTranslation5 } from "@dxos/react-ui";
|
|
106
|
+
import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
|
|
88
107
|
|
|
89
108
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
90
109
|
import React3 from "react";
|
|
@@ -111,10 +130,10 @@ var InputHeader = ({ children, error }) => {
|
|
|
111
130
|
};
|
|
112
131
|
|
|
113
132
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
114
|
-
var GeoPointInput = ({
|
|
133
|
+
var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
115
134
|
const { t } = useTranslation(translationKey);
|
|
116
|
-
const { status, error } = getStatus
|
|
117
|
-
const [lng = 0, lat = 0] =
|
|
135
|
+
const { status, error } = getStatus();
|
|
136
|
+
const [lng = 0, lat = 0] = getValue3() ?? [];
|
|
118
137
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
119
138
|
validationValence: status
|
|
120
139
|
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
@@ -126,7 +145,7 @@ var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: get
|
|
|
126
145
|
disabled,
|
|
127
146
|
placeholder: t("placeholder longitude"),
|
|
128
147
|
value: lng,
|
|
129
|
-
onChange: (event) => onValueChange(
|
|
148
|
+
onChange: (event) => onValueChange(type, [
|
|
130
149
|
lat,
|
|
131
150
|
safeParseFloat(event.target.value, 0)
|
|
132
151
|
]),
|
|
@@ -136,7 +155,7 @@ var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: get
|
|
|
136
155
|
disabled,
|
|
137
156
|
placeholder: t("placeholder latitude"),
|
|
138
157
|
value: lat,
|
|
139
|
-
onChange: (event) => onValueChange(
|
|
158
|
+
onChange: (event) => onValueChange(type, [
|
|
140
159
|
safeParseFloat(event.target.value, 0),
|
|
141
160
|
lng
|
|
142
161
|
]),
|
|
@@ -144,10 +163,193 @@ var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: get
|
|
|
144
163
|
})));
|
|
145
164
|
};
|
|
146
165
|
|
|
166
|
+
// packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
|
|
167
|
+
import React4, { useCallback, useEffect, useState, useRef } from "react";
|
|
168
|
+
import { PublicKey } from "@dxos/keys";
|
|
169
|
+
import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
170
|
+
import { List } from "@dxos/react-ui-list";
|
|
171
|
+
import { HuePickerToolbarButton } from "@dxos/react-ui-pickers";
|
|
172
|
+
import { hueTokenThemes, mx as mx2 } from "@dxos/react-ui-theme";
|
|
173
|
+
var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
|
|
174
|
+
const [selected, setSelectedId] = useState(null);
|
|
175
|
+
const [isNewOption, setIsNewOption] = useState(false);
|
|
176
|
+
const inputRef = useRef(null);
|
|
177
|
+
const { t } = useTranslation2(translationKey);
|
|
178
|
+
const { status, error } = getStatus();
|
|
179
|
+
const options = getValue3();
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (options === void 0) {
|
|
182
|
+
onValueChange(type, []);
|
|
183
|
+
}
|
|
184
|
+
}, [
|
|
185
|
+
options,
|
|
186
|
+
onValueChange,
|
|
187
|
+
type
|
|
188
|
+
]);
|
|
189
|
+
const randomHue = () => {
|
|
190
|
+
const usedHues = new Set(options?.map((option) => option.color) ?? []);
|
|
191
|
+
const hueKeys = Object.keys(hueTokenThemes);
|
|
192
|
+
const unusedHues = hueKeys.filter((hue) => !usedHues.has(hue));
|
|
193
|
+
if (unusedHues.length > 0) {
|
|
194
|
+
return unusedHues[Math.floor(Math.random() * unusedHues.length)];
|
|
195
|
+
}
|
|
196
|
+
const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
|
|
197
|
+
const availableHues = hueKeys.filter((hue) => !lastThree.has(hue));
|
|
198
|
+
return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hueKeys[Math.floor(Math.random() * hueKeys.length)];
|
|
199
|
+
};
|
|
200
|
+
const handleAdd = useCallback(() => {
|
|
201
|
+
const newId = PublicKey.random().truncate();
|
|
202
|
+
const newOption = {
|
|
203
|
+
id: newId,
|
|
204
|
+
title: "",
|
|
205
|
+
color: randomHue()
|
|
206
|
+
};
|
|
207
|
+
onValueChange(type, [
|
|
208
|
+
...options ?? [],
|
|
209
|
+
newOption
|
|
210
|
+
]);
|
|
211
|
+
setSelectedId(newId);
|
|
212
|
+
setIsNewOption(true);
|
|
213
|
+
}, [
|
|
214
|
+
options,
|
|
215
|
+
type,
|
|
216
|
+
onValueChange
|
|
217
|
+
]);
|
|
218
|
+
const handleDelete = useCallback((id) => {
|
|
219
|
+
const newOptions = options?.filter((option) => option.id !== id) ?? [];
|
|
220
|
+
onValueChange(type, newOptions);
|
|
221
|
+
setSelectedId(null);
|
|
222
|
+
}, [
|
|
223
|
+
options,
|
|
224
|
+
type,
|
|
225
|
+
onValueChange
|
|
226
|
+
]);
|
|
227
|
+
const handleMove = useCallback((from, to) => {
|
|
228
|
+
if (!options) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const newOptions = [
|
|
232
|
+
...options
|
|
233
|
+
];
|
|
234
|
+
const [removed] = newOptions.splice(from, 1);
|
|
235
|
+
newOptions.splice(to, 0, removed);
|
|
236
|
+
onValueChange(type, newOptions);
|
|
237
|
+
}, [
|
|
238
|
+
options,
|
|
239
|
+
type,
|
|
240
|
+
onValueChange
|
|
241
|
+
]);
|
|
242
|
+
const handleClick = useCallback((id) => {
|
|
243
|
+
setSelectedId((current) => current === id ? null : id);
|
|
244
|
+
}, []);
|
|
245
|
+
const handleTitleChange = useCallback((id) => (e) => {
|
|
246
|
+
const newOptions = options?.map((o) => o.id === id ? {
|
|
247
|
+
...o,
|
|
248
|
+
title: e.target.value
|
|
249
|
+
} : o);
|
|
250
|
+
onValueChange(type, newOptions ?? []);
|
|
251
|
+
}, [
|
|
252
|
+
options,
|
|
253
|
+
type,
|
|
254
|
+
onValueChange
|
|
255
|
+
]);
|
|
256
|
+
const handleKeyDown = useCallback((e) => {
|
|
257
|
+
if (e.key === "Enter") {
|
|
258
|
+
setSelectedId(null);
|
|
259
|
+
}
|
|
260
|
+
}, []);
|
|
261
|
+
const handleColorChange = useCallback((id) => (hue) => {
|
|
262
|
+
const newOptions = options?.map((o) => o.id === id ? {
|
|
263
|
+
...o,
|
|
264
|
+
color: hue
|
|
265
|
+
} : o);
|
|
266
|
+
onValueChange(type, newOptions ?? []);
|
|
267
|
+
}, [
|
|
268
|
+
options,
|
|
269
|
+
type,
|
|
270
|
+
onValueChange
|
|
271
|
+
]);
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
if (selected && isNewOption && inputRef.current) {
|
|
274
|
+
inputRef.current.focus();
|
|
275
|
+
setIsNewOption(false);
|
|
276
|
+
}
|
|
277
|
+
}, [
|
|
278
|
+
selected,
|
|
279
|
+
isNewOption
|
|
280
|
+
]);
|
|
281
|
+
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
282
|
+
validationValence: status
|
|
283
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
284
|
+
error
|
|
285
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
|
|
286
|
+
role: "none"
|
|
287
|
+
}, options && /* @__PURE__ */ React4.createElement(List.Root, {
|
|
288
|
+
items: options,
|
|
289
|
+
isItem: (item) => true,
|
|
290
|
+
onMove: handleMove
|
|
291
|
+
}, ({ items }) => /* @__PURE__ */ React4.createElement("div", {
|
|
292
|
+
role: "list",
|
|
293
|
+
className: "w-full overflow-auto"
|
|
294
|
+
}, items?.map((item) => /* @__PURE__ */ React4.createElement("div", {
|
|
295
|
+
key: item.id
|
|
296
|
+
}, /* @__PURE__ */ React4.createElement(List.Item, {
|
|
297
|
+
role: "button",
|
|
298
|
+
item,
|
|
299
|
+
classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
300
|
+
"aria-expanded": selected === item.id
|
|
301
|
+
}, /* @__PURE__ */ React4.createElement("div", {
|
|
302
|
+
className: "flex items-center"
|
|
303
|
+
}, /* @__PURE__ */ React4.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React4.createElement(List.ItemTitle, {
|
|
304
|
+
onClick: () => handleClick(item.id),
|
|
305
|
+
classNames: "flex-1"
|
|
306
|
+
}, /* @__PURE__ */ React4.createElement(Tag, {
|
|
307
|
+
palette: item.color
|
|
308
|
+
}, item.title || "\u200B")), /* @__PURE__ */ React4.createElement(Icon2, {
|
|
309
|
+
icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
|
|
310
|
+
onClick: () => handleClick(item.id)
|
|
311
|
+
})), selected === item.id && /* @__PURE__ */ React4.createElement("div", {
|
|
312
|
+
className: "mis-4 mbs-2 flex flex-col gap-1"
|
|
313
|
+
}, /* @__PURE__ */ React4.createElement(Input3.Label, {
|
|
314
|
+
classNames: "text-xs"
|
|
315
|
+
}, t("select option label")), /* @__PURE__ */ React4.createElement("div", {
|
|
316
|
+
className: "flex flex-row items-center gap-1"
|
|
317
|
+
}, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
|
|
318
|
+
placeholder: t("select option label placeholder"),
|
|
319
|
+
ref: selected === item.id ? inputRef : void 0,
|
|
320
|
+
value: item.title,
|
|
321
|
+
onChange: handleTitleChange(item.id),
|
|
322
|
+
onKeyDown: handleKeyDown,
|
|
323
|
+
classNames: "flex-1"
|
|
324
|
+
}), /* @__PURE__ */ React4.createElement(Toolbar.Root, {
|
|
325
|
+
classNames: "p-0 m-0 !is-auto"
|
|
326
|
+
}, /* @__PURE__ */ React4.createElement(HuePickerToolbarButton, {
|
|
327
|
+
hue: item.color,
|
|
328
|
+
onChangeHue: handleColorChange(item.id)
|
|
329
|
+
})), /* @__PURE__ */ React4.createElement(IconButton, {
|
|
330
|
+
icon: "ph--trash--fill",
|
|
331
|
+
iconOnly: true,
|
|
332
|
+
label: t("select option delete"),
|
|
333
|
+
onClick: () => handleDelete(item.id)
|
|
334
|
+
})))))), /* @__PURE__ */ React4.createElement("div", {
|
|
335
|
+
role: "listitem"
|
|
336
|
+
}, /* @__PURE__ */ React4.createElement("div", {
|
|
337
|
+
role: "button",
|
|
338
|
+
className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
339
|
+
onClick: handleAdd
|
|
340
|
+
}, /* @__PURE__ */ React4.createElement("div", {
|
|
341
|
+
className: "flex items-center gap-1"
|
|
342
|
+
}, /* @__PURE__ */ React4.createElement(Icon2, {
|
|
343
|
+
icon: "ph--plus--regular"
|
|
344
|
+
}), /* @__PURE__ */ React4.createElement("span", {
|
|
345
|
+
className: "text-sm"
|
|
346
|
+
}, t("select option add")))))))));
|
|
347
|
+
};
|
|
348
|
+
|
|
147
349
|
// packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
|
|
148
|
-
import
|
|
149
|
-
import { Input as
|
|
150
|
-
import { mx } from "@dxos/react-ui-theme";
|
|
350
|
+
import React5 from "react";
|
|
351
|
+
import { Input as Input4 } from "@dxos/react-ui";
|
|
352
|
+
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
151
353
|
import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
|
|
152
354
|
var gridCols = [
|
|
153
355
|
"grid-cols-1",
|
|
@@ -155,21 +357,21 @@ var gridCols = [
|
|
|
155
357
|
"grid-cols-3",
|
|
156
358
|
"grid-cols-4"
|
|
157
359
|
];
|
|
158
|
-
var TupleInput = ({ binding,
|
|
159
|
-
const { status, error } = getStatus
|
|
160
|
-
const values =
|
|
161
|
-
return /* @__PURE__ */
|
|
360
|
+
var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
361
|
+
const { status, error } = getStatus();
|
|
362
|
+
const values = getValue3() ?? {};
|
|
363
|
+
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
162
364
|
validationValence: status
|
|
163
|
-
}, /* @__PURE__ */
|
|
365
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
164
366
|
error
|
|
165
|
-
}, /* @__PURE__ */
|
|
166
|
-
className:
|
|
167
|
-
}, binding.map((prop) => /* @__PURE__ */
|
|
367
|
+
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
|
|
368
|
+
className: mx3("grid gap-2", gridCols[binding.length - 1])
|
|
369
|
+
}, binding.map((prop) => /* @__PURE__ */ React5.createElement(Input4.TextInput, {
|
|
168
370
|
key: prop,
|
|
169
371
|
type: "number",
|
|
170
372
|
disabled,
|
|
171
373
|
value: values[prop],
|
|
172
|
-
onChange: (event) => onValueChange(
|
|
374
|
+
onChange: (event) => onValueChange(type, {
|
|
173
375
|
...values,
|
|
174
376
|
[prop]: safeParseFloat2(event.target.value, 0)
|
|
175
377
|
}),
|
|
@@ -178,103 +380,89 @@ var TupleInput = ({ binding, property, type, label, disabled, getStatus, getValu
|
|
|
178
380
|
};
|
|
179
381
|
|
|
180
382
|
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
181
|
-
import
|
|
182
|
-
import { Input as
|
|
183
|
-
var TextInput = ({
|
|
184
|
-
const { status, error } = getStatus
|
|
185
|
-
return /* @__PURE__ */
|
|
383
|
+
import React6 from "react";
|
|
384
|
+
import { Input as Input5, Select } from "@dxos/react-ui";
|
|
385
|
+
var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
386
|
+
const { status, error } = getStatus();
|
|
387
|
+
return /* @__PURE__ */ React6.createElement(Input5.Root, {
|
|
186
388
|
validationValence: status
|
|
187
|
-
}, !inputOnly && /* @__PURE__ */
|
|
389
|
+
}, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
188
390
|
error
|
|
189
|
-
}, /* @__PURE__ */
|
|
391
|
+
}, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
|
|
190
392
|
disabled,
|
|
191
393
|
placeholder,
|
|
192
|
-
value:
|
|
193
|
-
onChange: (event) => onValueChange(
|
|
394
|
+
value: getValue3() ?? "",
|
|
395
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
194
396
|
onBlur
|
|
195
|
-
}), inputOnly && /* @__PURE__ */
|
|
397
|
+
}), inputOnly && /* @__PURE__ */ React6.createElement(Input5.Validation, null, error));
|
|
196
398
|
};
|
|
197
|
-
var NumberInput = ({
|
|
198
|
-
const { status, error } = getStatus
|
|
199
|
-
return /* @__PURE__ */
|
|
399
|
+
var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
400
|
+
const { status, error } = getStatus();
|
|
401
|
+
return /* @__PURE__ */ React6.createElement(Input5.Root, {
|
|
200
402
|
validationValence: status
|
|
201
|
-
}, !inputOnly && /* @__PURE__ */
|
|
403
|
+
}, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
202
404
|
error
|
|
203
|
-
}, /* @__PURE__ */
|
|
405
|
+
}, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
|
|
204
406
|
type: "number",
|
|
205
407
|
disabled,
|
|
206
408
|
placeholder,
|
|
207
|
-
value:
|
|
208
|
-
onChange: (event) => onValueChange(
|
|
409
|
+
value: getValue3() ?? 0,
|
|
410
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
209
411
|
onBlur
|
|
210
|
-
}), inputOnly && /* @__PURE__ */
|
|
412
|
+
}), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
|
|
211
413
|
};
|
|
212
|
-
var BooleanInput = ({
|
|
213
|
-
const { status, error } = getStatus
|
|
214
|
-
return /* @__PURE__ */
|
|
414
|
+
var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
|
|
415
|
+
const { status, error } = getStatus();
|
|
416
|
+
return /* @__PURE__ */ React6.createElement(Input5.Root, {
|
|
215
417
|
validationValence: status
|
|
216
|
-
}, !inputOnly && /* @__PURE__ */
|
|
418
|
+
}, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
217
419
|
error
|
|
218
|
-
}, /* @__PURE__ */
|
|
219
|
-
checked:
|
|
220
|
-
onCheckedChange: (value) => onValueChange(
|
|
221
|
-
}), inputOnly && /* @__PURE__ */
|
|
420
|
+
}, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.Switch, {
|
|
421
|
+
checked: getValue3(),
|
|
422
|
+
onCheckedChange: (value) => onValueChange(type, value)
|
|
423
|
+
}), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
|
|
222
424
|
};
|
|
223
|
-
var SelectInput = ({
|
|
224
|
-
const { status, error } = getStatus
|
|
225
|
-
return /* @__PURE__ */
|
|
425
|
+
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
426
|
+
const { status, error } = getStatus();
|
|
427
|
+
return /* @__PURE__ */ React6.createElement(Input5.Root, {
|
|
226
428
|
validationValence: status
|
|
227
|
-
}, !inputOnly && /* @__PURE__ */
|
|
429
|
+
}, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
228
430
|
error
|
|
229
|
-
}, /* @__PURE__ */
|
|
230
|
-
value:
|
|
231
|
-
onValueChange: (value) => onValueChange(
|
|
232
|
-
}, /* @__PURE__ */
|
|
431
|
+
}, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Select.Root, {
|
|
432
|
+
value: getValue3(),
|
|
433
|
+
onValueChange: (value) => onValueChange(type, value)
|
|
434
|
+
}, /* @__PURE__ */ React6.createElement(Select.TriggerButton, {
|
|
233
435
|
classNames: "is-full",
|
|
234
436
|
disabled,
|
|
235
437
|
placeholder
|
|
236
|
-
}), /* @__PURE__ */
|
|
438
|
+
}), /* @__PURE__ */ React6.createElement(Select.Portal, null, /* @__PURE__ */ React6.createElement(Select.Content, null, /* @__PURE__ */ React6.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
237
439
|
// NOTE: Numeric values are converted to and from strings.
|
|
238
|
-
/* @__PURE__ */
|
|
440
|
+
/* @__PURE__ */ React6.createElement(Select.Option, {
|
|
239
441
|
key: String(value),
|
|
240
442
|
value: String(value)
|
|
241
443
|
}, label2 ?? String(value))
|
|
242
|
-
)))))), inputOnly && /* @__PURE__ */
|
|
444
|
+
)))))), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
|
|
243
445
|
};
|
|
244
446
|
|
|
245
447
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
246
|
-
import {
|
|
247
|
-
import {
|
|
248
|
-
import React6, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
249
|
-
import { AST as AST2, S } from "@dxos/echo-schema";
|
|
250
|
-
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
251
|
-
import { log as log2 } from "@dxos/log";
|
|
252
|
-
import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
253
|
-
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
254
|
-
import { getSchemaProperties } from "@dxos/schema";
|
|
255
|
-
import { isNotFalsy } from "@dxos/util";
|
|
448
|
+
import React11, { useEffect as useEffect3, useMemo as useMemo4 } from "react";
|
|
449
|
+
import { mx as mx7 } from "@dxos/react-ui-theme";
|
|
256
450
|
|
|
257
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
258
|
-
import
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
case "number":
|
|
268
|
-
return NumberInput;
|
|
269
|
-
case "boolean":
|
|
270
|
-
return BooleanInput;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
451
|
+
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
452
|
+
import React8 from "react";
|
|
453
|
+
import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
454
|
+
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
455
|
+
|
|
456
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
457
|
+
import React7, { createContext, useContext } from "react";
|
|
458
|
+
import { raise } from "@dxos/debug";
|
|
459
|
+
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
460
|
+
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
273
461
|
|
|
274
462
|
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
275
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
463
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
276
464
|
import { getValue, setValue } from "@dxos/echo-schema";
|
|
277
|
-
import { AST } from "@dxos/effect";
|
|
465
|
+
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
278
466
|
import { invariant } from "@dxos/invariant";
|
|
279
467
|
import { log } from "@dxos/log";
|
|
280
468
|
import { validateSchema } from "@dxos/schema";
|
|
@@ -289,17 +477,17 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
289
477
|
""
|
|
290
478
|
]
|
|
291
479
|
});
|
|
292
|
-
const [values, setValues] =
|
|
293
|
-
|
|
480
|
+
const [values, setValues] = useState2(initialValues);
|
|
481
|
+
useEffect2(() => {
|
|
294
482
|
setValues(initialValues);
|
|
295
483
|
}, [
|
|
296
484
|
initialValues
|
|
297
485
|
]);
|
|
298
|
-
const [touched, setTouched] =
|
|
299
|
-
const [changed, setChanged] =
|
|
300
|
-
const [errors, setErrors] =
|
|
301
|
-
const [saving, setSaving] =
|
|
302
|
-
const validate =
|
|
486
|
+
const [touched, setTouched] = useState2(createKeySet(initialValues, false));
|
|
487
|
+
const [changed, setChanged] = useState2(createKeySet(initialValues, false));
|
|
488
|
+
const [errors, setErrors] = useState2({});
|
|
489
|
+
const [saving, setSaving] = useState2(false);
|
|
490
|
+
const validate = useCallback2((values2) => {
|
|
303
491
|
let errors2 = validateSchema(schema, values2) ?? [];
|
|
304
492
|
if (errors2.length === 0 && onValidate) {
|
|
305
493
|
const validatedValues = values2;
|
|
@@ -312,11 +500,11 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
312
500
|
schema,
|
|
313
501
|
onValidate
|
|
314
502
|
]);
|
|
315
|
-
const [schemaDirty, setSchemaDirty] =
|
|
316
|
-
|
|
503
|
+
const [schemaDirty, setSchemaDirty] = useState2(false);
|
|
504
|
+
useEffect2(() => setSchemaDirty(true), [
|
|
317
505
|
schema
|
|
318
506
|
]);
|
|
319
|
-
|
|
507
|
+
useEffect2(() => {
|
|
320
508
|
if (schemaDirty) {
|
|
321
509
|
validate(values);
|
|
322
510
|
setSchemaDirty(false);
|
|
@@ -325,13 +513,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
325
513
|
values,
|
|
326
514
|
schemaDirty
|
|
327
515
|
]);
|
|
328
|
-
const canSave = useMemo(() => !saving &&
|
|
329
|
-
|
|
516
|
+
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
517
|
+
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
330
518
|
touched,
|
|
331
519
|
errors,
|
|
332
520
|
saving
|
|
333
521
|
]);
|
|
334
|
-
const handleSave =
|
|
522
|
+
const handleSave = useCallback2(async () => {
|
|
335
523
|
if (validate(values)) {
|
|
336
524
|
setSaving(true);
|
|
337
525
|
try {
|
|
@@ -348,17 +536,21 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
348
536
|
onSave,
|
|
349
537
|
changed
|
|
350
538
|
]);
|
|
351
|
-
const getStatus =
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
539
|
+
const getStatus = useCallback2((path) => {
|
|
540
|
+
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
541
|
+
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
542
|
+
return {
|
|
543
|
+
status: matchingError ? "error" : void 0,
|
|
544
|
+
error: matchingError ? matchingError[1] : void 0
|
|
545
|
+
};
|
|
546
|
+
}, [
|
|
356
547
|
errors
|
|
357
548
|
]);
|
|
358
|
-
const getFormValue = (
|
|
359
|
-
return getValue(values,
|
|
549
|
+
const getFormValue = (path) => {
|
|
550
|
+
return getValue(values, createJsonPath(path));
|
|
360
551
|
};
|
|
361
|
-
const onValueChange = (
|
|
552
|
+
const onValueChange = (path, type, value) => {
|
|
553
|
+
const jsonPath = createJsonPath(path);
|
|
362
554
|
let parsedValue = value;
|
|
363
555
|
try {
|
|
364
556
|
if (type === "number") {
|
|
@@ -367,19 +559,19 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
367
559
|
} catch (err) {
|
|
368
560
|
log.catch(err, void 0, {
|
|
369
561
|
F: __dxlog_file,
|
|
370
|
-
L:
|
|
562
|
+
L: 193,
|
|
371
563
|
S: void 0,
|
|
372
564
|
C: (f, a) => f(...a)
|
|
373
565
|
});
|
|
374
566
|
parsedValue = void 0;
|
|
375
567
|
}
|
|
376
568
|
const newValues = {
|
|
377
|
-
...setValue(values,
|
|
569
|
+
...setValue(values, jsonPath, parsedValue)
|
|
378
570
|
};
|
|
379
571
|
setValues(newValues);
|
|
380
572
|
setChanged((prev) => ({
|
|
381
573
|
...prev,
|
|
382
|
-
[
|
|
574
|
+
[jsonPath]: true
|
|
383
575
|
}));
|
|
384
576
|
onValuesChanged?.(newValues);
|
|
385
577
|
const isValid = validate(newValues);
|
|
@@ -389,15 +581,12 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
389
581
|
});
|
|
390
582
|
}
|
|
391
583
|
};
|
|
392
|
-
const
|
|
393
|
-
const
|
|
584
|
+
const onTouched = useCallback2((path) => {
|
|
585
|
+
const jsonPath = createJsonPath(path);
|
|
394
586
|
setTouched((touched2) => ({
|
|
395
587
|
...touched2,
|
|
396
|
-
[
|
|
588
|
+
[jsonPath]: true
|
|
397
589
|
}));
|
|
398
|
-
if (event.relatedTarget?.getAttribute("type") === "submit") {
|
|
399
|
-
setTouched(createKeySet(values, true));
|
|
400
|
-
}
|
|
401
590
|
validate(values);
|
|
402
591
|
}, [
|
|
403
592
|
validate,
|
|
@@ -415,13 +604,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
415
604
|
getStatus,
|
|
416
605
|
getValue: getFormValue,
|
|
417
606
|
onValueChange,
|
|
418
|
-
|
|
607
|
+
onTouched
|
|
419
608
|
};
|
|
420
609
|
};
|
|
421
610
|
var createKeySet = (obj, value) => {
|
|
422
611
|
invariant(obj, void 0, {
|
|
423
612
|
F: __dxlog_file,
|
|
424
|
-
L:
|
|
613
|
+
L: 235,
|
|
425
614
|
S: void 0,
|
|
426
615
|
A: [
|
|
427
616
|
"obj",
|
|
@@ -435,338 +624,392 @@ var createKeySet = (obj, value) => {
|
|
|
435
624
|
};
|
|
436
625
|
var flatMap = (errors) => {
|
|
437
626
|
return errors.reduce((result, { path, message }) => {
|
|
438
|
-
|
|
439
|
-
|
|
627
|
+
const jsonPath = fromEffectValidationPath(path);
|
|
628
|
+
if (!(jsonPath in result)) {
|
|
629
|
+
result[jsonPath] = message;
|
|
440
630
|
}
|
|
441
631
|
return result;
|
|
442
632
|
}, {});
|
|
443
633
|
};
|
|
444
634
|
|
|
445
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
446
|
-
var
|
|
635
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
636
|
+
var FormContext = /* @__PURE__ */ createContext(void 0);
|
|
637
|
+
var useFormContext = () => {
|
|
638
|
+
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
639
|
+
};
|
|
640
|
+
var useFormValues = (path = []) => {
|
|
641
|
+
const { values: formValues } = useFormContext();
|
|
642
|
+
const jsonPath = createJsonPath2(path);
|
|
643
|
+
return getValue2(formValues, jsonPath);
|
|
644
|
+
};
|
|
645
|
+
var useInputProps = (path = []) => {
|
|
646
|
+
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
647
|
+
return {
|
|
648
|
+
getStatus: () => getStatus(path),
|
|
649
|
+
getValue: () => getFormValue(path),
|
|
650
|
+
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
651
|
+
onBlur: () => onTouched(path)
|
|
652
|
+
};
|
|
653
|
+
};
|
|
654
|
+
var FormProvider = ({ children, ...formOptions }) => {
|
|
655
|
+
const formHandler = useForm(formOptions);
|
|
656
|
+
return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
|
|
657
|
+
value: formHandler
|
|
658
|
+
}, children);
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
662
|
+
var FormActions = ({ onCancel, readonly }) => {
|
|
663
|
+
const { t } = useTranslation3(translationKey);
|
|
664
|
+
const { canSave, handleSave } = useFormContext();
|
|
665
|
+
return /* @__PURE__ */ React8.createElement("div", {
|
|
666
|
+
role: "none",
|
|
667
|
+
className: "flex justify-center"
|
|
668
|
+
}, /* @__PURE__ */ React8.createElement("div", {
|
|
669
|
+
role: "none",
|
|
670
|
+
className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
671
|
+
}, onCancel && !readonly && /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
672
|
+
"data-testid": "cancel-button",
|
|
673
|
+
icon: "ph--x--regular",
|
|
674
|
+
label: t("button cancel"),
|
|
675
|
+
onClick: onCancel
|
|
676
|
+
}), handleSave && /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
677
|
+
type: "submit",
|
|
678
|
+
"data-testid": "save-button",
|
|
679
|
+
disabled: !canSave,
|
|
680
|
+
icon: "ph--check--regular",
|
|
681
|
+
label: t("button save"),
|
|
682
|
+
onClick: handleSave
|
|
683
|
+
})));
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
687
|
+
import { pipe as pipe2 } from "effect";
|
|
688
|
+
import { capitalize as capitalize2 } from "effect/String";
|
|
689
|
+
import React10, { useMemo as useMemo3 } from "react";
|
|
690
|
+
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
691
|
+
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
692
|
+
import { mx as mx6 } from "@dxos/react-ui-theme";
|
|
693
|
+
import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
|
|
694
|
+
import { isNotFalsy } from "@dxos/util";
|
|
695
|
+
|
|
696
|
+
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
697
|
+
import { pipe } from "effect";
|
|
698
|
+
import { capitalize } from "effect/String";
|
|
699
|
+
import React9, { useCallback as useCallback3, useMemo as useMemo2 } from "react";
|
|
700
|
+
import { AST as AST2 } from "@dxos/echo-schema";
|
|
701
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
702
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
703
|
+
import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
|
|
704
|
+
import { mx as mx5 } from "@dxos/react-ui-theme";
|
|
705
|
+
import { getSchemaProperties } from "@dxos/schema";
|
|
706
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
|
|
447
707
|
var padding = "px-2";
|
|
448
|
-
var
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
708
|
+
var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
|
|
709
|
+
const { t } = useTranslation4(translationKey);
|
|
710
|
+
const { ast, name, type, title } = property;
|
|
711
|
+
const values = useFormValues(path ?? []);
|
|
712
|
+
invariant2(Array.isArray(values), `Values at path ${path?.join(".")} must be an array.`, {
|
|
713
|
+
F: __dxlog_file2,
|
|
714
|
+
L: 37,
|
|
715
|
+
S: void 0,
|
|
716
|
+
A: [
|
|
717
|
+
"Array.isArray(values)",
|
|
718
|
+
"`Values at path ${path?.join('.')} must be an array.`"
|
|
719
|
+
]
|
|
460
720
|
});
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
721
|
+
const label = title ?? pipe(name, capitalize);
|
|
722
|
+
const tupleType = findNode(ast, AST2.isTupleType);
|
|
723
|
+
const elementType = tupleType?.rest[0]?.type;
|
|
724
|
+
const getDefaultObjectValue = (typeNode) => {
|
|
725
|
+
const baseNode = findNode(typeNode, isDiscriminatedUnion);
|
|
726
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST2.isTypeLiteral);
|
|
727
|
+
if (!typeLiteral) {
|
|
728
|
+
return {};
|
|
729
|
+
}
|
|
730
|
+
return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [
|
|
731
|
+
prop.name,
|
|
732
|
+
prop.defaultValue
|
|
733
|
+
]));
|
|
734
|
+
};
|
|
735
|
+
const newValue = useMemo2(() => {
|
|
736
|
+
if (type === "object" && elementType) {
|
|
737
|
+
return getDefaultObjectValue(elementType);
|
|
738
|
+
} else {
|
|
739
|
+
return SimpleType.getDefaultValue(type);
|
|
740
|
+
}
|
|
469
741
|
}, [
|
|
470
|
-
|
|
471
|
-
|
|
742
|
+
type,
|
|
743
|
+
elementType
|
|
744
|
+
]);
|
|
745
|
+
const handleAdd = useCallback3(() => {
|
|
746
|
+
inputProps.onValueChange(type, [
|
|
747
|
+
...values,
|
|
748
|
+
newValue
|
|
749
|
+
]);
|
|
750
|
+
}, [
|
|
751
|
+
type,
|
|
752
|
+
elementType,
|
|
753
|
+
inputProps,
|
|
754
|
+
newValue,
|
|
472
755
|
values
|
|
473
756
|
]);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
errors
|
|
478
|
-
}, {
|
|
479
|
-
F: __dxlog_file2,
|
|
480
|
-
L: 106,
|
|
481
|
-
S: void 0,
|
|
482
|
-
C: (f, a) => f(...a)
|
|
483
|
-
});
|
|
484
|
-
}
|
|
757
|
+
const handleRemove = useCallback3((index) => {
|
|
758
|
+
const newValues = values.filter((_, i) => i !== index);
|
|
759
|
+
inputProps.onValueChange(type, newValues);
|
|
485
760
|
}, [
|
|
486
|
-
|
|
761
|
+
type,
|
|
762
|
+
inputProps,
|
|
763
|
+
values
|
|
487
764
|
]);
|
|
488
|
-
|
|
489
|
-
role: "form",
|
|
490
|
-
className: mx2("flex flex-col w-full gap-2 py-2", classNames),
|
|
491
|
-
"data-testid": testId
|
|
492
|
-
}, properties.map((property) => {
|
|
493
|
-
const { ast, name, type, format, title, description, examples, options, array } = property;
|
|
494
|
-
const key = [
|
|
495
|
-
...path,
|
|
496
|
-
name
|
|
497
|
-
];
|
|
498
|
-
const label = title ?? pipe(name, capitalize);
|
|
499
|
-
const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
|
|
500
|
-
const FoundComponent = lookupComponent?.({
|
|
501
|
-
prop: name,
|
|
502
|
-
schema: S.make(ast),
|
|
503
|
-
inputProps: {
|
|
504
|
-
property: name,
|
|
505
|
-
type,
|
|
506
|
-
format,
|
|
507
|
-
label,
|
|
508
|
-
disabled: readonly,
|
|
509
|
-
placeholder,
|
|
510
|
-
...inputProps
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
if (FoundComponent) {
|
|
514
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
515
|
-
key: name,
|
|
516
|
-
role: "none",
|
|
517
|
-
className: padding
|
|
518
|
-
}, FoundComponent);
|
|
519
|
-
}
|
|
520
|
-
let InputComponent = Custom?.[key.join(".")];
|
|
521
|
-
if (!InputComponent) {
|
|
522
|
-
if (options) {
|
|
523
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
524
|
-
key: name,
|
|
525
|
-
role: "none",
|
|
526
|
-
className: padding
|
|
527
|
-
}, /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
528
|
-
type,
|
|
529
|
-
format,
|
|
530
|
-
property: name,
|
|
531
|
-
disabled: readonly,
|
|
532
|
-
label,
|
|
533
|
-
options: options.map((option) => ({
|
|
534
|
-
value: option,
|
|
535
|
-
label: String(option)
|
|
536
|
-
})),
|
|
537
|
-
placeholder,
|
|
538
|
-
...inputProps
|
|
539
|
-
}));
|
|
540
|
-
}
|
|
541
|
-
InputComponent = getInputComponent(type, format);
|
|
542
|
-
if (array) {
|
|
543
|
-
return /* @__PURE__ */ React6.createElement(ArrayField, {
|
|
544
|
-
key: name,
|
|
545
|
-
name,
|
|
546
|
-
type,
|
|
547
|
-
array: true,
|
|
548
|
-
label,
|
|
549
|
-
ast,
|
|
550
|
-
values,
|
|
551
|
-
format,
|
|
552
|
-
readonly,
|
|
553
|
-
placeholder,
|
|
554
|
-
inputProps,
|
|
555
|
-
Custom
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
if (!InputComponent) {
|
|
560
|
-
if (type === "object") {
|
|
561
|
-
const baseNode = findNode(ast, isDiscriminatedUnion);
|
|
562
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(ast, AST2.isTypeLiteral);
|
|
563
|
-
if (typeLiteral) {
|
|
564
|
-
const schema2 = S.make(typeLiteral);
|
|
565
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
566
|
-
key: name,
|
|
567
|
-
role: "none"
|
|
568
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
569
|
-
className: padding
|
|
570
|
-
}, label), /* @__PURE__ */ React6.createElement(Form, {
|
|
571
|
-
schema: schema2,
|
|
572
|
-
path: key,
|
|
573
|
-
values: values[name] ?? {},
|
|
574
|
-
onValuesChanged: (childValues) => {
|
|
575
|
-
inputProps.onValueChange(name, "object", childValues);
|
|
576
|
-
},
|
|
577
|
-
Custom
|
|
578
|
-
}));
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
log2("no renderer for property", {
|
|
582
|
-
name,
|
|
583
|
-
type
|
|
584
|
-
}, {
|
|
585
|
-
F: __dxlog_file2,
|
|
586
|
-
L: 213,
|
|
587
|
-
S: void 0,
|
|
588
|
-
C: (f, a) => f(...a)
|
|
589
|
-
});
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
593
|
-
key: name,
|
|
594
|
-
role: "none",
|
|
595
|
-
className: padding
|
|
596
|
-
}, /* @__PURE__ */ React6.createElement(InputComponent, {
|
|
597
|
-
type,
|
|
598
|
-
format,
|
|
599
|
-
property: name,
|
|
600
|
-
disabled: readonly,
|
|
601
|
-
label,
|
|
602
|
-
placeholder,
|
|
603
|
-
...inputProps
|
|
604
|
-
}));
|
|
605
|
-
}).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React6.createElement(FormActions, {
|
|
606
|
-
onCancel,
|
|
607
|
-
onSubmit: handleSave,
|
|
608
|
-
canSubmit: canSave,
|
|
609
|
-
readonly
|
|
610
|
-
}));
|
|
611
|
-
};
|
|
612
|
-
var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
|
|
613
|
-
const { t } = useTranslation2(translationKey);
|
|
614
|
-
const arrayValues = values[name] ?? [];
|
|
615
|
-
const key = [
|
|
616
|
-
name
|
|
617
|
-
];
|
|
618
|
-
const tupleType = findNode(ast, AST2.isTupleType);
|
|
619
|
-
const elementType = tupleType?.rest[0]?.type;
|
|
620
|
-
if (type === "object" && elementType) {
|
|
621
|
-
const baseNode = findNode(elementType, isDiscriminatedUnion);
|
|
622
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST2.isTypeLiteral);
|
|
623
|
-
if (typeLiteral) {
|
|
624
|
-
const schema = S.make(typeLiteral);
|
|
625
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
626
|
-
key: name,
|
|
627
|
-
role: "none",
|
|
628
|
-
className: mx2(padding)
|
|
629
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
630
|
-
role: "none",
|
|
631
|
-
className: "flex flex-col gap-1"
|
|
632
|
-
}, arrayValues.map((value, index) => {
|
|
633
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
634
|
-
key: index,
|
|
635
|
-
role: "none",
|
|
636
|
-
className: "flex items-center gap-1"
|
|
637
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
638
|
-
role: "none",
|
|
639
|
-
className: "flex-1"
|
|
640
|
-
}, /* @__PURE__ */ React6.createElement(Form, {
|
|
641
|
-
schema,
|
|
642
|
-
path: [
|
|
643
|
-
...key,
|
|
644
|
-
`[${index}]`
|
|
645
|
-
],
|
|
646
|
-
values: value,
|
|
647
|
-
onValuesChanged: (childValues) => {
|
|
648
|
-
const newValues = [
|
|
649
|
-
...arrayValues
|
|
650
|
-
];
|
|
651
|
-
newValues[index] = childValues;
|
|
652
|
-
inputProps.onValueChange(name, "object", newValues);
|
|
653
|
-
},
|
|
654
|
-
Custom
|
|
655
|
-
})), /* @__PURE__ */ React6.createElement(IconButton, {
|
|
656
|
-
icon: "ph--trash--regular",
|
|
657
|
-
iconOnly: true,
|
|
658
|
-
label: t("button remove"),
|
|
659
|
-
onClick: () => {
|
|
660
|
-
const newValues = arrayValues.filter((_, i) => i !== index);
|
|
661
|
-
inputProps.onValueChange(name, type, newValues);
|
|
662
|
-
}
|
|
663
|
-
}));
|
|
664
|
-
})), /* @__PURE__ */ React6.createElement("div", {
|
|
665
|
-
role: "none",
|
|
666
|
-
className: "flex justify-between items-center plb-1"
|
|
667
|
-
}, /* @__PURE__ */ React6.createElement(IconButton, {
|
|
668
|
-
icon: "ph--plus--regular",
|
|
669
|
-
iconOnly: true,
|
|
670
|
-
label: t("button add"),
|
|
671
|
-
onClick: () => {
|
|
672
|
-
const newValues = [
|
|
673
|
-
...arrayValues,
|
|
674
|
-
{}
|
|
675
|
-
];
|
|
676
|
-
inputProps.onValueChange(name, type, newValues);
|
|
677
|
-
}
|
|
678
|
-
})));
|
|
679
|
-
}
|
|
680
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
681
|
-
key: name,
|
|
682
|
-
role: "none"
|
|
683
|
-
}, "Nested form not supported in arrays, yet.");
|
|
684
|
-
}
|
|
685
|
-
const InputComponent = getInputComponent(type, format);
|
|
686
|
-
if (!InputComponent) {
|
|
765
|
+
if (!elementType) {
|
|
687
766
|
return null;
|
|
688
767
|
}
|
|
689
|
-
return /* @__PURE__ */
|
|
768
|
+
return /* @__PURE__ */ React9.createElement("div", {
|
|
690
769
|
role: "none",
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
770
|
+
className: mx5(padding)
|
|
771
|
+
}, /* @__PURE__ */ React9.createElement(InputHeader, null, label), /* @__PURE__ */ React9.createElement("div", {
|
|
694
772
|
role: "none",
|
|
695
773
|
className: "flex flex-col gap-1"
|
|
696
|
-
},
|
|
774
|
+
}, values.map((_value, index) => /* @__PURE__ */ React9.createElement("div", {
|
|
697
775
|
key: index,
|
|
698
776
|
role: "none",
|
|
699
|
-
className: "flex items-
|
|
700
|
-
}, /* @__PURE__ */
|
|
777
|
+
className: "flex items-center gap-1"
|
|
778
|
+
}, /* @__PURE__ */ React9.createElement("div", {
|
|
701
779
|
role: "none",
|
|
702
780
|
className: "flex-1"
|
|
703
|
-
}, /* @__PURE__ */
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
781
|
+
}, /* @__PURE__ */ React9.createElement(FormField, {
|
|
782
|
+
property: {
|
|
783
|
+
...property,
|
|
784
|
+
array: false,
|
|
785
|
+
ast: elementType
|
|
786
|
+
},
|
|
787
|
+
path: [
|
|
788
|
+
...path ?? [],
|
|
789
|
+
index
|
|
790
|
+
],
|
|
791
|
+
readonly,
|
|
792
|
+
inline: true,
|
|
793
|
+
Custom,
|
|
794
|
+
lookupComponent
|
|
795
|
+
})), /* @__PURE__ */ React9.createElement(IconButton3, {
|
|
713
796
|
icon: "ph--trash--regular",
|
|
714
797
|
iconOnly: true,
|
|
715
798
|
label: t("button remove"),
|
|
716
|
-
onClick: () =>
|
|
717
|
-
|
|
718
|
-
inputProps.onValueChange(name, type, newValues);
|
|
719
|
-
}
|
|
720
|
-
})))), /* @__PURE__ */ React6.createElement("div", {
|
|
799
|
+
onClick: () => handleRemove(index)
|
|
800
|
+
})))), /* @__PURE__ */ React9.createElement("div", {
|
|
721
801
|
role: "none",
|
|
722
802
|
className: "flex justify-between items-center plb-1"
|
|
723
|
-
}, /* @__PURE__ */
|
|
803
|
+
}, /* @__PURE__ */ React9.createElement(IconButton3, {
|
|
724
804
|
icon: "ph--plus--regular",
|
|
725
805
|
iconOnly: true,
|
|
726
806
|
label: t("button add"),
|
|
727
|
-
onClick:
|
|
728
|
-
const newValues = [
|
|
729
|
-
...arrayValues,
|
|
730
|
-
SimpleType.getDefaultValue(type)
|
|
731
|
-
];
|
|
732
|
-
inputProps.onValueChange(name, type, newValues);
|
|
733
|
-
}
|
|
807
|
+
onClick: handleAdd
|
|
734
808
|
})));
|
|
735
809
|
};
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
810
|
+
|
|
811
|
+
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
812
|
+
import { FormatEnum } from "@dxos/echo-schema";
|
|
813
|
+
var getInputComponent = (type, format) => {
|
|
814
|
+
switch (format) {
|
|
815
|
+
case FormatEnum.LatLng:
|
|
816
|
+
return GeoPointInput;
|
|
817
|
+
}
|
|
818
|
+
switch (type) {
|
|
819
|
+
case "string":
|
|
820
|
+
return TextInput;
|
|
821
|
+
case "number":
|
|
822
|
+
return NumberInput;
|
|
823
|
+
case "boolean":
|
|
824
|
+
return BooleanInput;
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
829
|
+
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
830
|
+
const inputProps = useInputProps(path);
|
|
831
|
+
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
832
|
+
const label = useMemo3(() => title ?? pipe2(name, capitalize2), [
|
|
833
|
+
title,
|
|
834
|
+
name
|
|
835
|
+
]);
|
|
836
|
+
const placeholder = useMemo3(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
837
|
+
examples,
|
|
838
|
+
description
|
|
839
|
+
]);
|
|
840
|
+
const FoundComponent = lookupComponent?.({
|
|
841
|
+
prop: name,
|
|
842
|
+
schema: S.make(ast),
|
|
843
|
+
inputProps: {
|
|
844
|
+
type,
|
|
845
|
+
format,
|
|
846
|
+
label,
|
|
847
|
+
disabled: readonly,
|
|
848
|
+
placeholder,
|
|
849
|
+
...inputProps
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
if (FoundComponent) {
|
|
853
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
854
|
+
role: "none"
|
|
855
|
+
}, FoundComponent);
|
|
856
|
+
}
|
|
857
|
+
const jsonPath = createJsonPath3(path ?? []);
|
|
858
|
+
const CustomComponent = Custom?.[jsonPath];
|
|
859
|
+
if (CustomComponent) {
|
|
860
|
+
return (
|
|
861
|
+
// TODO(ZaymonFC): We should try to remove this div wrapper.
|
|
862
|
+
/* @__PURE__ */ React10.createElement("div", {
|
|
863
|
+
role: "none"
|
|
864
|
+
}, /* @__PURE__ */ React10.createElement(CustomComponent, {
|
|
865
|
+
type,
|
|
866
|
+
format,
|
|
867
|
+
label,
|
|
868
|
+
inputOnly: inline,
|
|
869
|
+
placeholder,
|
|
870
|
+
disabled: readonly,
|
|
871
|
+
...inputProps
|
|
872
|
+
}))
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
if (array) {
|
|
876
|
+
return /* @__PURE__ */ React10.createElement(ArrayField, {
|
|
877
|
+
property,
|
|
878
|
+
path,
|
|
879
|
+
inputProps,
|
|
880
|
+
readonly,
|
|
881
|
+
Custom
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
const InputComponent = getInputComponent(type, format);
|
|
885
|
+
if (InputComponent) {
|
|
886
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
887
|
+
role: "none"
|
|
888
|
+
}, /* @__PURE__ */ React10.createElement(InputComponent, {
|
|
889
|
+
type,
|
|
890
|
+
format,
|
|
891
|
+
label,
|
|
892
|
+
inputOnly: inline,
|
|
893
|
+
placeholder,
|
|
894
|
+
disabled: readonly,
|
|
895
|
+
...inputProps
|
|
896
|
+
}));
|
|
897
|
+
}
|
|
898
|
+
if (options) {
|
|
899
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
900
|
+
role: "none"
|
|
901
|
+
}, /* @__PURE__ */ React10.createElement(SelectInput, {
|
|
902
|
+
type,
|
|
903
|
+
format,
|
|
904
|
+
disabled: readonly,
|
|
905
|
+
inputOnly: inline,
|
|
906
|
+
label,
|
|
907
|
+
options: options.map((option) => ({
|
|
908
|
+
value: option,
|
|
909
|
+
label: String(option)
|
|
910
|
+
})),
|
|
911
|
+
placeholder,
|
|
912
|
+
...inputProps
|
|
913
|
+
}));
|
|
914
|
+
}
|
|
915
|
+
if (type === "object") {
|
|
916
|
+
const baseNode = findNode2(ast, isDiscriminatedUnion2);
|
|
917
|
+
const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
|
|
918
|
+
if (typeLiteral) {
|
|
919
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
920
|
+
role: "none"
|
|
921
|
+
}, !inline && /* @__PURE__ */ React10.createElement("div", null, label), /* @__PURE__ */ React10.createElement(FormFields, {
|
|
922
|
+
schema: S.make(typeLiteral),
|
|
923
|
+
path,
|
|
924
|
+
readonly,
|
|
925
|
+
Custom
|
|
926
|
+
}));
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return null;
|
|
930
|
+
};
|
|
931
|
+
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
932
|
+
const values = useFormValues(path);
|
|
933
|
+
const properties = useMemo3(() => {
|
|
934
|
+
const props = getSchemaProperties2(schema.ast, values);
|
|
935
|
+
const filtered = filter ? filter(props) : props;
|
|
936
|
+
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
937
|
+
}, [
|
|
938
|
+
schema,
|
|
939
|
+
values,
|
|
940
|
+
filter,
|
|
941
|
+
sort
|
|
942
|
+
]);
|
|
943
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
944
|
+
role: "form",
|
|
945
|
+
className: mx6("flex flex-col w-full gap-2 py-2")
|
|
946
|
+
}, properties.map((property) => {
|
|
947
|
+
return /* @__PURE__ */ React10.createElement(FormField, {
|
|
948
|
+
key: property.name,
|
|
949
|
+
property,
|
|
950
|
+
path: [
|
|
951
|
+
...path ?? [],
|
|
952
|
+
property.name
|
|
953
|
+
],
|
|
954
|
+
readonly,
|
|
955
|
+
lookupComponent,
|
|
956
|
+
Custom
|
|
957
|
+
});
|
|
958
|
+
}).filter(isNotFalsy));
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
962
|
+
var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
963
|
+
const onValid = useMemo4(() => autoSave ? onSave : void 0, [
|
|
964
|
+
autoSave,
|
|
965
|
+
onSave
|
|
966
|
+
]);
|
|
967
|
+
useEffect3(() => {
|
|
968
|
+
if (autoFocus) {
|
|
969
|
+
const input = document.querySelector("input");
|
|
970
|
+
if (input) {
|
|
971
|
+
input.focus();
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}, [
|
|
975
|
+
autoFocus
|
|
976
|
+
]);
|
|
977
|
+
return /* @__PURE__ */ React11.createElement(FormProvider, {
|
|
978
|
+
schema,
|
|
979
|
+
initialValues,
|
|
980
|
+
onValuesChanged,
|
|
981
|
+
onValidate,
|
|
982
|
+
onValid,
|
|
983
|
+
onSave
|
|
984
|
+
}, /* @__PURE__ */ React11.createElement("div", {
|
|
742
985
|
role: "none",
|
|
743
|
-
className:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
986
|
+
className: mx7("p-2", classNames),
|
|
987
|
+
"data-testid": testId
|
|
988
|
+
}, /* @__PURE__ */ React11.createElement(FormFields, {
|
|
989
|
+
schema,
|
|
990
|
+
path,
|
|
991
|
+
readonly,
|
|
992
|
+
filter,
|
|
993
|
+
sort,
|
|
994
|
+
lookupComponent,
|
|
995
|
+
Custom
|
|
996
|
+
}), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React11.createElement(FormActions, {
|
|
997
|
+
onCancel,
|
|
998
|
+
readonly
|
|
756
999
|
})));
|
|
757
1000
|
};
|
|
758
1001
|
|
|
759
1002
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
760
1003
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
761
1004
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
762
|
-
const { t } =
|
|
763
|
-
const [props, setProps] =
|
|
764
|
-
|
|
1005
|
+
const { t } = useTranslation5(translationKey);
|
|
1006
|
+
const [props, setProps] = useState3(projection.getFieldProjection(field.id).props);
|
|
1007
|
+
useEffect4(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
765
1008
|
field,
|
|
766
1009
|
projection
|
|
767
1010
|
]);
|
|
768
|
-
const [schemas, setSchemas] =
|
|
769
|
-
|
|
1011
|
+
const [schemas, setSchemas] = useState3([]);
|
|
1012
|
+
useEffect4(() => {
|
|
770
1013
|
if (!registry) {
|
|
771
1014
|
return;
|
|
772
1015
|
}
|
|
@@ -784,17 +1027,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
784
1027
|
}, [
|
|
785
1028
|
registry
|
|
786
1029
|
]);
|
|
787
|
-
const [referenceSchema, setReferenceSchema] =
|
|
788
|
-
|
|
1030
|
+
const [referenceSchema, setReferenceSchema] = useState3();
|
|
1031
|
+
useEffect4(() => {
|
|
789
1032
|
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
790
1033
|
}, [
|
|
791
1034
|
schemas,
|
|
792
1035
|
props?.referenceSchema
|
|
793
1036
|
]);
|
|
794
|
-
const [{ fieldSchema }, setFieldSchema] =
|
|
1037
|
+
const [{ fieldSchema }, setFieldSchema] = useState3({
|
|
795
1038
|
fieldSchema: getFormatSchema(props?.format)
|
|
796
1039
|
});
|
|
797
|
-
const handleValuesChanged =
|
|
1040
|
+
const handleValuesChanged = useCallback4((_props) => {
|
|
798
1041
|
setFieldSchema((prev) => {
|
|
799
1042
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
800
1043
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -822,14 +1065,14 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
822
1065
|
type
|
|
823
1066
|
};
|
|
824
1067
|
}
|
|
825
|
-
return
|
|
1068
|
+
return _props;
|
|
826
1069
|
});
|
|
827
1070
|
}, [
|
|
828
1071
|
schemas,
|
|
829
1072
|
props.format,
|
|
830
1073
|
props.referenceSchema
|
|
831
1074
|
]);
|
|
832
|
-
const handleValidate =
|
|
1075
|
+
const handleValidate = useCallback4(({ property }) => {
|
|
833
1076
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
834
1077
|
return [
|
|
835
1078
|
{
|
|
@@ -842,7 +1085,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
842
1085
|
view.fields,
|
|
843
1086
|
field
|
|
844
1087
|
]);
|
|
845
|
-
const handleSave =
|
|
1088
|
+
const handleSave = useCallback4((props2) => {
|
|
846
1089
|
projection.setFieldProjection({
|
|
847
1090
|
field,
|
|
848
1091
|
props: props2
|
|
@@ -853,50 +1096,51 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
853
1096
|
field,
|
|
854
1097
|
onSave
|
|
855
1098
|
]);
|
|
856
|
-
const handleCancel =
|
|
1099
|
+
const handleCancel = useCallback4(() => {
|
|
857
1100
|
requestAnimationFrame(() => onCancel?.());
|
|
858
1101
|
onSave();
|
|
859
1102
|
}, [
|
|
860
1103
|
onSave
|
|
861
1104
|
]);
|
|
862
|
-
const Custom =
|
|
863
|
-
["format"]: (props2) => /* @__PURE__ */
|
|
1105
|
+
const Custom = useMemo5(() => ({
|
|
1106
|
+
["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
864
1107
|
...props2,
|
|
865
1108
|
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
866
1109
|
value,
|
|
867
1110
|
label: t(`format ${value}`)
|
|
868
1111
|
}))
|
|
869
1112
|
}),
|
|
870
|
-
["referenceSchema"]: (props2) => /* @__PURE__ */
|
|
1113
|
+
["referenceSchema"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
871
1114
|
...props2,
|
|
872
1115
|
options: schemas.map((schema) => ({
|
|
873
1116
|
value: schema.typename
|
|
874
1117
|
}))
|
|
875
1118
|
}),
|
|
876
|
-
["referencePath"]: (props2) => /* @__PURE__ */
|
|
1119
|
+
["referencePath"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
877
1120
|
...props2,
|
|
878
|
-
options: referenceSchema ?
|
|
1121
|
+
options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
|
|
879
1122
|
value: p.name
|
|
880
1123
|
})) : []
|
|
881
|
-
})
|
|
1124
|
+
}),
|
|
1125
|
+
["options"]: (props2) => /* @__PURE__ */ React12.createElement(SelectOptionInput, props2)
|
|
882
1126
|
}), [
|
|
883
1127
|
t,
|
|
884
1128
|
schemas,
|
|
885
1129
|
referenceSchema
|
|
886
1130
|
]);
|
|
887
|
-
const propIsNotType =
|
|
1131
|
+
const propIsNotType = useCallback4((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
888
1132
|
if (!fieldSchema) {
|
|
889
|
-
|
|
1133
|
+
log2.warn("invalid format", {
|
|
890
1134
|
props
|
|
891
1135
|
}, {
|
|
892
1136
|
F: __dxlog_file3,
|
|
893
|
-
L:
|
|
1137
|
+
L: 174,
|
|
894
1138
|
S: void 0,
|
|
895
1139
|
C: (f, a) => f(...a)
|
|
896
1140
|
});
|
|
897
1141
|
return null;
|
|
898
1142
|
}
|
|
899
|
-
return /* @__PURE__ */
|
|
1143
|
+
return /* @__PURE__ */ React12.createElement(Form, {
|
|
900
1144
|
key: field.id,
|
|
901
1145
|
autoFocus: true,
|
|
902
1146
|
values: props,
|
|
@@ -915,30 +1159,29 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
915
1159
|
};
|
|
916
1160
|
|
|
917
1161
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
918
|
-
import
|
|
919
|
-
import { AST as
|
|
920
|
-
import { IconButton as
|
|
921
|
-
import { List } from "@dxos/react-ui-list";
|
|
922
|
-
import { ghostHover, inputTextLabel, mx as
|
|
1162
|
+
import React13, { useCallback as useCallback5, useMemo as useMemo6, useState as useState4 } from "react";
|
|
1163
|
+
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
1164
|
+
import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
|
|
1165
|
+
import { List as List2 } from "@dxos/react-ui-list";
|
|
1166
|
+
import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
|
|
923
1167
|
import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
924
|
-
import { arrayMove } from "@dxos/util";
|
|
925
1168
|
var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
|
|
926
1169
|
var ViewMetaSchema = S2.Struct({
|
|
927
1170
|
name: S2.String.annotations({
|
|
928
|
-
[
|
|
1171
|
+
[AST4.TitleAnnotationId]: "View"
|
|
929
1172
|
}),
|
|
930
1173
|
typename: Format.DXN.annotations({
|
|
931
|
-
[
|
|
1174
|
+
[AST4.TitleAnnotationId]: "Typename"
|
|
932
1175
|
})
|
|
933
1176
|
}).pipe(S2.mutable);
|
|
934
1177
|
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
935
|
-
const { t } =
|
|
936
|
-
const projection =
|
|
1178
|
+
const { t } = useTranslation6(translationKey);
|
|
1179
|
+
const projection = useMemo6(() => new ViewProjection(schema, view), [
|
|
937
1180
|
schema,
|
|
938
1181
|
view
|
|
939
1182
|
]);
|
|
940
|
-
const [field, setField] =
|
|
941
|
-
const viewValues =
|
|
1183
|
+
const [field, setField] = useState4();
|
|
1184
|
+
const viewValues = useMemo6(() => {
|
|
942
1185
|
return {
|
|
943
1186
|
name: view.name,
|
|
944
1187
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -948,7 +1191,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
948
1191
|
}, [
|
|
949
1192
|
view
|
|
950
1193
|
]);
|
|
951
|
-
const handleViewUpdate =
|
|
1194
|
+
const handleViewUpdate = useCallback5(({ name, typename }) => {
|
|
952
1195
|
requestAnimationFrame(() => {
|
|
953
1196
|
if (view.name !== name) {
|
|
954
1197
|
view.name = name;
|
|
@@ -962,67 +1205,72 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
962
1205
|
schema,
|
|
963
1206
|
onTypenameChanged
|
|
964
1207
|
]);
|
|
965
|
-
const handleSelect =
|
|
1208
|
+
const handleSelect = useCallback5((field2) => {
|
|
966
1209
|
setField((f) => f === field2 ? void 0 : field2);
|
|
967
1210
|
}, []);
|
|
968
|
-
const handleAdd =
|
|
1211
|
+
const handleAdd = useCallback5(() => {
|
|
969
1212
|
const field2 = projection.createFieldProjection();
|
|
970
1213
|
setField(field2);
|
|
971
1214
|
}, [
|
|
972
1215
|
view
|
|
973
1216
|
]);
|
|
974
|
-
const handleMove =
|
|
975
|
-
|
|
1217
|
+
const handleMove = useCallback5((fromIndex, toIndex) => {
|
|
1218
|
+
const fields = [
|
|
1219
|
+
...view.fields
|
|
1220
|
+
];
|
|
1221
|
+
const [moved] = fields.splice(fromIndex, 1);
|
|
1222
|
+
fields.splice(toIndex, 0, moved);
|
|
1223
|
+
view.fields = fields;
|
|
976
1224
|
}, [
|
|
977
1225
|
view.fields
|
|
978
1226
|
]);
|
|
979
|
-
const handleClose =
|
|
980
|
-
return /* @__PURE__ */
|
|
1227
|
+
const handleClose = useCallback5(() => setField(void 0), []);
|
|
1228
|
+
return /* @__PURE__ */ React13.createElement("div", {
|
|
981
1229
|
role: "none",
|
|
982
|
-
className:
|
|
983
|
-
}, /* @__PURE__ */
|
|
1230
|
+
className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
|
|
1231
|
+
}, /* @__PURE__ */ React13.createElement(Form, {
|
|
984
1232
|
autoFocus: true,
|
|
985
1233
|
autoSave: true,
|
|
986
1234
|
schema: ViewMetaSchema,
|
|
987
1235
|
values: viewValues,
|
|
988
1236
|
onSave: handleViewUpdate
|
|
989
|
-
}), /* @__PURE__ */
|
|
1237
|
+
}), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
|
|
990
1238
|
role: "none",
|
|
991
1239
|
className: "p-2"
|
|
992
|
-
}, /* @__PURE__ */
|
|
993
|
-
className:
|
|
994
|
-
}, t("fields label"))), /* @__PURE__ */
|
|
1240
|
+
}, /* @__PURE__ */ React13.createElement("label", {
|
|
1241
|
+
className: mx8(inputTextLabel)
|
|
1242
|
+
}, t("fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
|
|
995
1243
|
items: view.fields,
|
|
996
1244
|
isItem: S2.is(FieldSchema),
|
|
997
1245
|
getId: (field2) => field2.id,
|
|
998
1246
|
onMove: handleMove
|
|
999
|
-
}, ({ items: fields }) => /* @__PURE__ */
|
|
1247
|
+
}, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
|
|
1000
1248
|
role: "heading",
|
|
1001
1249
|
className: grid
|
|
1002
|
-
}, /* @__PURE__ */
|
|
1250
|
+
}, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement("div", {
|
|
1003
1251
|
className: "flex items-center text-sm"
|
|
1004
|
-
}, t("field path label"))), /* @__PURE__ */
|
|
1252
|
+
}, t("field path label"))), /* @__PURE__ */ React13.createElement("div", {
|
|
1005
1253
|
role: "list",
|
|
1006
1254
|
className: "flex flex-col w-full"
|
|
1007
|
-
}, fields?.map((field2) => /* @__PURE__ */
|
|
1255
|
+
}, fields?.map((field2) => /* @__PURE__ */ React13.createElement(List2.Item, {
|
|
1008
1256
|
key: field2.id,
|
|
1009
1257
|
item: field2,
|
|
1010
|
-
classNames:
|
|
1011
|
-
}, /* @__PURE__ */
|
|
1258
|
+
classNames: mx8(grid, ghostHover, "cursor-pointer")
|
|
1259
|
+
}, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
|
|
1012
1260
|
onClick: () => handleSelect(field2)
|
|
1013
|
-
}, field2.path), /* @__PURE__ */
|
|
1261
|
+
}, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
|
|
1014
1262
|
disabled: view.fields.length <= 1,
|
|
1015
1263
|
onClick: () => onDelete(field2.id)
|
|
1016
|
-
}))))))), field && /* @__PURE__ */
|
|
1264
|
+
}))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
|
|
1017
1265
|
key: field.id,
|
|
1018
1266
|
view,
|
|
1019
1267
|
projection,
|
|
1020
1268
|
field,
|
|
1021
1269
|
registry,
|
|
1022
1270
|
onSave: handleClose
|
|
1023
|
-
}), !readonly && !field && /* @__PURE__ */
|
|
1271
|
+
}), !readonly && !field && /* @__PURE__ */ React13.createElement("div", {
|
|
1024
1272
|
className: "flex p-2 justify-center"
|
|
1025
|
-
}, /* @__PURE__ */
|
|
1273
|
+
}, /* @__PURE__ */ React13.createElement(IconButton4, {
|
|
1026
1274
|
icon: "ph--plus--regular",
|
|
1027
1275
|
label: t("button add property"),
|
|
1028
1276
|
onClick: handleAdd,
|
|
@@ -1252,14 +1500,15 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1252
1500
|
};
|
|
1253
1501
|
export {
|
|
1254
1502
|
BooleanInput,
|
|
1503
|
+
DeprecatedFormContainer,
|
|
1255
1504
|
DeprecatedFormInput,
|
|
1256
1505
|
FieldEditor,
|
|
1257
1506
|
Form,
|
|
1258
|
-
FormActions,
|
|
1259
1507
|
GeoPointInput,
|
|
1260
1508
|
InputHeader,
|
|
1261
1509
|
NumberInput,
|
|
1262
1510
|
SelectInput,
|
|
1511
|
+
SelectOptionInput,
|
|
1263
1512
|
TextInput,
|
|
1264
1513
|
TupleInput,
|
|
1265
1514
|
ViewEditor,
|