@dxos/react-ui-form 0.7.5-main.499c70c → 0.7.5-main.6a330ac
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 +1054 -575
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +1115 -647
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1054 -575
- 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 +1 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +15 -0
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/Defaults.d.ts +6 -7
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +13 -16
- 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 +6 -0
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContent.d.ts +25 -0
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContext.d.ts +23 -0
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Input.d.ts +7 -8
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -3
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +3 -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 +2 -4
- 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/Form/index.d.ts +2 -0
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts +1 -2
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.d.ts +1 -2
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -2
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +6 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +11 -11
- 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/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -27
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.tsx +58 -40
- package/src/components/Form/ArrayField.tsx +111 -0
- package/src/components/Form/Defaults.tsx +78 -34
- package/src/components/Form/Form.stories.tsx +15 -5
- package/src/components/Form/Form.tsx +51 -327
- package/src/components/Form/FormActions.tsx +41 -0
- package/src/components/Form/FormContent.tsx +170 -0
- package/src/components/Form/FormContext.tsx +96 -0
- package/src/components/Form/Input.tsx +5 -6
- package/src/components/Form/custom/GeoPointInput.tsx +44 -23
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
- package/src/components/Form/custom/SelectOptionsInput.tsx +190 -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 +6 -7
- package/src/components/Form/index.ts +3 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +25 -4
- package/src/components/ViewEditor/ViewEditor.tsx +16 -8
- package/src/components/deprecated/FormInput.tsx +20 -7
- package/src/hooks/useForm.ts +116 -79
- package/src/translations.ts +9 -1
- package/src/util/field.ts +19 -1
- package/src/util/formatting.ts +29 -1
|
@@ -29,12 +29,13 @@ var translations_default = [
|
|
|
29
29
|
"format hostname": "Hostname",
|
|
30
30
|
"format integer": "Integer",
|
|
31
31
|
"format json": "JSON",
|
|
32
|
-
"format latlng": "
|
|
32
|
+
"format latlng": "Geopoint",
|
|
33
33
|
"format markdown": "Markdown",
|
|
34
34
|
"format percent": "Percent",
|
|
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,224 +78,57 @@ 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 useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState4 } from "react";
|
|
84
103
|
import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
|
|
85
|
-
import { log as log3 } from "@dxos/log";
|
|
86
|
-
import { useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
87
|
-
import { getFormatSchema, getSchemaProperties as getSchemaProperties2, sortProperties } from "@dxos/schema";
|
|
88
|
-
|
|
89
|
-
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
90
|
-
import React3 from "react";
|
|
91
|
-
import { Input as Input2, useTranslation } from "@dxos/react-ui";
|
|
92
|
-
import { safeParseFloat } from "@dxos/util";
|
|
93
|
-
|
|
94
|
-
// packages/ui/react-ui-form/src/components/Form/Input.tsx
|
|
95
|
-
import React2 from "react";
|
|
96
|
-
import { Icon, Tooltip } from "@dxos/react-ui";
|
|
97
|
-
import { errorText } from "@dxos/react-ui-theme";
|
|
98
|
-
var InputHeader = ({ children, error }) => {
|
|
99
|
-
return /* @__PURE__ */ React2.createElement("div", {
|
|
100
|
-
role: "none",
|
|
101
|
-
className: "flex justify-between items-center mb-1"
|
|
102
|
-
}, children, error && /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
|
|
103
|
-
asChild: true
|
|
104
|
-
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
105
|
-
icon: "ph--warning--regular",
|
|
106
|
-
size: 4,
|
|
107
|
-
classNames: errorText
|
|
108
|
-
})), /* @__PURE__ */ React2.createElement(Tooltip.Content, {
|
|
109
|
-
side: "bottom"
|
|
110
|
-
}, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
114
|
-
var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: getValue2, onValueChange, onBlur }) => {
|
|
115
|
-
const { t } = useTranslation(translationKey);
|
|
116
|
-
const { status, error } = getStatus?.(property);
|
|
117
|
-
const [lng = 0, lat = 0] = getValue2(property) ?? [];
|
|
118
|
-
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
119
|
-
validationValence: status
|
|
120
|
-
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
121
|
-
error
|
|
122
|
-
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
|
|
123
|
-
className: "grid grid-cols-2 gap-2"
|
|
124
|
-
}, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
125
|
-
type: "number",
|
|
126
|
-
disabled,
|
|
127
|
-
placeholder: t("placeholder longitude"),
|
|
128
|
-
value: lng,
|
|
129
|
-
onChange: (event) => onValueChange(property, type, [
|
|
130
|
-
lat,
|
|
131
|
-
safeParseFloat(event.target.value, 0)
|
|
132
|
-
]),
|
|
133
|
-
onBlur
|
|
134
|
-
}), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
135
|
-
type: "number",
|
|
136
|
-
disabled,
|
|
137
|
-
placeholder: t("placeholder latitude"),
|
|
138
|
-
value: lat,
|
|
139
|
-
onChange: (event) => onValueChange(property, type, [
|
|
140
|
-
safeParseFloat(event.target.value, 0),
|
|
141
|
-
lng
|
|
142
|
-
]),
|
|
143
|
-
onBlur
|
|
144
|
-
})));
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
|
|
148
|
-
import React4 from "react";
|
|
149
|
-
import { Input as Input3 } from "@dxos/react-ui";
|
|
150
|
-
import { mx } from "@dxos/react-ui-theme";
|
|
151
|
-
import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
|
|
152
|
-
var gridCols = [
|
|
153
|
-
"grid-cols-1",
|
|
154
|
-
"grid-cols-2",
|
|
155
|
-
"grid-cols-3",
|
|
156
|
-
"grid-cols-4"
|
|
157
|
-
];
|
|
158
|
-
var TupleInput = ({ binding, property, type, label, disabled, getStatus, getValue: getValue2, onValueChange, onBlur }) => {
|
|
159
|
-
const { status, error } = getStatus?.(property);
|
|
160
|
-
const values = getValue2(property) ?? {};
|
|
161
|
-
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
162
|
-
validationValence: status
|
|
163
|
-
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
164
|
-
error
|
|
165
|
-
}, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
|
|
166
|
-
className: mx("grid gap-2", gridCols[binding.length - 1])
|
|
167
|
-
}, binding.map((prop) => /* @__PURE__ */ React4.createElement(Input3.TextInput, {
|
|
168
|
-
key: prop,
|
|
169
|
-
type: "number",
|
|
170
|
-
disabled,
|
|
171
|
-
value: values[prop],
|
|
172
|
-
onChange: (event) => onValueChange(property, type, {
|
|
173
|
-
...values,
|
|
174
|
-
[prop]: safeParseFloat2(event.target.value, 0)
|
|
175
|
-
}),
|
|
176
|
-
onBlur
|
|
177
|
-
}))));
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
181
|
-
import React5 from "react";
|
|
182
|
-
import { Input as Input4, Select } from "@dxos/react-ui";
|
|
183
|
-
var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue2, onValueChange, onBlur }) => {
|
|
184
|
-
const { status, error } = getStatus?.(property);
|
|
185
|
-
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
186
|
-
validationValence: status
|
|
187
|
-
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
188
|
-
error
|
|
189
|
-
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Input4.TextInput, {
|
|
190
|
-
disabled,
|
|
191
|
-
placeholder,
|
|
192
|
-
value: getValue2(property) ?? "",
|
|
193
|
-
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
194
|
-
onBlur
|
|
195
|
-
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.Validation, null, error));
|
|
196
|
-
};
|
|
197
|
-
var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue2, onValueChange, onBlur }) => {
|
|
198
|
-
const { status, error } = getStatus?.(property);
|
|
199
|
-
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
200
|
-
validationValence: status
|
|
201
|
-
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
202
|
-
error
|
|
203
|
-
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Input4.TextInput, {
|
|
204
|
-
type: "number",
|
|
205
|
-
disabled,
|
|
206
|
-
placeholder,
|
|
207
|
-
value: getValue2(property) ?? 0,
|
|
208
|
-
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
209
|
-
onBlur
|
|
210
|
-
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
|
|
211
|
-
};
|
|
212
|
-
var BooleanInput = ({ property, type, label, inputOnly, getStatus, getValue: getValue2, onValueChange }) => {
|
|
213
|
-
const { status, error } = getStatus?.(property);
|
|
214
|
-
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
215
|
-
validationValence: status
|
|
216
|
-
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
217
|
-
error
|
|
218
|
-
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Input4.Switch, {
|
|
219
|
-
checked: getValue2(property),
|
|
220
|
-
onCheckedChange: (value) => onValueChange(property, type, value)
|
|
221
|
-
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
|
|
222
|
-
};
|
|
223
|
-
var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue2, onValueChange }) => {
|
|
224
|
-
const { status, error } = getStatus?.(property);
|
|
225
|
-
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
226
|
-
validationValence: status
|
|
227
|
-
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
228
|
-
error
|
|
229
|
-
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Select.Root, {
|
|
230
|
-
value: getValue2(property),
|
|
231
|
-
onValueChange: (value) => onValueChange(property, type, value)
|
|
232
|
-
}, /* @__PURE__ */ React5.createElement(Select.TriggerButton, {
|
|
233
|
-
classNames: "is-full",
|
|
234
|
-
disabled,
|
|
235
|
-
placeholder
|
|
236
|
-
}), /* @__PURE__ */ React5.createElement(Select.Portal, null, /* @__PURE__ */ React5.createElement(Select.Content, null, /* @__PURE__ */ React5.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
237
|
-
// NOTE: Numeric values are converted to and from strings.
|
|
238
|
-
/* @__PURE__ */ React5.createElement(Select.Option, {
|
|
239
|
-
key: String(value),
|
|
240
|
-
value: String(value)
|
|
241
|
-
}, label2 ?? String(value))
|
|
242
|
-
)))))), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
246
|
-
import { pipe } from "effect";
|
|
247
|
-
import { capitalize } from "effect/String";
|
|
248
|
-
import React6, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
249
|
-
import { AST, S } from "@dxos/echo-schema";
|
|
250
|
-
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
251
104
|
import { log as log2 } from "@dxos/log";
|
|
252
|
-
import {
|
|
253
|
-
import {
|
|
254
|
-
import { getSchemaProperties } from "@dxos/schema";
|
|
255
|
-
import { isNotFalsy } from "@dxos/util";
|
|
105
|
+
import { useTranslation as useTranslation5 } from "@dxos/react-ui";
|
|
106
|
+
import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
|
|
256
107
|
|
|
257
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
258
|
-
import {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return GeoPointInput;
|
|
263
|
-
}
|
|
264
|
-
switch (type) {
|
|
265
|
-
case "string":
|
|
266
|
-
return TextInput;
|
|
267
|
-
case "number":
|
|
268
|
-
return NumberInput;
|
|
269
|
-
case "boolean":
|
|
270
|
-
return BooleanInput;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
108
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
109
|
+
import React2, { createContext, useContext, useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
110
|
+
import { raise } from "@dxos/debug";
|
|
111
|
+
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
112
|
+
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
273
113
|
|
|
274
114
|
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
275
115
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
276
116
|
import { getValue, setValue } from "@dxos/echo-schema";
|
|
117
|
+
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
277
118
|
import { invariant } from "@dxos/invariant";
|
|
278
119
|
import { log } from "@dxos/log";
|
|
279
120
|
import { validateSchema } from "@dxos/schema";
|
|
280
121
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
281
122
|
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
123
|
+
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
124
|
+
F: __dxlog_file,
|
|
125
|
+
L: 92,
|
|
126
|
+
S: void 0,
|
|
127
|
+
A: [
|
|
128
|
+
"AST.isTypeLiteral(schema.ast)",
|
|
129
|
+
""
|
|
130
|
+
]
|
|
131
|
+
});
|
|
282
132
|
const [values, setValues] = useState(initialValues);
|
|
283
133
|
useEffect(() => {
|
|
284
134
|
setValues(initialValues);
|
|
@@ -315,8 +165,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
315
165
|
values,
|
|
316
166
|
schemaDirty
|
|
317
167
|
]);
|
|
318
|
-
const
|
|
319
|
-
|
|
168
|
+
const formIsValid = useMemo(() => {
|
|
169
|
+
return Object.keys(errors).length === 0;
|
|
170
|
+
}, [
|
|
171
|
+
errors
|
|
172
|
+
]);
|
|
173
|
+
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors.
|
|
174
|
+
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
320
175
|
touched,
|
|
321
176
|
errors,
|
|
322
177
|
saving
|
|
@@ -338,17 +193,23 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
338
193
|
onSave,
|
|
339
194
|
changed
|
|
340
195
|
]);
|
|
341
|
-
const getStatus = useCallback((
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
196
|
+
const getStatus = useCallback((path) => {
|
|
197
|
+
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
198
|
+
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
199
|
+
return {
|
|
200
|
+
status: matchingError ? "error" : void 0,
|
|
201
|
+
error: matchingError ? matchingError[1] : void 0
|
|
202
|
+
};
|
|
203
|
+
}, [
|
|
346
204
|
errors
|
|
347
205
|
]);
|
|
348
|
-
const getFormValue = (
|
|
349
|
-
return getValue(values,
|
|
350
|
-
}
|
|
351
|
-
|
|
206
|
+
const getFormValue = useCallback((path) => {
|
|
207
|
+
return getValue(values, createJsonPath(path));
|
|
208
|
+
}, [
|
|
209
|
+
values
|
|
210
|
+
]);
|
|
211
|
+
const onValueChange = useCallback((path, type, value) => {
|
|
212
|
+
const jsonPath = createJsonPath(path);
|
|
352
213
|
let parsedValue = value;
|
|
353
214
|
try {
|
|
354
215
|
if (type === "number") {
|
|
@@ -357,19 +218,19 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
357
218
|
} catch (err) {
|
|
358
219
|
log.catch(err, void 0, {
|
|
359
220
|
F: __dxlog_file,
|
|
360
|
-
L:
|
|
221
|
+
L: 202,
|
|
361
222
|
S: void 0,
|
|
362
223
|
C: (f, a) => f(...a)
|
|
363
224
|
});
|
|
364
225
|
parsedValue = void 0;
|
|
365
226
|
}
|
|
366
227
|
const newValues = {
|
|
367
|
-
...setValue(values,
|
|
228
|
+
...setValue(values, jsonPath, parsedValue)
|
|
368
229
|
};
|
|
369
230
|
setValues(newValues);
|
|
370
231
|
setChanged((prev) => ({
|
|
371
232
|
...prev,
|
|
372
|
-
[
|
|
233
|
+
[jsonPath]: true
|
|
373
234
|
}));
|
|
374
235
|
onValuesChanged?.(newValues);
|
|
375
236
|
const isValid = validate(newValues);
|
|
@@ -378,40 +239,57 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
378
239
|
changed
|
|
379
240
|
});
|
|
380
241
|
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
242
|
+
}, [
|
|
243
|
+
values,
|
|
244
|
+
onValuesChanged,
|
|
245
|
+
validate,
|
|
246
|
+
onValid,
|
|
247
|
+
changed
|
|
248
|
+
]);
|
|
249
|
+
const onTouched = useCallback((path) => {
|
|
250
|
+
const jsonPath = createJsonPath(path);
|
|
384
251
|
setTouched((touched2) => ({
|
|
385
252
|
...touched2,
|
|
386
|
-
[
|
|
253
|
+
[jsonPath]: true
|
|
387
254
|
}));
|
|
388
|
-
if (event.relatedTarget?.getAttribute("type") === "submit") {
|
|
389
|
-
setTouched(createKeySet(values, true));
|
|
390
|
-
}
|
|
391
255
|
validate(values);
|
|
392
256
|
}, [
|
|
393
257
|
validate,
|
|
394
258
|
values
|
|
395
259
|
]);
|
|
396
|
-
return {
|
|
260
|
+
return useMemo(() => ({
|
|
397
261
|
// State.
|
|
398
262
|
values,
|
|
399
263
|
errors,
|
|
400
264
|
touched,
|
|
401
265
|
changed,
|
|
402
266
|
canSave,
|
|
267
|
+
formIsValid,
|
|
268
|
+
// Actions.
|
|
403
269
|
handleSave,
|
|
404
270
|
// Field utils.
|
|
405
271
|
getStatus,
|
|
406
272
|
getValue: getFormValue,
|
|
407
273
|
onValueChange,
|
|
408
|
-
|
|
409
|
-
}
|
|
274
|
+
onTouched
|
|
275
|
+
}), [
|
|
276
|
+
values,
|
|
277
|
+
errors,
|
|
278
|
+
touched,
|
|
279
|
+
changed,
|
|
280
|
+
canSave,
|
|
281
|
+
formIsValid,
|
|
282
|
+
handleSave,
|
|
283
|
+
getStatus,
|
|
284
|
+
getFormValue,
|
|
285
|
+
onValueChange,
|
|
286
|
+
onTouched
|
|
287
|
+
]);
|
|
410
288
|
};
|
|
411
289
|
var createKeySet = (obj, value) => {
|
|
412
290
|
invariant(obj, void 0, {
|
|
413
291
|
F: __dxlog_file,
|
|
414
|
-
L:
|
|
292
|
+
L: 264,
|
|
415
293
|
S: void 0,
|
|
416
294
|
A: [
|
|
417
295
|
"obj",
|
|
@@ -423,320 +301,858 @@ var createKeySet = (obj, value) => {
|
|
|
423
301
|
[key]: value
|
|
424
302
|
}), {});
|
|
425
303
|
};
|
|
426
|
-
var flatMap = (errors) => {
|
|
427
|
-
return errors.reduce((result, { path, message }) => {
|
|
428
|
-
|
|
429
|
-
|
|
304
|
+
var flatMap = (errors) => {
|
|
305
|
+
return errors.reduce((result, { path, message }) => {
|
|
306
|
+
const jsonPath = fromEffectValidationPath(path);
|
|
307
|
+
if (!(jsonPath in result)) {
|
|
308
|
+
result[jsonPath] = message;
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}, {});
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
315
|
+
var FormContext = /* @__PURE__ */ createContext(void 0);
|
|
316
|
+
var useFormContext = () => {
|
|
317
|
+
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
318
|
+
};
|
|
319
|
+
var useFormValues = (path = []) => {
|
|
320
|
+
const { values: formValues } = useFormContext();
|
|
321
|
+
const jsonPath = createJsonPath2(path);
|
|
322
|
+
return getValue2(formValues, jsonPath);
|
|
323
|
+
};
|
|
324
|
+
var useInputProps = (path = []) => {
|
|
325
|
+
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
326
|
+
const stablePath = useMemo2(() => path, [
|
|
327
|
+
Array.isArray(path) ? path.join(".") : path
|
|
328
|
+
]);
|
|
329
|
+
return useMemo2(() => ({
|
|
330
|
+
getStatus: () => getStatus(stablePath),
|
|
331
|
+
getValue: () => getFormValue(stablePath),
|
|
332
|
+
onValueChange: (type, value) => onValueChange(stablePath, type, value),
|
|
333
|
+
onBlur: () => onTouched(stablePath)
|
|
334
|
+
}), [
|
|
335
|
+
getStatus,
|
|
336
|
+
getFormValue,
|
|
337
|
+
onValueChange,
|
|
338
|
+
onTouched,
|
|
339
|
+
stablePath
|
|
340
|
+
]);
|
|
341
|
+
};
|
|
342
|
+
var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
|
|
343
|
+
const form = useForm(formOptions);
|
|
344
|
+
useEffect2(() => {
|
|
345
|
+
if (!formRef?.current) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const handleKeyDown = (event) => {
|
|
349
|
+
const keyIsEnter = event.key === "Enter";
|
|
350
|
+
const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
|
|
351
|
+
const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
|
|
352
|
+
const inputOptOut = event.target.hasAttribute("data-no-submit");
|
|
353
|
+
const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
|
|
354
|
+
const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
|
|
355
|
+
if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
|
|
356
|
+
if (!autoSave && form.canSave) {
|
|
357
|
+
form.handleSave();
|
|
358
|
+
}
|
|
359
|
+
if (autoSave && form.formIsValid) {
|
|
360
|
+
event.target.blur();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const formElement = formRef.current;
|
|
365
|
+
formElement.addEventListener("keydown", handleKeyDown);
|
|
366
|
+
return () => formElement.removeEventListener("keydown", handleKeyDown);
|
|
367
|
+
}, [
|
|
368
|
+
formRef,
|
|
369
|
+
form.canSave,
|
|
370
|
+
form.formIsValid,
|
|
371
|
+
form.handleSave,
|
|
372
|
+
autoSave
|
|
373
|
+
]);
|
|
374
|
+
return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
|
|
375
|
+
value: form
|
|
376
|
+
}, children);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
380
|
+
import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo3 } from "react";
|
|
381
|
+
import { GeoLocation } from "@dxos/echo-schema";
|
|
382
|
+
import { Input as Input2, useTranslation } from "@dxos/react-ui";
|
|
383
|
+
import { safeParseFloat } from "@dxos/util";
|
|
384
|
+
|
|
385
|
+
// packages/ui/react-ui-form/src/components/Form/Input.tsx
|
|
386
|
+
import React3 from "react";
|
|
387
|
+
import { Icon, Tooltip } from "@dxos/react-ui";
|
|
388
|
+
import { errorText } from "@dxos/react-ui-theme";
|
|
389
|
+
var InputHeader = ({ children, error }) => {
|
|
390
|
+
return /* @__PURE__ */ React3.createElement("div", {
|
|
391
|
+
role: "none",
|
|
392
|
+
className: "flex justify-between items-center mb-1"
|
|
393
|
+
}, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
|
|
394
|
+
asChild: true
|
|
395
|
+
}, /* @__PURE__ */ React3.createElement(Icon, {
|
|
396
|
+
icon: "ph--warning--regular",
|
|
397
|
+
size: 4,
|
|
398
|
+
classNames: errorText
|
|
399
|
+
})), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
|
|
400
|
+
side: "bottom"
|
|
401
|
+
}, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
405
|
+
var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
406
|
+
const { t } = useTranslation(translationKey);
|
|
407
|
+
const { status, error } = getStatus();
|
|
408
|
+
const geoPoint = useMemo3(() => getValue3() ?? [
|
|
409
|
+
0,
|
|
410
|
+
0
|
|
411
|
+
], [
|
|
412
|
+
getValue3
|
|
413
|
+
]);
|
|
414
|
+
const geoLocation = useMemo3(() => GeoLocation.fromGeoPoint(geoPoint), [
|
|
415
|
+
geoPoint
|
|
416
|
+
]);
|
|
417
|
+
const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
|
|
418
|
+
const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
|
|
419
|
+
useEffect3(() => {
|
|
420
|
+
const location = GeoLocation.fromGeoPoint(geoPoint);
|
|
421
|
+
setLongitudeText(location.longitude.toString());
|
|
422
|
+
setLatitudeText(location.latitude.toString());
|
|
423
|
+
}, [
|
|
424
|
+
geoPoint
|
|
425
|
+
]);
|
|
426
|
+
const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
|
|
427
|
+
const inputText = event.target.value;
|
|
428
|
+
setText(inputText);
|
|
429
|
+
if (inputText !== "" && inputText !== "-") {
|
|
430
|
+
const coord = safeParseFloat(inputText);
|
|
431
|
+
if (coord !== void 0 && !isNaN(coord)) {
|
|
432
|
+
const currentLocation = GeoLocation.fromGeoPoint(getValue3() ?? [
|
|
433
|
+
0,
|
|
434
|
+
0
|
|
435
|
+
]);
|
|
436
|
+
const newLocation = {
|
|
437
|
+
...currentLocation,
|
|
438
|
+
[coordinateType]: coord
|
|
439
|
+
};
|
|
440
|
+
const newValue = GeoLocation.toGeoPoint(newLocation);
|
|
441
|
+
onValueChange(type, newValue);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}, [
|
|
445
|
+
getValue3,
|
|
446
|
+
onValueChange,
|
|
447
|
+
type
|
|
448
|
+
]);
|
|
449
|
+
return /* @__PURE__ */ React4.createElement(Input2.Root, {
|
|
450
|
+
validationValence: status
|
|
451
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
452
|
+
error
|
|
453
|
+
}, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
|
|
454
|
+
role: "none",
|
|
455
|
+
className: "grid grid-cols-2 gap-2"
|
|
456
|
+
}, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
457
|
+
type: "text",
|
|
458
|
+
pattern: "^-?[0-9]*\\.?[0-9]*$",
|
|
459
|
+
disabled,
|
|
460
|
+
placeholder: t("placeholder latitude"),
|
|
461
|
+
value: latitudeText,
|
|
462
|
+
onChange: handleCoordinateChange("latitude", setLatitudeText),
|
|
463
|
+
onBlur
|
|
464
|
+
}), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
465
|
+
type: "text",
|
|
466
|
+
pattern: "^-?[0-9]*\\.?[0-9]*$",
|
|
467
|
+
disabled,
|
|
468
|
+
placeholder: t("placeholder longitude"),
|
|
469
|
+
value: longitudeText,
|
|
470
|
+
onChange: handleCoordinateChange("longitude", setLongitudeText),
|
|
471
|
+
onBlur
|
|
472
|
+
})));
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
|
|
476
|
+
import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
|
|
477
|
+
import { PublicKey } from "@dxos/keys";
|
|
478
|
+
import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
479
|
+
import { List } from "@dxos/react-ui-list";
|
|
480
|
+
import { HuePicker } from "@dxos/react-ui-pickers";
|
|
481
|
+
import { hues, mx as mx2 } from "@dxos/react-ui-theme";
|
|
482
|
+
var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
|
|
483
|
+
const [selected, setSelectedId] = useState3(null);
|
|
484
|
+
const [isNewOption, setIsNewOption] = useState3(false);
|
|
485
|
+
const inputRef = useRef(null);
|
|
486
|
+
const { t } = useTranslation2(translationKey);
|
|
487
|
+
const { status, error } = getStatus();
|
|
488
|
+
const options = getValue3();
|
|
489
|
+
useEffect4(() => {
|
|
490
|
+
if (options === void 0) {
|
|
491
|
+
onValueChange(type, []);
|
|
492
|
+
}
|
|
493
|
+
}, [
|
|
494
|
+
options,
|
|
495
|
+
onValueChange,
|
|
496
|
+
type
|
|
497
|
+
]);
|
|
498
|
+
const randomHue = () => {
|
|
499
|
+
const usedHues = new Set(options?.map((option) => option.color) ?? []);
|
|
500
|
+
const unusedHues = hues.filter((hue) => !usedHues.has(hue));
|
|
501
|
+
if (unusedHues.length > 0) {
|
|
502
|
+
return unusedHues[Math.floor(Math.random() * unusedHues.length)];
|
|
503
|
+
}
|
|
504
|
+
const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
|
|
505
|
+
const availableHues = hues.filter((hue) => !lastThree.has(hue));
|
|
506
|
+
return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
|
|
507
|
+
};
|
|
508
|
+
const handleAdd = useCallback3(() => {
|
|
509
|
+
const newId = PublicKey.random().truncate();
|
|
510
|
+
const newOption = {
|
|
511
|
+
id: newId,
|
|
512
|
+
title: "",
|
|
513
|
+
color: randomHue()
|
|
514
|
+
};
|
|
515
|
+
onValueChange(type, [
|
|
516
|
+
...options ?? [],
|
|
517
|
+
newOption
|
|
518
|
+
]);
|
|
519
|
+
setSelectedId(newId);
|
|
520
|
+
setIsNewOption(true);
|
|
521
|
+
}, [
|
|
522
|
+
options,
|
|
523
|
+
type,
|
|
524
|
+
onValueChange
|
|
525
|
+
]);
|
|
526
|
+
const handleDelete = useCallback3((id) => {
|
|
527
|
+
const newOptions = options?.filter((option) => option.id !== id) ?? [];
|
|
528
|
+
onValueChange(type, newOptions);
|
|
529
|
+
setSelectedId(null);
|
|
530
|
+
}, [
|
|
531
|
+
options,
|
|
532
|
+
type,
|
|
533
|
+
onValueChange
|
|
534
|
+
]);
|
|
535
|
+
const handleMove = useCallback3((from, to) => {
|
|
536
|
+
if (!options) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const newOptions = [
|
|
540
|
+
...options
|
|
541
|
+
];
|
|
542
|
+
const [removed] = newOptions.splice(from, 1);
|
|
543
|
+
newOptions.splice(to, 0, removed);
|
|
544
|
+
onValueChange(type, newOptions);
|
|
545
|
+
}, [
|
|
546
|
+
options,
|
|
547
|
+
type,
|
|
548
|
+
onValueChange
|
|
549
|
+
]);
|
|
550
|
+
const handleClick = useCallback3((id) => {
|
|
551
|
+
setSelectedId((current) => current === id ? null : id);
|
|
552
|
+
}, []);
|
|
553
|
+
const handleTitleChange = useCallback3((id) => (e) => {
|
|
554
|
+
const newOptions = options?.map((o) => o.id === id ? {
|
|
555
|
+
...o,
|
|
556
|
+
title: e.target.value
|
|
557
|
+
} : o);
|
|
558
|
+
onValueChange(type, newOptions ?? []);
|
|
559
|
+
}, [
|
|
560
|
+
options,
|
|
561
|
+
type,
|
|
562
|
+
onValueChange
|
|
563
|
+
]);
|
|
564
|
+
const handleKeyDown = useCallback3((e) => {
|
|
565
|
+
if (e.key === "Enter") {
|
|
566
|
+
setSelectedId(null);
|
|
567
|
+
}
|
|
568
|
+
}, []);
|
|
569
|
+
const handleColorChange = useCallback3((id) => (hue) => {
|
|
570
|
+
const newOptions = options?.map((o) => o.id === id ? {
|
|
571
|
+
...o,
|
|
572
|
+
color: hue
|
|
573
|
+
} : o);
|
|
574
|
+
onValueChange(type, newOptions ?? []);
|
|
575
|
+
}, [
|
|
576
|
+
options,
|
|
577
|
+
type,
|
|
578
|
+
onValueChange
|
|
579
|
+
]);
|
|
580
|
+
useEffect4(() => {
|
|
581
|
+
if (selected && isNewOption && inputRef.current) {
|
|
582
|
+
inputRef.current.focus();
|
|
583
|
+
setIsNewOption(false);
|
|
584
|
+
}
|
|
585
|
+
}, [
|
|
586
|
+
selected,
|
|
587
|
+
isNewOption
|
|
588
|
+
]);
|
|
589
|
+
return /* @__PURE__ */ React5.createElement(Input3.Root, {
|
|
590
|
+
validationValence: status
|
|
591
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
592
|
+
error
|
|
593
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
|
|
594
|
+
role: "none"
|
|
595
|
+
}, options && /* @__PURE__ */ React5.createElement(List.Root, {
|
|
596
|
+
items: options,
|
|
597
|
+
isItem: (item) => true,
|
|
598
|
+
onMove: handleMove
|
|
599
|
+
}, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
|
|
600
|
+
role: "list",
|
|
601
|
+
className: "w-full overflow-auto"
|
|
602
|
+
}, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
|
|
603
|
+
key: item.id
|
|
604
|
+
}, /* @__PURE__ */ React5.createElement(List.Item, {
|
|
605
|
+
role: "button",
|
|
606
|
+
item,
|
|
607
|
+
classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
608
|
+
"aria-expanded": selected === item.id
|
|
609
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
610
|
+
className: "flex items-center"
|
|
611
|
+
}, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
|
|
612
|
+
onClick: () => handleClick(item.id),
|
|
613
|
+
classNames: "flex-1"
|
|
614
|
+
}, /* @__PURE__ */ React5.createElement(Tag, {
|
|
615
|
+
palette: item.color
|
|
616
|
+
}, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
|
|
617
|
+
icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
|
|
618
|
+
onClick: () => handleClick(item.id)
|
|
619
|
+
})), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
|
|
620
|
+
className: "mis-4 mbs-2 flex flex-col gap-1"
|
|
621
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, {
|
|
622
|
+
classNames: "text-xs"
|
|
623
|
+
}, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
|
|
624
|
+
className: "flex flex-row items-center gap-1"
|
|
625
|
+
}, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
|
|
626
|
+
placeholder: t("select option label placeholder"),
|
|
627
|
+
ref: selected === item.id ? inputRef : void 0,
|
|
628
|
+
value: item.title,
|
|
629
|
+
onChange: handleTitleChange(item.id),
|
|
630
|
+
onKeyDown: handleKeyDown,
|
|
631
|
+
classNames: "flex-1",
|
|
632
|
+
"data-no-submit": true
|
|
633
|
+
}), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
|
|
634
|
+
classNames: "p-0 m-0 !is-auto"
|
|
635
|
+
}, /* @__PURE__ */ React5.createElement(HuePicker, {
|
|
636
|
+
value: item.color,
|
|
637
|
+
onChange: handleColorChange(item.id)
|
|
638
|
+
})), /* @__PURE__ */ React5.createElement(IconButton, {
|
|
639
|
+
icon: "ph--trash--fill",
|
|
640
|
+
iconOnly: true,
|
|
641
|
+
label: t("select option delete"),
|
|
642
|
+
onClick: () => handleDelete(item.id)
|
|
643
|
+
})))))), /* @__PURE__ */ React5.createElement("div", {
|
|
644
|
+
role: "listitem"
|
|
645
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
646
|
+
role: "button",
|
|
647
|
+
className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
648
|
+
onClick: handleAdd
|
|
649
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
650
|
+
className: "flex items-center gap-1"
|
|
651
|
+
}, /* @__PURE__ */ React5.createElement(Icon2, {
|
|
652
|
+
icon: "ph--plus--regular"
|
|
653
|
+
}), /* @__PURE__ */ React5.createElement("span", {
|
|
654
|
+
className: "text-sm"
|
|
655
|
+
}, t("select option add")))))))));
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
// packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
|
|
659
|
+
import React6 from "react";
|
|
660
|
+
import { Input as Input4 } from "@dxos/react-ui";
|
|
661
|
+
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
662
|
+
import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
|
|
663
|
+
var gridCols = [
|
|
664
|
+
"grid-cols-1",
|
|
665
|
+
"grid-cols-2",
|
|
666
|
+
"grid-cols-3",
|
|
667
|
+
"grid-cols-4"
|
|
668
|
+
];
|
|
669
|
+
var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
670
|
+
const { status, error } = getStatus();
|
|
671
|
+
const values = getValue3() ?? {};
|
|
672
|
+
return /* @__PURE__ */ React6.createElement(Input4.Root, {
|
|
673
|
+
validationValence: status
|
|
674
|
+
}, /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
675
|
+
error
|
|
676
|
+
}, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
|
|
677
|
+
className: mx3("grid gap-2", gridCols[binding.length - 1])
|
|
678
|
+
}, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
|
|
679
|
+
key: prop,
|
|
680
|
+
type: "number",
|
|
681
|
+
disabled,
|
|
682
|
+
value: values[prop],
|
|
683
|
+
onChange: (event) => onValueChange(type, {
|
|
684
|
+
...values,
|
|
685
|
+
[prop]: safeParseFloat2(event.target.value, 0)
|
|
686
|
+
}),
|
|
687
|
+
onBlur
|
|
688
|
+
}))));
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
692
|
+
import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
|
|
693
|
+
import { Input as Input5, Select } from "@dxos/react-ui";
|
|
694
|
+
var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
695
|
+
const { status, error } = getStatus();
|
|
696
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
697
|
+
validationValence: status
|
|
698
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
699
|
+
error
|
|
700
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
701
|
+
disabled,
|
|
702
|
+
placeholder,
|
|
703
|
+
value: getValue3() ?? "",
|
|
704
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
705
|
+
onBlur
|
|
706
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
|
|
707
|
+
};
|
|
708
|
+
var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
709
|
+
const { status, error } = getStatus();
|
|
710
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
711
|
+
validationValence: status
|
|
712
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
713
|
+
error
|
|
714
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
715
|
+
type: "number",
|
|
716
|
+
disabled,
|
|
717
|
+
placeholder,
|
|
718
|
+
value: getValue3() ?? 0,
|
|
719
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
720
|
+
onBlur
|
|
721
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
722
|
+
};
|
|
723
|
+
var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
|
|
724
|
+
const { status, error } = getStatus();
|
|
725
|
+
const checked = Boolean(getValue3());
|
|
726
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
727
|
+
validationValence: status
|
|
728
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
729
|
+
error
|
|
730
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
|
|
731
|
+
checked,
|
|
732
|
+
onCheckedChange: (value) => onValueChange(type, value)
|
|
733
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
734
|
+
};
|
|
735
|
+
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
736
|
+
const { status, error } = getStatus();
|
|
737
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
738
|
+
validationValence: status
|
|
739
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
740
|
+
error
|
|
741
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
|
|
742
|
+
value: getValue3(),
|
|
743
|
+
onValueChange: (value) => onValueChange(type, value)
|
|
744
|
+
}, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
|
|
745
|
+
classNames: "is-full",
|
|
746
|
+
disabled,
|
|
747
|
+
placeholder
|
|
748
|
+
}), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
749
|
+
// NOTE: Numeric values are converted to and from strings.
|
|
750
|
+
/* @__PURE__ */ React7.createElement(Select.Option, {
|
|
751
|
+
key: String(value),
|
|
752
|
+
value: String(value)
|
|
753
|
+
}, label2 ?? String(value))
|
|
754
|
+
)))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
755
|
+
};
|
|
756
|
+
var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
757
|
+
const { status, error } = getStatus();
|
|
758
|
+
const textareaRef = useRef2(null);
|
|
759
|
+
const adjustHeight = useCallback4(() => {
|
|
760
|
+
const textarea = textareaRef.current;
|
|
761
|
+
if (textarea) {
|
|
762
|
+
textarea.style.blockSize = "auto";
|
|
763
|
+
textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
|
|
430
764
|
}
|
|
431
|
-
|
|
432
|
-
|
|
765
|
+
}, []);
|
|
766
|
+
useEffect5(() => {
|
|
767
|
+
adjustHeight();
|
|
768
|
+
}, [
|
|
769
|
+
adjustHeight
|
|
770
|
+
]);
|
|
771
|
+
useEffect5(() => {
|
|
772
|
+
adjustHeight();
|
|
773
|
+
}, [
|
|
774
|
+
getValue3(),
|
|
775
|
+
adjustHeight
|
|
776
|
+
]);
|
|
777
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
778
|
+
validationValence: status
|
|
779
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
780
|
+
error
|
|
781
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
|
|
782
|
+
ref: textareaRef,
|
|
783
|
+
disabled,
|
|
784
|
+
placeholder,
|
|
785
|
+
value: getValue3() ?? "",
|
|
786
|
+
classNames: "min-bs-auto max-h-40 overflow-auto",
|
|
787
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
788
|
+
onBlur,
|
|
789
|
+
style: {
|
|
790
|
+
resize: "none"
|
|
791
|
+
},
|
|
792
|
+
spellCheck: false
|
|
793
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
|
|
433
794
|
};
|
|
434
795
|
|
|
435
796
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
436
|
-
|
|
797
|
+
import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
|
|
798
|
+
import { mx as mx7 } from "@dxos/react-ui-theme";
|
|
799
|
+
|
|
800
|
+
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
801
|
+
import React8 from "react";
|
|
802
|
+
import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
803
|
+
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
804
|
+
var FormActions = ({ onCancel, readonly }) => {
|
|
805
|
+
const { t } = useTranslation3(translationKey);
|
|
806
|
+
const { canSave, handleSave } = useFormContext();
|
|
807
|
+
return /* @__PURE__ */ React8.createElement("div", {
|
|
808
|
+
role: "none",
|
|
809
|
+
className: "mt-2 flex justify-center"
|
|
810
|
+
}, /* @__PURE__ */ React8.createElement("div", {
|
|
811
|
+
role: "none",
|
|
812
|
+
className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
813
|
+
}, onCancel && !readonly && /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
814
|
+
"data-testid": "cancel-button",
|
|
815
|
+
icon: "ph--x--regular",
|
|
816
|
+
label: t("button cancel"),
|
|
817
|
+
onClick: onCancel
|
|
818
|
+
}), handleSave && /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
819
|
+
type: "submit",
|
|
820
|
+
"data-testid": "save-button",
|
|
821
|
+
disabled: !canSave,
|
|
822
|
+
icon: "ph--check--regular",
|
|
823
|
+
label: t("button save"),
|
|
824
|
+
onClick: handleSave
|
|
825
|
+
})));
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
829
|
+
import { pipe as pipe2 } from "effect";
|
|
830
|
+
import { capitalize as capitalize2 } from "effect/String";
|
|
831
|
+
import React10, { useMemo as useMemo5 } from "react";
|
|
832
|
+
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
833
|
+
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
834
|
+
import { mx as mx6 } from "@dxos/react-ui-theme";
|
|
835
|
+
import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
|
|
836
|
+
import { isNotFalsy } from "@dxos/util";
|
|
837
|
+
|
|
838
|
+
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
839
|
+
import { pipe } from "effect";
|
|
840
|
+
import { capitalize } from "effect/String";
|
|
841
|
+
import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
|
|
842
|
+
import { AST as AST2 } from "@dxos/echo-schema";
|
|
843
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
844
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
845
|
+
import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
|
|
846
|
+
import { mx as mx5 } from "@dxos/react-ui-theme";
|
|
847
|
+
import { getSchemaProperties } from "@dxos/schema";
|
|
848
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
|
|
437
849
|
var padding = "px-2";
|
|
438
|
-
var
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
850
|
+
var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
|
|
851
|
+
const { t } = useTranslation4(translationKey);
|
|
852
|
+
const { ast, name, type, title } = property;
|
|
853
|
+
const values = useFormValues(path ?? []);
|
|
854
|
+
invariant2(Array.isArray(values), `Values at path ${path?.join(".")} must be an array.`, {
|
|
855
|
+
F: __dxlog_file2,
|
|
856
|
+
L: 37,
|
|
857
|
+
S: void 0,
|
|
858
|
+
A: [
|
|
859
|
+
"Array.isArray(values)",
|
|
860
|
+
"`Values at path ${path?.join('.')} must be an array.`"
|
|
861
|
+
]
|
|
450
862
|
});
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
863
|
+
const label = title ?? pipe(name, capitalize);
|
|
864
|
+
const tupleType = findNode(ast, AST2.isTupleType);
|
|
865
|
+
const elementType = tupleType?.rest[0]?.type;
|
|
866
|
+
const getDefaultObjectValue = (typeNode) => {
|
|
867
|
+
const baseNode = findNode(typeNode, isDiscriminatedUnion);
|
|
868
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST2.isTypeLiteral);
|
|
869
|
+
if (!typeLiteral) {
|
|
870
|
+
return {};
|
|
871
|
+
}
|
|
872
|
+
return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [
|
|
873
|
+
prop.name,
|
|
874
|
+
prop.defaultValue
|
|
875
|
+
]));
|
|
876
|
+
};
|
|
877
|
+
const newValue = useMemo4(() => {
|
|
878
|
+
if (type === "object" && elementType) {
|
|
879
|
+
return getDefaultObjectValue(elementType);
|
|
880
|
+
} else {
|
|
881
|
+
return SimpleType.getDefaultValue(type);
|
|
882
|
+
}
|
|
459
883
|
}, [
|
|
460
|
-
|
|
461
|
-
|
|
884
|
+
type,
|
|
885
|
+
elementType
|
|
886
|
+
]);
|
|
887
|
+
const handleAdd = useCallback5(() => {
|
|
888
|
+
inputProps.onValueChange(type, [
|
|
889
|
+
...values,
|
|
890
|
+
newValue
|
|
891
|
+
]);
|
|
892
|
+
}, [
|
|
893
|
+
type,
|
|
894
|
+
elementType,
|
|
895
|
+
inputProps,
|
|
896
|
+
newValue,
|
|
462
897
|
values
|
|
463
898
|
]);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
errors
|
|
468
|
-
}, {
|
|
469
|
-
F: __dxlog_file2,
|
|
470
|
-
L: 99,
|
|
471
|
-
S: void 0,
|
|
472
|
-
C: (f, a) => f(...a)
|
|
473
|
-
});
|
|
474
|
-
}
|
|
899
|
+
const handleRemove = useCallback5((index) => {
|
|
900
|
+
const newValues = values.filter((_, i) => i !== index);
|
|
901
|
+
inputProps.onValueChange(type, newValues);
|
|
475
902
|
}, [
|
|
476
|
-
|
|
903
|
+
type,
|
|
904
|
+
inputProps,
|
|
905
|
+
values
|
|
477
906
|
]);
|
|
478
|
-
|
|
479
|
-
role: "form",
|
|
480
|
-
className: mx2("flex flex-col w-full gap-2 py-2", classNames),
|
|
481
|
-
"data-testid": testId
|
|
482
|
-
}, properties.map((property) => {
|
|
483
|
-
const { ast, name, type, format, title, description, examples, options, array } = property;
|
|
484
|
-
const key = [
|
|
485
|
-
...path,
|
|
486
|
-
name
|
|
487
|
-
];
|
|
488
|
-
const label = title ?? pipe(name, capitalize);
|
|
489
|
-
const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
|
|
490
|
-
let InputComponent = Custom?.[key.join(".")];
|
|
491
|
-
if (!InputComponent) {
|
|
492
|
-
if (options) {
|
|
493
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
494
|
-
key: name,
|
|
495
|
-
role: "none",
|
|
496
|
-
className: padding
|
|
497
|
-
}, /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
498
|
-
type,
|
|
499
|
-
format,
|
|
500
|
-
property: name,
|
|
501
|
-
disabled: readonly,
|
|
502
|
-
label,
|
|
503
|
-
options: options.map((option) => ({
|
|
504
|
-
value: option,
|
|
505
|
-
label: String(option)
|
|
506
|
-
})),
|
|
507
|
-
placeholder,
|
|
508
|
-
...inputProps
|
|
509
|
-
}));
|
|
510
|
-
}
|
|
511
|
-
InputComponent = getInputComponent(type, format);
|
|
512
|
-
if (array) {
|
|
513
|
-
return /* @__PURE__ */ React6.createElement(ArrayField, {
|
|
514
|
-
key: name,
|
|
515
|
-
name,
|
|
516
|
-
type,
|
|
517
|
-
array: true,
|
|
518
|
-
label,
|
|
519
|
-
ast,
|
|
520
|
-
values,
|
|
521
|
-
format,
|
|
522
|
-
readonly,
|
|
523
|
-
placeholder,
|
|
524
|
-
inputProps,
|
|
525
|
-
Custom
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
if (!InputComponent) {
|
|
530
|
-
if (type === "object") {
|
|
531
|
-
const baseNode = findNode(ast, isDiscriminatedUnion);
|
|
532
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(ast, AST.isTypeLiteral);
|
|
533
|
-
if (typeLiteral) {
|
|
534
|
-
const schema2 = S.make(typeLiteral);
|
|
535
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
536
|
-
key: name,
|
|
537
|
-
role: "none"
|
|
538
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
539
|
-
className: padding
|
|
540
|
-
}, label), /* @__PURE__ */ React6.createElement(Form, {
|
|
541
|
-
schema: schema2,
|
|
542
|
-
path: key,
|
|
543
|
-
values: values[name] ?? {},
|
|
544
|
-
onValuesChanged: (childValues) => {
|
|
545
|
-
inputProps.onValueChange(name, "object", childValues);
|
|
546
|
-
},
|
|
547
|
-
Custom
|
|
548
|
-
}));
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
log2("no renderer for property", {
|
|
552
|
-
name,
|
|
553
|
-
type
|
|
554
|
-
}, {
|
|
555
|
-
F: __dxlog_file2,
|
|
556
|
-
L: 184,
|
|
557
|
-
S: void 0,
|
|
558
|
-
C: (f, a) => f(...a)
|
|
559
|
-
});
|
|
560
|
-
return null;
|
|
561
|
-
}
|
|
562
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
563
|
-
key: name,
|
|
564
|
-
role: "none",
|
|
565
|
-
className: padding
|
|
566
|
-
}, /* @__PURE__ */ React6.createElement(InputComponent, {
|
|
567
|
-
type,
|
|
568
|
-
format,
|
|
569
|
-
property: name,
|
|
570
|
-
disabled: readonly,
|
|
571
|
-
label,
|
|
572
|
-
placeholder,
|
|
573
|
-
...inputProps
|
|
574
|
-
}));
|
|
575
|
-
}).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React6.createElement(FormActions, {
|
|
576
|
-
onCancel,
|
|
577
|
-
onSubmit: handleSave,
|
|
578
|
-
canSubmit: canSave,
|
|
579
|
-
readonly
|
|
580
|
-
}));
|
|
581
|
-
};
|
|
582
|
-
var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
|
|
583
|
-
const { t } = useTranslation2(translationKey);
|
|
584
|
-
const arrayValues = values[name] ?? [];
|
|
585
|
-
const key = [
|
|
586
|
-
name
|
|
587
|
-
];
|
|
588
|
-
const tupleType = findNode(ast, AST.isTupleType);
|
|
589
|
-
const elementType = tupleType?.rest[0]?.type;
|
|
590
|
-
if (type === "object" && elementType) {
|
|
591
|
-
const baseNode = findNode(elementType, isDiscriminatedUnion);
|
|
592
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST.isTypeLiteral);
|
|
593
|
-
if (typeLiteral) {
|
|
594
|
-
const schema = S.make(typeLiteral);
|
|
595
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
596
|
-
key: name,
|
|
597
|
-
role: "none",
|
|
598
|
-
className: mx2(padding)
|
|
599
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
600
|
-
role: "none",
|
|
601
|
-
className: "flex flex-col gap-1"
|
|
602
|
-
}, arrayValues.map((value, index) => {
|
|
603
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
604
|
-
key: index,
|
|
605
|
-
role: "none",
|
|
606
|
-
className: "flex items-center gap-1"
|
|
607
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
608
|
-
role: "none",
|
|
609
|
-
className: "flex-1"
|
|
610
|
-
}, /* @__PURE__ */ React6.createElement(Form, {
|
|
611
|
-
schema,
|
|
612
|
-
path: [
|
|
613
|
-
...key,
|
|
614
|
-
`[${index}]`
|
|
615
|
-
],
|
|
616
|
-
values: value,
|
|
617
|
-
onValuesChanged: (childValues) => {
|
|
618
|
-
const newValues = [
|
|
619
|
-
...arrayValues
|
|
620
|
-
];
|
|
621
|
-
newValues[index] = childValues;
|
|
622
|
-
inputProps.onValueChange(name, "object", newValues);
|
|
623
|
-
},
|
|
624
|
-
Custom
|
|
625
|
-
})), /* @__PURE__ */ React6.createElement(IconButton, {
|
|
626
|
-
icon: "ph--trash--regular",
|
|
627
|
-
iconOnly: true,
|
|
628
|
-
label: t("button remove"),
|
|
629
|
-
onClick: () => {
|
|
630
|
-
const newValues = arrayValues.filter((_, i) => i !== index);
|
|
631
|
-
inputProps.onValueChange(name, type, newValues);
|
|
632
|
-
}
|
|
633
|
-
}));
|
|
634
|
-
})), /* @__PURE__ */ React6.createElement("div", {
|
|
635
|
-
role: "none",
|
|
636
|
-
className: "flex justify-between items-center plb-1"
|
|
637
|
-
}, /* @__PURE__ */ React6.createElement(IconButton, {
|
|
638
|
-
icon: "ph--plus--regular",
|
|
639
|
-
iconOnly: true,
|
|
640
|
-
label: t("button add"),
|
|
641
|
-
onClick: () => {
|
|
642
|
-
const newValues = [
|
|
643
|
-
...arrayValues,
|
|
644
|
-
{}
|
|
645
|
-
];
|
|
646
|
-
inputProps.onValueChange(name, type, newValues);
|
|
647
|
-
}
|
|
648
|
-
})));
|
|
649
|
-
}
|
|
650
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
651
|
-
key: name,
|
|
652
|
-
role: "none"
|
|
653
|
-
}, "Nested form not supported in arrays, yet.");
|
|
654
|
-
}
|
|
655
|
-
const InputComponent = getInputComponent(type, format);
|
|
656
|
-
if (!InputComponent) {
|
|
907
|
+
if (!elementType) {
|
|
657
908
|
return null;
|
|
658
909
|
}
|
|
659
|
-
return /* @__PURE__ */
|
|
910
|
+
return /* @__PURE__ */ React9.createElement("div", {
|
|
660
911
|
role: "none",
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
912
|
+
className: mx5(padding)
|
|
913
|
+
}, /* @__PURE__ */ React9.createElement(InputHeader, null, label), /* @__PURE__ */ React9.createElement("div", {
|
|
664
914
|
role: "none",
|
|
665
915
|
className: "flex flex-col gap-1"
|
|
666
|
-
},
|
|
916
|
+
}, values.map((_value, index) => /* @__PURE__ */ React9.createElement("div", {
|
|
667
917
|
key: index,
|
|
668
918
|
role: "none",
|
|
669
|
-
className: "flex items-
|
|
670
|
-
}, /* @__PURE__ */
|
|
919
|
+
className: "flex items-center gap-1"
|
|
920
|
+
}, /* @__PURE__ */ React9.createElement("div", {
|
|
671
921
|
role: "none",
|
|
672
922
|
className: "flex-1"
|
|
673
|
-
}, /* @__PURE__ */
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
923
|
+
}, /* @__PURE__ */ React9.createElement(FormField, {
|
|
924
|
+
property: {
|
|
925
|
+
...property,
|
|
926
|
+
array: false,
|
|
927
|
+
ast: elementType
|
|
928
|
+
},
|
|
929
|
+
path: [
|
|
930
|
+
...path ?? [],
|
|
931
|
+
index
|
|
932
|
+
],
|
|
933
|
+
readonly,
|
|
934
|
+
inline: true,
|
|
935
|
+
Custom,
|
|
936
|
+
lookupComponent
|
|
937
|
+
})), /* @__PURE__ */ React9.createElement(IconButton3, {
|
|
683
938
|
icon: "ph--trash--regular",
|
|
684
939
|
iconOnly: true,
|
|
685
940
|
label: t("button remove"),
|
|
686
|
-
onClick: () =>
|
|
687
|
-
|
|
688
|
-
inputProps.onValueChange(name, type, newValues);
|
|
689
|
-
}
|
|
690
|
-
})))), /* @__PURE__ */ React6.createElement("div", {
|
|
941
|
+
onClick: () => handleRemove(index)
|
|
942
|
+
})))), /* @__PURE__ */ React9.createElement("div", {
|
|
691
943
|
role: "none",
|
|
692
944
|
className: "flex justify-between items-center plb-1"
|
|
693
|
-
}, /* @__PURE__ */
|
|
945
|
+
}, /* @__PURE__ */ React9.createElement(IconButton3, {
|
|
694
946
|
icon: "ph--plus--regular",
|
|
695
947
|
iconOnly: true,
|
|
696
948
|
label: t("button add"),
|
|
697
|
-
onClick:
|
|
698
|
-
const newValues = [
|
|
699
|
-
...arrayValues,
|
|
700
|
-
SimpleType.getDefaultValue(type)
|
|
701
|
-
];
|
|
702
|
-
inputProps.onValueChange(name, type, newValues);
|
|
703
|
-
}
|
|
949
|
+
onClick: handleAdd
|
|
704
950
|
})));
|
|
705
951
|
};
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
952
|
+
|
|
953
|
+
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
954
|
+
import { FormatEnum } from "@dxos/echo-schema";
|
|
955
|
+
var getInputComponent = (type, format) => {
|
|
956
|
+
switch (format) {
|
|
957
|
+
case FormatEnum.GeoPoint:
|
|
958
|
+
return GeoPointInput;
|
|
959
|
+
case FormatEnum.Markdown:
|
|
960
|
+
return MarkdownInput;
|
|
961
|
+
}
|
|
962
|
+
switch (type) {
|
|
963
|
+
case "string":
|
|
964
|
+
return TextInput;
|
|
965
|
+
case "number":
|
|
966
|
+
return NumberInput;
|
|
967
|
+
case "boolean":
|
|
968
|
+
return BooleanInput;
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
973
|
+
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
974
|
+
const inputProps = useInputProps(path);
|
|
975
|
+
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
976
|
+
const label = useMemo5(() => title ?? pipe2(name, capitalize2), [
|
|
977
|
+
title,
|
|
978
|
+
name
|
|
979
|
+
]);
|
|
980
|
+
const placeholder = useMemo5(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
981
|
+
examples,
|
|
982
|
+
description
|
|
983
|
+
]);
|
|
984
|
+
const FoundComponent = lookupComponent?.({
|
|
985
|
+
prop: name,
|
|
986
|
+
schema: S.make(ast),
|
|
987
|
+
inputProps: {
|
|
988
|
+
type,
|
|
989
|
+
format,
|
|
990
|
+
label,
|
|
991
|
+
disabled: readonly,
|
|
992
|
+
placeholder,
|
|
993
|
+
...inputProps
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
if (FoundComponent) {
|
|
997
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
998
|
+
role: "none"
|
|
999
|
+
}, FoundComponent);
|
|
1000
|
+
}
|
|
1001
|
+
const jsonPath = createJsonPath3(path ?? []);
|
|
1002
|
+
const CustomComponent = Custom?.[jsonPath];
|
|
1003
|
+
if (CustomComponent) {
|
|
1004
|
+
return /* @__PURE__ */ React10.createElement(CustomComponent, {
|
|
1005
|
+
type,
|
|
1006
|
+
format,
|
|
1007
|
+
label,
|
|
1008
|
+
inputOnly: inline,
|
|
1009
|
+
placeholder,
|
|
1010
|
+
disabled: readonly,
|
|
1011
|
+
...inputProps
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (array) {
|
|
1015
|
+
return /* @__PURE__ */ React10.createElement(ArrayField, {
|
|
1016
|
+
property,
|
|
1017
|
+
path,
|
|
1018
|
+
inputProps,
|
|
1019
|
+
readonly,
|
|
1020
|
+
Custom
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
const InputComponent = getInputComponent(type, format);
|
|
1024
|
+
if (InputComponent) {
|
|
1025
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
1026
|
+
role: "none"
|
|
1027
|
+
}, /* @__PURE__ */ React10.createElement(InputComponent, {
|
|
1028
|
+
type,
|
|
1029
|
+
format,
|
|
1030
|
+
label,
|
|
1031
|
+
inputOnly: inline,
|
|
1032
|
+
placeholder,
|
|
1033
|
+
disabled: readonly,
|
|
1034
|
+
...inputProps
|
|
1035
|
+
}));
|
|
1036
|
+
}
|
|
1037
|
+
if (options) {
|
|
1038
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
1039
|
+
role: "none"
|
|
1040
|
+
}, /* @__PURE__ */ React10.createElement(SelectInput, {
|
|
1041
|
+
type,
|
|
1042
|
+
format,
|
|
1043
|
+
disabled: readonly,
|
|
1044
|
+
inputOnly: inline,
|
|
1045
|
+
label,
|
|
1046
|
+
options: options.map((option) => ({
|
|
1047
|
+
value: option,
|
|
1048
|
+
label: String(option)
|
|
1049
|
+
})),
|
|
1050
|
+
placeholder,
|
|
1051
|
+
...inputProps
|
|
1052
|
+
}));
|
|
1053
|
+
}
|
|
1054
|
+
if (type === "object") {
|
|
1055
|
+
const baseNode = findNode2(ast, isDiscriminatedUnion2);
|
|
1056
|
+
const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
|
|
1057
|
+
if (typeLiteral) {
|
|
1058
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
1059
|
+
role: "none"
|
|
1060
|
+
}, !inline && /* @__PURE__ */ React10.createElement("div", null, label), /* @__PURE__ */ React10.createElement(FormFields, {
|
|
1061
|
+
schema: S.make(typeLiteral),
|
|
1062
|
+
path,
|
|
1063
|
+
readonly,
|
|
1064
|
+
Custom
|
|
1065
|
+
}));
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
};
|
|
1070
|
+
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
1071
|
+
const values = useFormValues(path);
|
|
1072
|
+
const properties = useMemo5(() => {
|
|
1073
|
+
const props = getSchemaProperties2(schema.ast, values);
|
|
1074
|
+
const filtered = filter ? filter(props) : props;
|
|
1075
|
+
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
1076
|
+
}, [
|
|
1077
|
+
schema,
|
|
1078
|
+
values,
|
|
1079
|
+
filter,
|
|
1080
|
+
sort
|
|
1081
|
+
]);
|
|
1082
|
+
return /* @__PURE__ */ React10.createElement("div", {
|
|
1083
|
+
role: "form",
|
|
1084
|
+
className: mx6("flex flex-col w-full gap-2")
|
|
1085
|
+
}, properties.map((property) => {
|
|
1086
|
+
return /* @__PURE__ */ React10.createElement(FormField, {
|
|
1087
|
+
key: property.name,
|
|
1088
|
+
property,
|
|
1089
|
+
path: [
|
|
1090
|
+
...path ?? [],
|
|
1091
|
+
property.name
|
|
1092
|
+
],
|
|
1093
|
+
readonly,
|
|
1094
|
+
lookupComponent,
|
|
1095
|
+
Custom
|
|
1096
|
+
});
|
|
1097
|
+
}).filter(isNotFalsy));
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
1101
|
+
var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
1102
|
+
const formRef = useRef3(null);
|
|
1103
|
+
const onValid = useMemo6(() => autoSave ? onSave : void 0, [
|
|
1104
|
+
autoSave,
|
|
1105
|
+
onSave
|
|
1106
|
+
]);
|
|
1107
|
+
useEffect6(() => {
|
|
1108
|
+
if (autoFocus && formRef.current) {
|
|
1109
|
+
const input = formRef.current.querySelector("input");
|
|
1110
|
+
if (input) {
|
|
1111
|
+
input.focus();
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}, [
|
|
1115
|
+
autoFocus
|
|
1116
|
+
]);
|
|
1117
|
+
return /* @__PURE__ */ React11.createElement(FormProvider, {
|
|
1118
|
+
formRef,
|
|
1119
|
+
schema,
|
|
1120
|
+
autoSave,
|
|
1121
|
+
initialValues,
|
|
1122
|
+
onValuesChanged,
|
|
1123
|
+
onValidate,
|
|
1124
|
+
onValid,
|
|
1125
|
+
onSave
|
|
1126
|
+
}, /* @__PURE__ */ React11.createElement("div", {
|
|
1127
|
+
ref: formRef,
|
|
712
1128
|
role: "none",
|
|
713
|
-
className:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
1129
|
+
className: mx7("p-2", classNames),
|
|
1130
|
+
"data-testid": testId
|
|
1131
|
+
}, /* @__PURE__ */ React11.createElement(FormFields, {
|
|
1132
|
+
schema,
|
|
1133
|
+
path,
|
|
1134
|
+
readonly,
|
|
1135
|
+
filter,
|
|
1136
|
+
sort,
|
|
1137
|
+
lookupComponent,
|
|
1138
|
+
Custom
|
|
1139
|
+
}), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React11.createElement(FormActions, {
|
|
1140
|
+
onCancel,
|
|
1141
|
+
readonly
|
|
726
1142
|
})));
|
|
727
1143
|
};
|
|
728
1144
|
|
|
729
1145
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
730
1146
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
731
1147
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
732
|
-
const { t } =
|
|
733
|
-
const [props, setProps] =
|
|
734
|
-
|
|
1148
|
+
const { t } = useTranslation5(translationKey);
|
|
1149
|
+
const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
|
|
1150
|
+
useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
735
1151
|
field,
|
|
736
1152
|
projection
|
|
737
1153
|
]);
|
|
738
|
-
const [schemas, setSchemas] =
|
|
739
|
-
|
|
1154
|
+
const [schemas, setSchemas] = useState4([]);
|
|
1155
|
+
useEffect7(() => {
|
|
740
1156
|
if (!registry) {
|
|
741
1157
|
return;
|
|
742
1158
|
}
|
|
@@ -754,17 +1170,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
754
1170
|
}, [
|
|
755
1171
|
registry
|
|
756
1172
|
]);
|
|
757
|
-
const [
|
|
758
|
-
|
|
759
|
-
|
|
1173
|
+
const [referenceSchema, setReferenceSchema] = useState4();
|
|
1174
|
+
useEffect7(() => {
|
|
1175
|
+
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
760
1176
|
}, [
|
|
761
1177
|
schemas,
|
|
762
1178
|
props?.referenceSchema
|
|
763
1179
|
]);
|
|
764
|
-
const [{ fieldSchema }, setFieldSchema] =
|
|
1180
|
+
const [{ fieldSchema }, setFieldSchema] = useState4({
|
|
765
1181
|
fieldSchema: getFormatSchema(props?.format)
|
|
766
1182
|
});
|
|
767
|
-
const handleValuesChanged =
|
|
1183
|
+
const handleValuesChanged = useCallback6((_props) => {
|
|
768
1184
|
setFieldSchema((prev) => {
|
|
769
1185
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
770
1186
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -774,9 +1190,15 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
774
1190
|
fieldSchema: fieldSchema2
|
|
775
1191
|
};
|
|
776
1192
|
});
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1193
|
+
setReferenceSchema((prev) => {
|
|
1194
|
+
if (_props.referenceSchema !== prev?.typename) {
|
|
1195
|
+
const newSchema = schemas.find((schema) => schema.typename === _props.referenceSchema);
|
|
1196
|
+
if (newSchema) {
|
|
1197
|
+
return newSchema;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
return prev;
|
|
1201
|
+
});
|
|
780
1202
|
setProps((props2) => {
|
|
781
1203
|
const type = formatToType[_props.format];
|
|
782
1204
|
if (props2.type !== type) {
|
|
@@ -786,14 +1208,14 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
786
1208
|
type
|
|
787
1209
|
};
|
|
788
1210
|
}
|
|
789
|
-
return
|
|
1211
|
+
return _props;
|
|
790
1212
|
});
|
|
791
1213
|
}, [
|
|
792
1214
|
schemas,
|
|
793
1215
|
props.format,
|
|
794
1216
|
props.referenceSchema
|
|
795
1217
|
]);
|
|
796
|
-
const handleValidate =
|
|
1218
|
+
const handleValidate = useCallback6(({ property }) => {
|
|
797
1219
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
798
1220
|
return [
|
|
799
1221
|
{
|
|
@@ -806,7 +1228,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
806
1228
|
view.fields,
|
|
807
1229
|
field
|
|
808
1230
|
]);
|
|
809
|
-
const handleSave =
|
|
1231
|
+
const handleSave = useCallback6((props2) => {
|
|
810
1232
|
projection.setFieldProjection({
|
|
811
1233
|
field,
|
|
812
1234
|
props: props2
|
|
@@ -817,29 +1239,56 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
817
1239
|
field,
|
|
818
1240
|
onSave
|
|
819
1241
|
]);
|
|
820
|
-
const handleCancel =
|
|
1242
|
+
const handleCancel = useCallback6(() => {
|
|
821
1243
|
requestAnimationFrame(() => onCancel?.());
|
|
822
1244
|
onSave();
|
|
823
1245
|
}, [
|
|
824
1246
|
onSave
|
|
825
1247
|
]);
|
|
1248
|
+
const Custom = useMemo7(() => ({
|
|
1249
|
+
["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1250
|
+
...props2,
|
|
1251
|
+
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
1252
|
+
value,
|
|
1253
|
+
label: t(`format ${value}`)
|
|
1254
|
+
}))
|
|
1255
|
+
}),
|
|
1256
|
+
["referenceSchema"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1257
|
+
...props2,
|
|
1258
|
+
options: schemas.map((schema) => ({
|
|
1259
|
+
value: schema.typename
|
|
1260
|
+
}))
|
|
1261
|
+
}),
|
|
1262
|
+
["referencePath"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1263
|
+
...props2,
|
|
1264
|
+
options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
|
|
1265
|
+
value: p.name
|
|
1266
|
+
})) : []
|
|
1267
|
+
}),
|
|
1268
|
+
["options"]: (props2) => /* @__PURE__ */ React12.createElement(SelectOptionInput, props2)
|
|
1269
|
+
}), [
|
|
1270
|
+
t,
|
|
1271
|
+
schemas,
|
|
1272
|
+
referenceSchema
|
|
1273
|
+
]);
|
|
1274
|
+
const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
826
1275
|
if (!fieldSchema) {
|
|
827
|
-
|
|
1276
|
+
log2.warn("invalid format", {
|
|
828
1277
|
props
|
|
829
1278
|
}, {
|
|
830
1279
|
F: __dxlog_file3,
|
|
831
|
-
L:
|
|
1280
|
+
L: 174,
|
|
832
1281
|
S: void 0,
|
|
833
1282
|
C: (f, a) => f(...a)
|
|
834
1283
|
});
|
|
835
1284
|
return null;
|
|
836
1285
|
}
|
|
837
|
-
return /* @__PURE__ */
|
|
1286
|
+
return /* @__PURE__ */ React12.createElement(Form, {
|
|
838
1287
|
key: field.id,
|
|
839
1288
|
autoFocus: true,
|
|
840
1289
|
values: props,
|
|
841
1290
|
schema: fieldSchema,
|
|
842
|
-
filter:
|
|
1291
|
+
filter: propIsNotType,
|
|
843
1292
|
sort: [
|
|
844
1293
|
"property",
|
|
845
1294
|
"format"
|
|
@@ -848,55 +1297,34 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
848
1297
|
onValidate: handleValidate,
|
|
849
1298
|
onSave: handleSave,
|
|
850
1299
|
onCancel: handleCancel,
|
|
851
|
-
Custom
|
|
852
|
-
["format"]: (props2) => /* @__PURE__ */ React7.createElement(SelectInput, {
|
|
853
|
-
...props2,
|
|
854
|
-
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
855
|
-
value,
|
|
856
|
-
label: t(`format ${value}`)
|
|
857
|
-
}))
|
|
858
|
-
}),
|
|
859
|
-
["referenceSchema"]: (props2) => /* @__PURE__ */ React7.createElement(SelectInput, {
|
|
860
|
-
...props2,
|
|
861
|
-
options: schemas.map((schema2) => ({
|
|
862
|
-
value: schema2.typename
|
|
863
|
-
}))
|
|
864
|
-
}),
|
|
865
|
-
["referencePath"]: (props2) => /* @__PURE__ */ React7.createElement(SelectInput, {
|
|
866
|
-
...props2,
|
|
867
|
-
options: schema ? getSchemaProperties2(schema.ast).sort(sortProperties).map((p) => ({
|
|
868
|
-
value: p.name
|
|
869
|
-
})) : []
|
|
870
|
-
})
|
|
871
|
-
}
|
|
1300
|
+
Custom
|
|
872
1301
|
});
|
|
873
1302
|
};
|
|
874
1303
|
|
|
875
1304
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
876
|
-
import
|
|
877
|
-
import { AST as
|
|
878
|
-
import { IconButton as
|
|
879
|
-
import { List } from "@dxos/react-ui-list";
|
|
880
|
-
import { ghostHover, inputTextLabel, mx as
|
|
1305
|
+
import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
|
|
1306
|
+
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
1307
|
+
import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
|
|
1308
|
+
import { List as List2 } from "@dxos/react-ui-list";
|
|
1309
|
+
import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
|
|
881
1310
|
import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
882
|
-
import { arrayMove } from "@dxos/util";
|
|
883
1311
|
var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
|
|
884
1312
|
var ViewMetaSchema = S2.Struct({
|
|
885
1313
|
name: S2.String.annotations({
|
|
886
|
-
[
|
|
1314
|
+
[AST4.TitleAnnotationId]: "View"
|
|
887
1315
|
}),
|
|
888
|
-
typename: Format.
|
|
889
|
-
[
|
|
1316
|
+
typename: Format.URL.annotations({
|
|
1317
|
+
[AST4.TitleAnnotationId]: "Typename"
|
|
890
1318
|
})
|
|
891
1319
|
}).pipe(S2.mutable);
|
|
892
|
-
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onDelete }) => {
|
|
893
|
-
const { t } =
|
|
894
|
-
const projection =
|
|
1320
|
+
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
1321
|
+
const { t } = useTranslation6(translationKey);
|
|
1322
|
+
const projection = useMemo8(() => new ViewProjection(schema, view), [
|
|
895
1323
|
schema,
|
|
896
1324
|
view
|
|
897
1325
|
]);
|
|
898
|
-
const [field, setField] =
|
|
899
|
-
const viewValues =
|
|
1326
|
+
const [field, setField] = useState5();
|
|
1327
|
+
const viewValues = useMemo8(() => {
|
|
900
1328
|
return {
|
|
901
1329
|
name: view.name,
|
|
902
1330
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -906,77 +1334,85 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
906
1334
|
}, [
|
|
907
1335
|
view
|
|
908
1336
|
]);
|
|
909
|
-
const handleViewUpdate =
|
|
1337
|
+
const handleViewUpdate = useCallback7(({ name, typename }) => {
|
|
910
1338
|
requestAnimationFrame(() => {
|
|
911
|
-
view.name
|
|
912
|
-
|
|
913
|
-
|
|
1339
|
+
if (view.name !== name) {
|
|
1340
|
+
view.name = name;
|
|
1341
|
+
}
|
|
1342
|
+
if (view.query.type !== typename) {
|
|
1343
|
+
onTypenameChanged(typename);
|
|
1344
|
+
}
|
|
914
1345
|
});
|
|
915
1346
|
}, [
|
|
916
1347
|
view,
|
|
917
|
-
schema
|
|
1348
|
+
schema,
|
|
1349
|
+
onTypenameChanged
|
|
918
1350
|
]);
|
|
919
|
-
const handleSelect =
|
|
1351
|
+
const handleSelect = useCallback7((field2) => {
|
|
920
1352
|
setField((f) => f === field2 ? void 0 : field2);
|
|
921
1353
|
}, []);
|
|
922
|
-
const handleAdd =
|
|
1354
|
+
const handleAdd = useCallback7(() => {
|
|
923
1355
|
const field2 = projection.createFieldProjection();
|
|
924
1356
|
setField(field2);
|
|
925
1357
|
}, [
|
|
926
1358
|
view
|
|
927
1359
|
]);
|
|
928
|
-
const handleMove =
|
|
929
|
-
|
|
1360
|
+
const handleMove = useCallback7((fromIndex, toIndex) => {
|
|
1361
|
+
const fields = [
|
|
1362
|
+
...view.fields
|
|
1363
|
+
];
|
|
1364
|
+
const [moved] = fields.splice(fromIndex, 1);
|
|
1365
|
+
fields.splice(toIndex, 0, moved);
|
|
1366
|
+
view.fields = fields;
|
|
930
1367
|
}, [
|
|
931
1368
|
view.fields
|
|
932
1369
|
]);
|
|
933
|
-
const handleClose =
|
|
934
|
-
return /* @__PURE__ */
|
|
1370
|
+
const handleClose = useCallback7(() => setField(void 0), []);
|
|
1371
|
+
return /* @__PURE__ */ React13.createElement("div", {
|
|
935
1372
|
role: "none",
|
|
936
|
-
className:
|
|
937
|
-
}, /* @__PURE__ */
|
|
938
|
-
autoFocus: true,
|
|
1373
|
+
className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
|
|
1374
|
+
}, /* @__PURE__ */ React13.createElement(Form, {
|
|
939
1375
|
autoSave: true,
|
|
940
1376
|
schema: ViewMetaSchema,
|
|
941
1377
|
values: viewValues,
|
|
942
1378
|
onSave: handleViewUpdate
|
|
943
|
-
}), /* @__PURE__ */
|
|
1379
|
+
}), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
|
|
944
1380
|
role: "none",
|
|
945
1381
|
className: "p-2"
|
|
946
|
-
}, /* @__PURE__ */
|
|
947
|
-
className:
|
|
948
|
-
}, t("fields label"))), /* @__PURE__ */
|
|
1382
|
+
}, /* @__PURE__ */ React13.createElement("label", {
|
|
1383
|
+
className: mx8(inputTextLabel)
|
|
1384
|
+
}, t("fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
|
|
949
1385
|
items: view.fields,
|
|
950
1386
|
isItem: S2.is(FieldSchema),
|
|
951
1387
|
getId: (field2) => field2.id,
|
|
952
1388
|
onMove: handleMove
|
|
953
|
-
}, ({ items: fields }) => /* @__PURE__ */
|
|
1389
|
+
}, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
|
|
954
1390
|
role: "heading",
|
|
955
1391
|
className: grid
|
|
956
|
-
}, /* @__PURE__ */
|
|
1392
|
+
}, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement("div", {
|
|
957
1393
|
className: "flex items-center text-sm"
|
|
958
|
-
}, t("field path label"))), /* @__PURE__ */
|
|
1394
|
+
}, t("field path label"))), /* @__PURE__ */ React13.createElement("div", {
|
|
959
1395
|
role: "list",
|
|
960
1396
|
className: "flex flex-col w-full"
|
|
961
|
-
}, fields?.map((field2) => /* @__PURE__ */
|
|
1397
|
+
}, fields?.map((field2) => /* @__PURE__ */ React13.createElement(List2.Item, {
|
|
962
1398
|
key: field2.id,
|
|
963
1399
|
item: field2,
|
|
964
|
-
classNames:
|
|
965
|
-
}, /* @__PURE__ */
|
|
1400
|
+
classNames: mx8(grid, ghostHover, "cursor-pointer")
|
|
1401
|
+
}, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
|
|
966
1402
|
onClick: () => handleSelect(field2)
|
|
967
|
-
}, field2.path), /* @__PURE__ */
|
|
1403
|
+
}, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
|
|
968
1404
|
disabled: view.fields.length <= 1,
|
|
969
1405
|
onClick: () => onDelete(field2.id)
|
|
970
|
-
}))))))), field && /* @__PURE__ */
|
|
1406
|
+
}))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
|
|
971
1407
|
key: field.id,
|
|
972
1408
|
view,
|
|
973
1409
|
projection,
|
|
974
1410
|
field,
|
|
975
1411
|
registry,
|
|
976
1412
|
onSave: handleClose
|
|
977
|
-
}), !readonly && !field && /* @__PURE__ */
|
|
1413
|
+
}), !readonly && !field && /* @__PURE__ */ React13.createElement("div", {
|
|
978
1414
|
className: "flex p-2 justify-center"
|
|
979
|
-
}, /* @__PURE__ */
|
|
1415
|
+
}, /* @__PURE__ */ React13.createElement(IconButton4, {
|
|
980
1416
|
icon: "ph--plus--regular",
|
|
981
1417
|
label: t("button add property"),
|
|
982
1418
|
onClick: handleAdd,
|
|
@@ -986,7 +1422,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
986
1422
|
};
|
|
987
1423
|
|
|
988
1424
|
// packages/ui/react-ui-form/src/util/field.ts
|
|
989
|
-
import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
|
|
1425
|
+
import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
|
|
990
1426
|
var parseValue = ({ type, format, value }) => {
|
|
991
1427
|
if (value === void 0 || value === null || value === "") {
|
|
992
1428
|
return void 0;
|
|
@@ -1040,6 +1476,26 @@ var parseValue = ({ type, format, value }) => {
|
|
|
1040
1476
|
const date = new Date(value);
|
|
1041
1477
|
return isNaN(date.getTime()) ? null : date;
|
|
1042
1478
|
}
|
|
1479
|
+
case FormatEnum3.GeoPoint: {
|
|
1480
|
+
if (typeof value === "string") {
|
|
1481
|
+
const cleaned = value.replace(/[^0-9.,-]+/g, "");
|
|
1482
|
+
const parts = cleaned.split(",");
|
|
1483
|
+
if (parts.length === 2) {
|
|
1484
|
+
const lat = parseFloat(parts[0]);
|
|
1485
|
+
const long = parseFloat(parts[1]);
|
|
1486
|
+
if (!isNaN(lat) && !isNaN(long)) {
|
|
1487
|
+
return GeoLocation2.toGeoPoint({
|
|
1488
|
+
latitude: lat,
|
|
1489
|
+
longitude: long
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return [
|
|
1495
|
+
0,
|
|
1496
|
+
0
|
|
1497
|
+
];
|
|
1498
|
+
}
|
|
1043
1499
|
default: {
|
|
1044
1500
|
return value;
|
|
1045
1501
|
}
|
|
@@ -1098,7 +1554,7 @@ var pathNotUniqueError = (path) => ({
|
|
|
1098
1554
|
});
|
|
1099
1555
|
|
|
1100
1556
|
// packages/ui/react-ui-form/src/util/formatting.ts
|
|
1101
|
-
import { FormatEnum as FormatEnum4, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
|
|
1557
|
+
import { FormatEnum as FormatEnum4, GeoLocation as GeoLocation3, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
|
|
1102
1558
|
var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
1103
1559
|
const formatScalar = (type2) => {
|
|
1104
1560
|
switch (type2) {
|
|
@@ -1151,6 +1607,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
|
1151
1607
|
return date.toLocaleTimeString(locale);
|
|
1152
1608
|
}
|
|
1153
1609
|
}
|
|
1610
|
+
case FormatEnum4.GeoPoint: {
|
|
1611
|
+
if (value === null || value === void 0) {
|
|
1612
|
+
return "";
|
|
1613
|
+
}
|
|
1614
|
+
if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
|
|
1615
|
+
const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
|
|
1616
|
+
return `${latitude},${longitude}`;
|
|
1617
|
+
}
|
|
1618
|
+
return String(value);
|
|
1619
|
+
}
|
|
1154
1620
|
default: {
|
|
1155
1621
|
if (value === null || value === "undefined") {
|
|
1156
1622
|
return "";
|
|
@@ -1199,6 +1665,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1199
1665
|
const date = new Date(value);
|
|
1200
1666
|
return date.toISOString().split("T")[1].split(".")[0];
|
|
1201
1667
|
}
|
|
1668
|
+
case FormatEnum4.GeoPoint: {
|
|
1669
|
+
if (value === null || value === void 0) {
|
|
1670
|
+
return "";
|
|
1671
|
+
}
|
|
1672
|
+
if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
|
|
1673
|
+
const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
|
|
1674
|
+
return `${latitude},${longitude}`;
|
|
1675
|
+
}
|
|
1676
|
+
return String(value);
|
|
1677
|
+
}
|
|
1202
1678
|
default: {
|
|
1203
1679
|
return String(value);
|
|
1204
1680
|
}
|
|
@@ -1206,14 +1682,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1206
1682
|
};
|
|
1207
1683
|
export {
|
|
1208
1684
|
BooleanInput,
|
|
1685
|
+
DeprecatedFormContainer,
|
|
1209
1686
|
DeprecatedFormInput,
|
|
1210
1687
|
FieldEditor,
|
|
1211
1688
|
Form,
|
|
1212
|
-
FormActions,
|
|
1213
1689
|
GeoPointInput,
|
|
1214
1690
|
InputHeader,
|
|
1691
|
+
MarkdownInput,
|
|
1215
1692
|
NumberInput,
|
|
1216
1693
|
SelectInput,
|
|
1694
|
+
SelectOptionInput,
|
|
1217
1695
|
TextInput,
|
|
1218
1696
|
TupleInput,
|
|
1219
1697
|
ViewEditor,
|
|
@@ -1224,6 +1702,7 @@ export {
|
|
|
1224
1702
|
parseValue,
|
|
1225
1703
|
pathNotUniqueError,
|
|
1226
1704
|
translations_default as translations,
|
|
1227
|
-
useForm
|
|
1705
|
+
useForm,
|
|
1706
|
+
useFormValues
|
|
1228
1707
|
};
|
|
1229
1708
|
//# sourceMappingURL=index.mjs.map
|