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