@dxos/react-ui-form 0.7.5-labs.e27f9b9 → 0.7.5-labs.f400bbc
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 +546 -362
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +589 -401
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +546 -362
- 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 +1 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +2 -2
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +1 -2
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +2 -3
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +4 -1
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +2 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/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 +1 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.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 +2 -2
- 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 +1 -0
- package/dist/types/src/hooks/useForm.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/package.json +25 -24
- package/src/components/Form/Defaults.tsx +61 -4
- package/src/components/Form/Form.tsx +9 -5
- package/src/components/Form/FormActions.tsx +1 -1
- package/src/components/Form/FormContent.tsx +10 -13
- package/src/components/Form/FormContext.tsx +58 -10
- package/src/components/Form/custom/GeoPointInput.tsx +42 -12
- package/src/components/Form/custom/SelectOptionsInput.tsx +7 -9
- package/src/components/Form/factory.tsx +4 -2
- package/src/components/Form/index.ts +3 -0
- package/src/components/ViewEditor/ViewEditor.tsx +2 -2
- package/src/hooks/useForm.ts +69 -40
- package/src/translations.ts +1 -1
- package/src/util/field.ts +19 -1
- package/src/util/formatting.ts +29 -1
|
@@ -31,7 +31,7 @@ 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",
|
|
@@ -101,85 +101,394 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
104
|
-
import React12, { useCallback as
|
|
104
|
+
import React12, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState4 } from "react";
|
|
105
105
|
import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
|
|
106
106
|
import { log as log2 } from "@dxos/log";
|
|
107
107
|
import { useTranslation as useTranslation5 } from "@dxos/react-ui";
|
|
108
108
|
import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
|
|
109
109
|
|
|
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";
|
|
115
|
+
|
|
116
|
+
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
117
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
118
|
+
import { getValue, setValue } from "@dxos/echo-schema";
|
|
119
|
+
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
120
|
+
import { invariant } from "@dxos/invariant";
|
|
121
|
+
import { log } from "@dxos/log";
|
|
122
|
+
import { validateSchema } from "@dxos/schema";
|
|
123
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
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
|
+
});
|
|
134
|
+
const [values, setValues] = useState(initialValues);
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
setValues(initialValues);
|
|
137
|
+
}, [
|
|
138
|
+
initialValues
|
|
139
|
+
]);
|
|
140
|
+
const [touched, setTouched] = useState(createKeySet(initialValues, false));
|
|
141
|
+
const [changed, setChanged] = useState(createKeySet(initialValues, false));
|
|
142
|
+
const [errors, setErrors] = useState({});
|
|
143
|
+
const [saving, setSaving] = useState(false);
|
|
144
|
+
const validate = useCallback((values2) => {
|
|
145
|
+
let errors2 = validateSchema(schema, values2) ?? [];
|
|
146
|
+
if (errors2.length === 0 && onValidate) {
|
|
147
|
+
const validatedValues = values2;
|
|
148
|
+
errors2 = onValidate(validatedValues) ?? [];
|
|
149
|
+
}
|
|
150
|
+
setErrors(flatMap(errors2));
|
|
151
|
+
const valid = errors2.length === 0;
|
|
152
|
+
return valid;
|
|
153
|
+
}, [
|
|
154
|
+
schema,
|
|
155
|
+
onValidate
|
|
156
|
+
]);
|
|
157
|
+
const [schemaDirty, setSchemaDirty] = useState(false);
|
|
158
|
+
useEffect(() => setSchemaDirty(true), [
|
|
159
|
+
schema
|
|
160
|
+
]);
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (schemaDirty) {
|
|
163
|
+
validate(values);
|
|
164
|
+
setSchemaDirty(false);
|
|
165
|
+
}
|
|
166
|
+
}, [
|
|
167
|
+
values,
|
|
168
|
+
schemaDirty
|
|
169
|
+
]);
|
|
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}[`))), [
|
|
177
|
+
touched,
|
|
178
|
+
errors,
|
|
179
|
+
saving
|
|
180
|
+
]);
|
|
181
|
+
const handleSave = useCallback(async () => {
|
|
182
|
+
if (validate(values)) {
|
|
183
|
+
setSaving(true);
|
|
184
|
+
try {
|
|
185
|
+
await onSave?.(values, {
|
|
186
|
+
changed
|
|
187
|
+
});
|
|
188
|
+
} finally {
|
|
189
|
+
setSaving(false);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}, [
|
|
193
|
+
values,
|
|
194
|
+
validate,
|
|
195
|
+
onSave,
|
|
196
|
+
changed
|
|
197
|
+
]);
|
|
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
|
+
}, [
|
|
206
|
+
errors
|
|
207
|
+
]);
|
|
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);
|
|
215
|
+
let parsedValue = value;
|
|
216
|
+
try {
|
|
217
|
+
if (type === "number") {
|
|
218
|
+
parsedValue = parseFloat(value) || 0;
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
log.catch(err, void 0, {
|
|
222
|
+
F: __dxlog_file,
|
|
223
|
+
L: 202,
|
|
224
|
+
S: void 0,
|
|
225
|
+
C: (f, a) => f(...a)
|
|
226
|
+
});
|
|
227
|
+
parsedValue = void 0;
|
|
228
|
+
}
|
|
229
|
+
const newValues = {
|
|
230
|
+
...setValue(values, jsonPath, parsedValue)
|
|
231
|
+
};
|
|
232
|
+
setValues(newValues);
|
|
233
|
+
setChanged((prev) => ({
|
|
234
|
+
...prev,
|
|
235
|
+
[jsonPath]: true
|
|
236
|
+
}));
|
|
237
|
+
onValuesChanged?.(newValues);
|
|
238
|
+
const isValid = validate(newValues);
|
|
239
|
+
if (isValid && onValid) {
|
|
240
|
+
onValid(newValues, {
|
|
241
|
+
changed
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}, [
|
|
245
|
+
values,
|
|
246
|
+
onValuesChanged,
|
|
247
|
+
validate,
|
|
248
|
+
onValid,
|
|
249
|
+
changed
|
|
250
|
+
]);
|
|
251
|
+
const onTouched = useCallback((path) => {
|
|
252
|
+
const jsonPath = createJsonPath(path);
|
|
253
|
+
setTouched((touched2) => ({
|
|
254
|
+
...touched2,
|
|
255
|
+
[jsonPath]: true
|
|
256
|
+
}));
|
|
257
|
+
validate(values);
|
|
258
|
+
}, [
|
|
259
|
+
validate,
|
|
260
|
+
values
|
|
261
|
+
]);
|
|
262
|
+
return useMemo(() => ({
|
|
263
|
+
// State.
|
|
264
|
+
values,
|
|
265
|
+
errors,
|
|
266
|
+
touched,
|
|
267
|
+
changed,
|
|
268
|
+
canSave,
|
|
269
|
+
formIsValid,
|
|
270
|
+
// Actions.
|
|
271
|
+
handleSave,
|
|
272
|
+
// Field utils.
|
|
273
|
+
getStatus,
|
|
274
|
+
getValue: getFormValue,
|
|
275
|
+
onValueChange,
|
|
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
|
+
]);
|
|
290
|
+
};
|
|
291
|
+
var createKeySet = (obj, value) => {
|
|
292
|
+
invariant(obj, void 0, {
|
|
293
|
+
F: __dxlog_file,
|
|
294
|
+
L: 264,
|
|
295
|
+
S: void 0,
|
|
296
|
+
A: [
|
|
297
|
+
"obj",
|
|
298
|
+
""
|
|
299
|
+
]
|
|
300
|
+
});
|
|
301
|
+
return Object.keys(obj).reduce((acc, key) => ({
|
|
302
|
+
...acc,
|
|
303
|
+
[key]: value
|
|
304
|
+
}), {});
|
|
305
|
+
};
|
|
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
|
+
|
|
110
381
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
111
|
-
import
|
|
382
|
+
import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo3 } from "react";
|
|
383
|
+
import { GeoLocation } from "@dxos/echo-schema";
|
|
112
384
|
import { Input as Input2, useTranslation } from "@dxos/react-ui";
|
|
113
385
|
import { safeParseFloat } from "@dxos/util";
|
|
114
386
|
|
|
115
387
|
// packages/ui/react-ui-form/src/components/Form/Input.tsx
|
|
116
|
-
import
|
|
388
|
+
import React3 from "react";
|
|
117
389
|
import { Icon, Tooltip } from "@dxos/react-ui";
|
|
118
390
|
import { errorText } from "@dxos/react-ui-theme";
|
|
119
391
|
var InputHeader = ({ children, error }) => {
|
|
120
|
-
return /* @__PURE__ */
|
|
392
|
+
return /* @__PURE__ */ React3.createElement("div", {
|
|
121
393
|
role: "none",
|
|
122
394
|
className: "flex justify-between items-center mb-1"
|
|
123
|
-
}, children, error && /* @__PURE__ */
|
|
395
|
+
}, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
|
|
124
396
|
asChild: true
|
|
125
|
-
}, /* @__PURE__ */
|
|
397
|
+
}, /* @__PURE__ */ React3.createElement(Icon, {
|
|
126
398
|
icon: "ph--warning--regular",
|
|
127
399
|
size: 4,
|
|
128
400
|
classNames: errorText
|
|
129
|
-
})), /* @__PURE__ */
|
|
401
|
+
})), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
|
|
130
402
|
side: "bottom"
|
|
131
|
-
}, /* @__PURE__ */
|
|
403
|
+
}, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
|
|
132
404
|
};
|
|
133
405
|
|
|
134
406
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
135
407
|
var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
136
408
|
const { t } = useTranslation(translationKey);
|
|
137
409
|
const { status, error } = getStatus();
|
|
138
|
-
const
|
|
139
|
-
|
|
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, {
|
|
140
452
|
validationValence: status
|
|
141
|
-
}, /* @__PURE__ */
|
|
453
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
142
454
|
error
|
|
143
|
-
}, /* @__PURE__ */
|
|
455
|
+
}, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
|
|
456
|
+
role: "none",
|
|
144
457
|
className: "grid grid-cols-2 gap-2"
|
|
145
|
-
}, /* @__PURE__ */
|
|
146
|
-
type: "
|
|
458
|
+
}, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
459
|
+
type: "text",
|
|
460
|
+
pattern: "^-?[0-9]*\\.?[0-9]*$",
|
|
147
461
|
disabled,
|
|
148
|
-
placeholder: t("placeholder
|
|
149
|
-
value:
|
|
150
|
-
onChange: (
|
|
151
|
-
lat,
|
|
152
|
-
safeParseFloat(event.target.value, 0)
|
|
153
|
-
]),
|
|
462
|
+
placeholder: t("placeholder latitude"),
|
|
463
|
+
value: latitudeText,
|
|
464
|
+
onChange: handleCoordinateChange("latitude", setLatitudeText),
|
|
154
465
|
onBlur
|
|
155
|
-
}), /* @__PURE__ */
|
|
156
|
-
type: "
|
|
466
|
+
}), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
467
|
+
type: "text",
|
|
468
|
+
pattern: "^-?[0-9]*\\.?[0-9]*$",
|
|
157
469
|
disabled,
|
|
158
|
-
placeholder: t("placeholder
|
|
159
|
-
value:
|
|
160
|
-
onChange: (
|
|
161
|
-
safeParseFloat(event.target.value, 0),
|
|
162
|
-
lng
|
|
163
|
-
]),
|
|
470
|
+
placeholder: t("placeholder longitude"),
|
|
471
|
+
value: longitudeText,
|
|
472
|
+
onChange: handleCoordinateChange("longitude", setLongitudeText),
|
|
164
473
|
onBlur
|
|
165
474
|
})));
|
|
166
475
|
};
|
|
167
476
|
|
|
168
477
|
// packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
|
|
169
|
-
import
|
|
478
|
+
import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
|
|
170
479
|
import { PublicKey } from "@dxos/keys";
|
|
171
480
|
import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
172
481
|
import { List } from "@dxos/react-ui-list";
|
|
173
|
-
import {
|
|
174
|
-
import {
|
|
482
|
+
import { HuePicker } from "@dxos/react-ui-pickers";
|
|
483
|
+
import { hues, mx as mx2 } from "@dxos/react-ui-theme";
|
|
175
484
|
var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
|
|
176
|
-
const [selected, setSelectedId] =
|
|
177
|
-
const [isNewOption, setIsNewOption] =
|
|
485
|
+
const [selected, setSelectedId] = useState3(null);
|
|
486
|
+
const [isNewOption, setIsNewOption] = useState3(false);
|
|
178
487
|
const inputRef = useRef(null);
|
|
179
488
|
const { t } = useTranslation2(translationKey);
|
|
180
489
|
const { status, error } = getStatus();
|
|
181
490
|
const options = getValue3();
|
|
182
|
-
|
|
491
|
+
useEffect4(() => {
|
|
183
492
|
if (options === void 0) {
|
|
184
493
|
onValueChange(type, []);
|
|
185
494
|
}
|
|
@@ -190,16 +499,15 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
190
499
|
]);
|
|
191
500
|
const randomHue = () => {
|
|
192
501
|
const usedHues = new Set(options?.map((option) => option.color) ?? []);
|
|
193
|
-
const
|
|
194
|
-
const unusedHues = hueKeys.filter((hue) => !usedHues.has(hue));
|
|
502
|
+
const unusedHues = hues.filter((hue) => !usedHues.has(hue));
|
|
195
503
|
if (unusedHues.length > 0) {
|
|
196
504
|
return unusedHues[Math.floor(Math.random() * unusedHues.length)];
|
|
197
505
|
}
|
|
198
506
|
const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
|
|
199
|
-
const availableHues =
|
|
200
|
-
return availableHues[Math.floor(Math.random() * availableHues.length)] ??
|
|
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)];
|
|
201
509
|
};
|
|
202
|
-
const handleAdd =
|
|
510
|
+
const handleAdd = useCallback3(() => {
|
|
203
511
|
const newId = PublicKey.random().truncate();
|
|
204
512
|
const newOption = {
|
|
205
513
|
id: newId,
|
|
@@ -217,7 +525,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
217
525
|
type,
|
|
218
526
|
onValueChange
|
|
219
527
|
]);
|
|
220
|
-
const handleDelete =
|
|
528
|
+
const handleDelete = useCallback3((id) => {
|
|
221
529
|
const newOptions = options?.filter((option) => option.id !== id) ?? [];
|
|
222
530
|
onValueChange(type, newOptions);
|
|
223
531
|
setSelectedId(null);
|
|
@@ -226,7 +534,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
226
534
|
type,
|
|
227
535
|
onValueChange
|
|
228
536
|
]);
|
|
229
|
-
const handleMove =
|
|
537
|
+
const handleMove = useCallback3((from, to) => {
|
|
230
538
|
if (!options) {
|
|
231
539
|
return;
|
|
232
540
|
}
|
|
@@ -241,10 +549,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
241
549
|
type,
|
|
242
550
|
onValueChange
|
|
243
551
|
]);
|
|
244
|
-
const handleClick =
|
|
552
|
+
const handleClick = useCallback3((id) => {
|
|
245
553
|
setSelectedId((current) => current === id ? null : id);
|
|
246
554
|
}, []);
|
|
247
|
-
const handleTitleChange =
|
|
555
|
+
const handleTitleChange = useCallback3((id) => (e) => {
|
|
248
556
|
const newOptions = options?.map((o) => o.id === id ? {
|
|
249
557
|
...o,
|
|
250
558
|
title: e.target.value
|
|
@@ -255,12 +563,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
255
563
|
type,
|
|
256
564
|
onValueChange
|
|
257
565
|
]);
|
|
258
|
-
const handleKeyDown =
|
|
566
|
+
const handleKeyDown = useCallback3((e) => {
|
|
259
567
|
if (e.key === "Enter") {
|
|
260
568
|
setSelectedId(null);
|
|
261
569
|
}
|
|
262
570
|
}, []);
|
|
263
|
-
const handleColorChange =
|
|
571
|
+
const handleColorChange = useCallback3((id) => (hue) => {
|
|
264
572
|
const newOptions = options?.map((o) => o.id === id ? {
|
|
265
573
|
...o,
|
|
266
574
|
color: hue
|
|
@@ -271,7 +579,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
271
579
|
type,
|
|
272
580
|
onValueChange
|
|
273
581
|
]);
|
|
274
|
-
|
|
582
|
+
useEffect4(() => {
|
|
275
583
|
if (selected && isNewOption && inputRef.current) {
|
|
276
584
|
inputRef.current.focus();
|
|
277
585
|
setIsNewOption(false);
|
|
@@ -280,76 +588,77 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
280
588
|
selected,
|
|
281
589
|
isNewOption
|
|
282
590
|
]);
|
|
283
|
-
return /* @__PURE__ */
|
|
591
|
+
return /* @__PURE__ */ React5.createElement(Input3.Root, {
|
|
284
592
|
validationValence: status
|
|
285
|
-
}, /* @__PURE__ */
|
|
593
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
286
594
|
error
|
|
287
|
-
}, /* @__PURE__ */
|
|
595
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
|
|
288
596
|
role: "none"
|
|
289
|
-
}, options && /* @__PURE__ */
|
|
597
|
+
}, options && /* @__PURE__ */ React5.createElement(List.Root, {
|
|
290
598
|
items: options,
|
|
291
599
|
isItem: (item) => true,
|
|
292
600
|
onMove: handleMove
|
|
293
|
-
}, ({ items }) => /* @__PURE__ */
|
|
601
|
+
}, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
|
|
294
602
|
role: "list",
|
|
295
603
|
className: "w-full overflow-auto"
|
|
296
|
-
}, items?.map((item) => /* @__PURE__ */
|
|
604
|
+
}, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
|
|
297
605
|
key: item.id
|
|
298
|
-
}, /* @__PURE__ */
|
|
606
|
+
}, /* @__PURE__ */ React5.createElement(List.Item, {
|
|
299
607
|
role: "button",
|
|
300
608
|
item,
|
|
301
609
|
classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
302
610
|
"aria-expanded": selected === item.id
|
|
303
|
-
}, /* @__PURE__ */
|
|
611
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
304
612
|
className: "flex items-center"
|
|
305
|
-
}, /* @__PURE__ */
|
|
613
|
+
}, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
|
|
306
614
|
onClick: () => handleClick(item.id),
|
|
307
615
|
classNames: "flex-1"
|
|
308
|
-
}, /* @__PURE__ */
|
|
616
|
+
}, /* @__PURE__ */ React5.createElement(Tag, {
|
|
309
617
|
palette: item.color
|
|
310
|
-
}, item.title || "\u200B")), /* @__PURE__ */
|
|
618
|
+
}, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
|
|
311
619
|
icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
|
|
312
620
|
onClick: () => handleClick(item.id)
|
|
313
|
-
})), selected === item.id && /* @__PURE__ */
|
|
621
|
+
})), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
|
|
314
622
|
className: "mis-4 mbs-2 flex flex-col gap-1"
|
|
315
|
-
}, /* @__PURE__ */
|
|
623
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, {
|
|
316
624
|
classNames: "text-xs"
|
|
317
|
-
}, t("select option label")), /* @__PURE__ */
|
|
625
|
+
}, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
|
|
318
626
|
className: "flex flex-row items-center gap-1"
|
|
319
|
-
}, /* @__PURE__ */
|
|
627
|
+
}, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
|
|
320
628
|
placeholder: t("select option label placeholder"),
|
|
321
629
|
ref: selected === item.id ? inputRef : void 0,
|
|
322
630
|
value: item.title,
|
|
323
631
|
onChange: handleTitleChange(item.id),
|
|
324
632
|
onKeyDown: handleKeyDown,
|
|
325
|
-
classNames: "flex-1"
|
|
326
|
-
|
|
633
|
+
classNames: "flex-1",
|
|
634
|
+
"data-no-submit": true
|
|
635
|
+
}), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
|
|
327
636
|
classNames: "p-0 m-0 !is-auto"
|
|
328
|
-
}, /* @__PURE__ */
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
})), /* @__PURE__ */
|
|
637
|
+
}, /* @__PURE__ */ React5.createElement(HuePicker, {
|
|
638
|
+
value: item.color,
|
|
639
|
+
onChange: handleColorChange(item.id)
|
|
640
|
+
})), /* @__PURE__ */ React5.createElement(IconButton, {
|
|
332
641
|
icon: "ph--trash--fill",
|
|
333
642
|
iconOnly: true,
|
|
334
643
|
label: t("select option delete"),
|
|
335
644
|
onClick: () => handleDelete(item.id)
|
|
336
|
-
})))))), /* @__PURE__ */
|
|
645
|
+
})))))), /* @__PURE__ */ React5.createElement("div", {
|
|
337
646
|
role: "listitem"
|
|
338
|
-
}, /* @__PURE__ */
|
|
647
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
339
648
|
role: "button",
|
|
340
649
|
className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
341
650
|
onClick: handleAdd
|
|
342
|
-
}, /* @__PURE__ */
|
|
651
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
343
652
|
className: "flex items-center gap-1"
|
|
344
|
-
}, /* @__PURE__ */
|
|
653
|
+
}, /* @__PURE__ */ React5.createElement(Icon2, {
|
|
345
654
|
icon: "ph--plus--regular"
|
|
346
|
-
}), /* @__PURE__ */
|
|
655
|
+
}), /* @__PURE__ */ React5.createElement("span", {
|
|
347
656
|
className: "text-sm"
|
|
348
657
|
}, t("select option add")))))))));
|
|
349
658
|
};
|
|
350
659
|
|
|
351
660
|
// packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
|
|
352
|
-
import
|
|
661
|
+
import React6 from "react";
|
|
353
662
|
import { Input as Input4 } from "@dxos/react-ui";
|
|
354
663
|
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
355
664
|
import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
|
|
@@ -362,13 +671,13 @@ var gridCols = [
|
|
|
362
671
|
var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
363
672
|
const { status, error } = getStatus();
|
|
364
673
|
const values = getValue3() ?? {};
|
|
365
|
-
return /* @__PURE__ */
|
|
674
|
+
return /* @__PURE__ */ React6.createElement(Input4.Root, {
|
|
366
675
|
validationValence: status
|
|
367
|
-
}, /* @__PURE__ */
|
|
676
|
+
}, /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
368
677
|
error
|
|
369
|
-
}, /* @__PURE__ */
|
|
678
|
+
}, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
|
|
370
679
|
className: mx3("grid gap-2", gridCols[binding.length - 1])
|
|
371
|
-
}, binding.map((prop) => /* @__PURE__ */
|
|
680
|
+
}, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
|
|
372
681
|
key: prop,
|
|
373
682
|
type: "number",
|
|
374
683
|
disabled,
|
|
@@ -382,291 +691,124 @@ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValu
|
|
|
382
691
|
};
|
|
383
692
|
|
|
384
693
|
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
385
|
-
import
|
|
694
|
+
import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
|
|
386
695
|
import { Input as Input5, Select } from "@dxos/react-ui";
|
|
387
696
|
var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
388
697
|
const { status, error } = getStatus();
|
|
389
|
-
return /* @__PURE__ */
|
|
698
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
390
699
|
validationValence: status
|
|
391
|
-
}, !inputOnly && /* @__PURE__ */
|
|
700
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
392
701
|
error
|
|
393
|
-
}, /* @__PURE__ */
|
|
702
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
394
703
|
disabled,
|
|
395
704
|
placeholder,
|
|
396
705
|
value: getValue3() ?? "",
|
|
397
706
|
onChange: (event) => onValueChange(type, event.target.value),
|
|
398
707
|
onBlur
|
|
399
|
-
}), inputOnly && /* @__PURE__ */
|
|
708
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
|
|
400
709
|
};
|
|
401
710
|
var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
402
711
|
const { status, error } = getStatus();
|
|
403
|
-
return /* @__PURE__ */
|
|
712
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
404
713
|
validationValence: status
|
|
405
|
-
}, !inputOnly && /* @__PURE__ */
|
|
714
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
406
715
|
error
|
|
407
|
-
}, /* @__PURE__ */
|
|
716
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
408
717
|
type: "number",
|
|
409
718
|
disabled,
|
|
410
719
|
placeholder,
|
|
411
720
|
value: getValue3() ?? 0,
|
|
412
721
|
onChange: (event) => onValueChange(type, event.target.value),
|
|
413
722
|
onBlur
|
|
414
|
-
}), inputOnly && /* @__PURE__ */
|
|
723
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
415
724
|
};
|
|
416
725
|
var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
|
|
417
726
|
const { status, error } = getStatus();
|
|
418
|
-
|
|
727
|
+
const checked = Boolean(getValue3());
|
|
728
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
419
729
|
validationValence: status
|
|
420
|
-
}, !inputOnly && /* @__PURE__ */
|
|
730
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
421
731
|
error
|
|
422
|
-
}, /* @__PURE__ */
|
|
423
|
-
checked
|
|
732
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
|
|
733
|
+
checked,
|
|
424
734
|
onCheckedChange: (value) => onValueChange(type, value)
|
|
425
|
-
}), inputOnly && /* @__PURE__ */
|
|
735
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
426
736
|
};
|
|
427
737
|
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
428
738
|
const { status, error } = getStatus();
|
|
429
|
-
return /* @__PURE__ */
|
|
739
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
430
740
|
validationValence: status
|
|
431
|
-
}, !inputOnly && /* @__PURE__ */
|
|
741
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
432
742
|
error
|
|
433
|
-
}, /* @__PURE__ */
|
|
743
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
|
|
434
744
|
value: getValue3(),
|
|
435
745
|
onValueChange: (value) => onValueChange(type, value)
|
|
436
|
-
}, /* @__PURE__ */
|
|
746
|
+
}, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
|
|
437
747
|
classNames: "is-full",
|
|
438
748
|
disabled,
|
|
439
749
|
placeholder
|
|
440
|
-
}), /* @__PURE__ */
|
|
750
|
+
}), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
441
751
|
// NOTE: Numeric values are converted to and from strings.
|
|
442
|
-
/* @__PURE__ */
|
|
752
|
+
/* @__PURE__ */ React7.createElement(Select.Option, {
|
|
443
753
|
key: String(value),
|
|
444
754
|
value: String(value)
|
|
445
755
|
}, label2 ?? String(value))
|
|
446
|
-
)))))), inputOnly && /* @__PURE__ */
|
|
756
|
+
)))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
447
757
|
};
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
457
|
-
|
|
458
|
-
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
459
|
-
import React7, { createContext, useContext } from "react";
|
|
460
|
-
import { raise } from "@dxos/debug";
|
|
461
|
-
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
462
|
-
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
463
|
-
|
|
464
|
-
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
465
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
466
|
-
import { getValue, setValue } from "@dxos/echo-schema";
|
|
467
|
-
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
468
|
-
import { invariant } from "@dxos/invariant";
|
|
469
|
-
import { log } from "@dxos/log";
|
|
470
|
-
import { validateSchema } from "@dxos/schema";
|
|
471
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
472
|
-
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
473
|
-
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
474
|
-
F: __dxlog_file,
|
|
475
|
-
L: 91,
|
|
476
|
-
S: void 0,
|
|
477
|
-
A: [
|
|
478
|
-
"AST.isTypeLiteral(schema.ast)",
|
|
479
|
-
""
|
|
480
|
-
]
|
|
481
|
-
});
|
|
482
|
-
const [values, setValues] = useState2(initialValues);
|
|
483
|
-
useEffect2(() => {
|
|
484
|
-
setValues(initialValues);
|
|
485
|
-
}, [
|
|
486
|
-
initialValues
|
|
487
|
-
]);
|
|
488
|
-
const [touched, setTouched] = useState2(createKeySet(initialValues, false));
|
|
489
|
-
const [changed, setChanged] = useState2(createKeySet(initialValues, false));
|
|
490
|
-
const [errors, setErrors] = useState2({});
|
|
491
|
-
const [saving, setSaving] = useState2(false);
|
|
492
|
-
const validate = useCallback2((values2) => {
|
|
493
|
-
let errors2 = validateSchema(schema, values2) ?? [];
|
|
494
|
-
if (errors2.length === 0 && onValidate) {
|
|
495
|
-
const validatedValues = values2;
|
|
496
|
-
errors2 = onValidate(validatedValues) ?? [];
|
|
497
|
-
}
|
|
498
|
-
setErrors(flatMap(errors2));
|
|
499
|
-
const valid = errors2.length === 0;
|
|
500
|
-
return valid;
|
|
501
|
-
}, [
|
|
502
|
-
schema,
|
|
503
|
-
onValidate
|
|
504
|
-
]);
|
|
505
|
-
const [schemaDirty, setSchemaDirty] = useState2(false);
|
|
506
|
-
useEffect2(() => setSchemaDirty(true), [
|
|
507
|
-
schema
|
|
508
|
-
]);
|
|
509
|
-
useEffect2(() => {
|
|
510
|
-
if (schemaDirty) {
|
|
511
|
-
validate(values);
|
|
512
|
-
setSchemaDirty(false);
|
|
513
|
-
}
|
|
514
|
-
}, [
|
|
515
|
-
values,
|
|
516
|
-
schemaDirty
|
|
517
|
-
]);
|
|
518
|
-
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
519
|
-
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
520
|
-
touched,
|
|
521
|
-
errors,
|
|
522
|
-
saving
|
|
523
|
-
]);
|
|
524
|
-
const handleSave = useCallback2(async () => {
|
|
525
|
-
if (validate(values)) {
|
|
526
|
-
setSaving(true);
|
|
527
|
-
try {
|
|
528
|
-
await onSave?.(values, {
|
|
529
|
-
changed
|
|
530
|
-
});
|
|
531
|
-
} finally {
|
|
532
|
-
setSaving(false);
|
|
533
|
-
}
|
|
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`;
|
|
534
766
|
}
|
|
767
|
+
}, []);
|
|
768
|
+
useEffect5(() => {
|
|
769
|
+
adjustHeight();
|
|
535
770
|
}, [
|
|
536
|
-
|
|
537
|
-
validate,
|
|
538
|
-
onSave,
|
|
539
|
-
changed
|
|
540
|
-
]);
|
|
541
|
-
const getStatus = useCallback2((path) => {
|
|
542
|
-
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
543
|
-
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
544
|
-
return {
|
|
545
|
-
status: matchingError ? "error" : void 0,
|
|
546
|
-
error: matchingError ? matchingError[1] : void 0
|
|
547
|
-
};
|
|
548
|
-
}, [
|
|
549
|
-
errors
|
|
771
|
+
adjustHeight
|
|
550
772
|
]);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
};
|
|
554
|
-
const onValueChange = (path, type, value) => {
|
|
555
|
-
const jsonPath = createJsonPath(path);
|
|
556
|
-
let parsedValue = value;
|
|
557
|
-
try {
|
|
558
|
-
if (type === "number") {
|
|
559
|
-
parsedValue = parseFloat(value) || 0;
|
|
560
|
-
}
|
|
561
|
-
} catch (err) {
|
|
562
|
-
log.catch(err, void 0, {
|
|
563
|
-
F: __dxlog_file,
|
|
564
|
-
L: 193,
|
|
565
|
-
S: void 0,
|
|
566
|
-
C: (f, a) => f(...a)
|
|
567
|
-
});
|
|
568
|
-
parsedValue = void 0;
|
|
569
|
-
}
|
|
570
|
-
const newValues = {
|
|
571
|
-
...setValue(values, jsonPath, parsedValue)
|
|
572
|
-
};
|
|
573
|
-
setValues(newValues);
|
|
574
|
-
setChanged((prev) => ({
|
|
575
|
-
...prev,
|
|
576
|
-
[jsonPath]: true
|
|
577
|
-
}));
|
|
578
|
-
onValuesChanged?.(newValues);
|
|
579
|
-
const isValid = validate(newValues);
|
|
580
|
-
if (isValid && onValid) {
|
|
581
|
-
onValid(newValues, {
|
|
582
|
-
changed
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
const onTouched = useCallback2((path) => {
|
|
587
|
-
const jsonPath = createJsonPath(path);
|
|
588
|
-
setTouched((touched2) => ({
|
|
589
|
-
...touched2,
|
|
590
|
-
[jsonPath]: true
|
|
591
|
-
}));
|
|
592
|
-
validate(values);
|
|
773
|
+
useEffect5(() => {
|
|
774
|
+
adjustHeight();
|
|
593
775
|
}, [
|
|
594
|
-
|
|
595
|
-
|
|
776
|
+
getValue3(),
|
|
777
|
+
adjustHeight
|
|
596
778
|
]);
|
|
597
|
-
return {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
F: __dxlog_file,
|
|
615
|
-
L: 235,
|
|
616
|
-
S: void 0,
|
|
617
|
-
A: [
|
|
618
|
-
"obj",
|
|
619
|
-
""
|
|
620
|
-
]
|
|
621
|
-
});
|
|
622
|
-
return Object.keys(obj).reduce((acc, key) => ({
|
|
623
|
-
...acc,
|
|
624
|
-
[key]: value
|
|
625
|
-
}), {});
|
|
626
|
-
};
|
|
627
|
-
var flatMap = (errors) => {
|
|
628
|
-
return errors.reduce((result, { path, message }) => {
|
|
629
|
-
const jsonPath = fromEffectValidationPath(path);
|
|
630
|
-
if (!(jsonPath in result)) {
|
|
631
|
-
result[jsonPath] = message;
|
|
632
|
-
}
|
|
633
|
-
return result;
|
|
634
|
-
}, {});
|
|
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));
|
|
635
796
|
};
|
|
636
797
|
|
|
637
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
641
|
-
};
|
|
642
|
-
var useFormValues = (path = []) => {
|
|
643
|
-
const { values: formValues } = useFormContext();
|
|
644
|
-
const jsonPath = createJsonPath2(path);
|
|
645
|
-
return getValue2(formValues, jsonPath);
|
|
646
|
-
};
|
|
647
|
-
var useInputProps = (path = []) => {
|
|
648
|
-
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
649
|
-
return {
|
|
650
|
-
getStatus: () => getStatus(path),
|
|
651
|
-
getValue: () => getFormValue(path),
|
|
652
|
-
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
653
|
-
onBlur: () => onTouched(path)
|
|
654
|
-
};
|
|
655
|
-
};
|
|
656
|
-
var FormProvider = ({ children, ...formOptions }) => {
|
|
657
|
-
const formHandler = useForm(formOptions);
|
|
658
|
-
return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
|
|
659
|
-
value: formHandler
|
|
660
|
-
}, children);
|
|
661
|
-
};
|
|
798
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
799
|
+
import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
|
|
800
|
+
import { mx as mx7 } from "@dxos/react-ui-theme";
|
|
662
801
|
|
|
663
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";
|
|
664
806
|
var FormActions = ({ onCancel, readonly }) => {
|
|
665
807
|
const { t } = useTranslation3(translationKey);
|
|
666
808
|
const { canSave, handleSave } = useFormContext();
|
|
667
809
|
return /* @__PURE__ */ React8.createElement("div", {
|
|
668
810
|
role: "none",
|
|
669
|
-
className: "flex justify-center"
|
|
811
|
+
className: "mt-2 flex justify-center"
|
|
670
812
|
}, /* @__PURE__ */ React8.createElement("div", {
|
|
671
813
|
role: "none",
|
|
672
814
|
className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
@@ -688,7 +830,7 @@ var FormActions = ({ onCancel, readonly }) => {
|
|
|
688
830
|
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
689
831
|
import { pipe as pipe2 } from "effect";
|
|
690
832
|
import { capitalize as capitalize2 } from "effect/String";
|
|
691
|
-
import React10, { useMemo as
|
|
833
|
+
import React10, { useMemo as useMemo5 } from "react";
|
|
692
834
|
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
693
835
|
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
694
836
|
import { mx as mx6 } from "@dxos/react-ui-theme";
|
|
@@ -698,7 +840,7 @@ import { isNotFalsy } from "@dxos/util";
|
|
|
698
840
|
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
699
841
|
import { pipe } from "effect";
|
|
700
842
|
import { capitalize } from "effect/String";
|
|
701
|
-
import React9, { useCallback as
|
|
843
|
+
import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
|
|
702
844
|
import { AST as AST2 } from "@dxos/echo-schema";
|
|
703
845
|
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
704
846
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
@@ -734,7 +876,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
734
876
|
prop.defaultValue
|
|
735
877
|
]));
|
|
736
878
|
};
|
|
737
|
-
const newValue =
|
|
879
|
+
const newValue = useMemo4(() => {
|
|
738
880
|
if (type === "object" && elementType) {
|
|
739
881
|
return getDefaultObjectValue(elementType);
|
|
740
882
|
} else {
|
|
@@ -744,7 +886,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
744
886
|
type,
|
|
745
887
|
elementType
|
|
746
888
|
]);
|
|
747
|
-
const handleAdd =
|
|
889
|
+
const handleAdd = useCallback5(() => {
|
|
748
890
|
inputProps.onValueChange(type, [
|
|
749
891
|
...values,
|
|
750
892
|
newValue
|
|
@@ -756,7 +898,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
756
898
|
newValue,
|
|
757
899
|
values
|
|
758
900
|
]);
|
|
759
|
-
const handleRemove =
|
|
901
|
+
const handleRemove = useCallback5((index) => {
|
|
760
902
|
const newValues = values.filter((_, i) => i !== index);
|
|
761
903
|
inputProps.onValueChange(type, newValues);
|
|
762
904
|
}, [
|
|
@@ -814,8 +956,10 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
814
956
|
import { FormatEnum } from "@dxos/echo-schema";
|
|
815
957
|
var getInputComponent = (type, format) => {
|
|
816
958
|
switch (format) {
|
|
817
|
-
case FormatEnum.
|
|
959
|
+
case FormatEnum.GeoPoint:
|
|
818
960
|
return GeoPointInput;
|
|
961
|
+
case FormatEnum.Markdown:
|
|
962
|
+
return MarkdownInput;
|
|
819
963
|
}
|
|
820
964
|
switch (type) {
|
|
821
965
|
case "string":
|
|
@@ -831,11 +975,11 @@ var getInputComponent = (type, format) => {
|
|
|
831
975
|
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
832
976
|
const inputProps = useInputProps(path);
|
|
833
977
|
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
834
|
-
const label =
|
|
978
|
+
const label = useMemo5(() => title ?? pipe2(name, capitalize2), [
|
|
835
979
|
title,
|
|
836
980
|
name
|
|
837
981
|
]);
|
|
838
|
-
const placeholder =
|
|
982
|
+
const placeholder = useMemo5(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
839
983
|
examples,
|
|
840
984
|
description
|
|
841
985
|
]);
|
|
@@ -859,20 +1003,15 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
859
1003
|
const jsonPath = createJsonPath3(path ?? []);
|
|
860
1004
|
const CustomComponent = Custom?.[jsonPath];
|
|
861
1005
|
if (CustomComponent) {
|
|
862
|
-
return (
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
placeholder,
|
|
872
|
-
disabled: readonly,
|
|
873
|
-
...inputProps
|
|
874
|
-
}))
|
|
875
|
-
);
|
|
1006
|
+
return /* @__PURE__ */ React10.createElement(CustomComponent, {
|
|
1007
|
+
type,
|
|
1008
|
+
format,
|
|
1009
|
+
label,
|
|
1010
|
+
inputOnly: inline,
|
|
1011
|
+
placeholder,
|
|
1012
|
+
disabled: readonly,
|
|
1013
|
+
...inputProps
|
|
1014
|
+
});
|
|
876
1015
|
}
|
|
877
1016
|
if (array) {
|
|
878
1017
|
return /* @__PURE__ */ React10.createElement(ArrayField, {
|
|
@@ -932,7 +1071,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
932
1071
|
};
|
|
933
1072
|
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
934
1073
|
const values = useFormValues(path);
|
|
935
|
-
const properties =
|
|
1074
|
+
const properties = useMemo5(() => {
|
|
936
1075
|
const props = getSchemaProperties2(schema.ast, values);
|
|
937
1076
|
const filtered = filter ? filter(props) : props;
|
|
938
1077
|
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
@@ -944,7 +1083,7 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
944
1083
|
]);
|
|
945
1084
|
return /* @__PURE__ */ React10.createElement("div", {
|
|
946
1085
|
role: "form",
|
|
947
|
-
className: mx6("flex flex-col w-full gap-2
|
|
1086
|
+
className: mx6("flex flex-col w-full gap-2")
|
|
948
1087
|
}, properties.map((property) => {
|
|
949
1088
|
return /* @__PURE__ */ React10.createElement(FormField, {
|
|
950
1089
|
key: property.name,
|
|
@@ -962,13 +1101,14 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
962
1101
|
|
|
963
1102
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
964
1103
|
var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
965
|
-
const
|
|
1104
|
+
const formRef = useRef3(null);
|
|
1105
|
+
const onValid = useMemo6(() => autoSave ? onSave : void 0, [
|
|
966
1106
|
autoSave,
|
|
967
1107
|
onSave
|
|
968
1108
|
]);
|
|
969
|
-
|
|
970
|
-
if (autoFocus) {
|
|
971
|
-
const input =
|
|
1109
|
+
useEffect6(() => {
|
|
1110
|
+
if (autoFocus && formRef.current) {
|
|
1111
|
+
const input = formRef.current.querySelector("input");
|
|
972
1112
|
if (input) {
|
|
973
1113
|
input.focus();
|
|
974
1114
|
}
|
|
@@ -977,13 +1117,16 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
977
1117
|
autoFocus
|
|
978
1118
|
]);
|
|
979
1119
|
return /* @__PURE__ */ React11.createElement(FormProvider, {
|
|
1120
|
+
formRef,
|
|
980
1121
|
schema,
|
|
1122
|
+
autoSave,
|
|
981
1123
|
initialValues,
|
|
982
1124
|
onValuesChanged,
|
|
983
1125
|
onValidate,
|
|
984
1126
|
onValid,
|
|
985
1127
|
onSave
|
|
986
1128
|
}, /* @__PURE__ */ React11.createElement("div", {
|
|
1129
|
+
ref: formRef,
|
|
987
1130
|
role: "none",
|
|
988
1131
|
className: mx7("p-2", classNames),
|
|
989
1132
|
"data-testid": testId
|
|
@@ -1005,13 +1148,13 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
1005
1148
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
1006
1149
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
1007
1150
|
const { t } = useTranslation5(translationKey);
|
|
1008
|
-
const [props, setProps] =
|
|
1009
|
-
|
|
1151
|
+
const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
|
|
1152
|
+
useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
1010
1153
|
field,
|
|
1011
1154
|
projection
|
|
1012
1155
|
]);
|
|
1013
|
-
const [schemas, setSchemas] =
|
|
1014
|
-
|
|
1156
|
+
const [schemas, setSchemas] = useState4([]);
|
|
1157
|
+
useEffect7(() => {
|
|
1015
1158
|
if (!registry) {
|
|
1016
1159
|
return;
|
|
1017
1160
|
}
|
|
@@ -1029,17 +1172,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1029
1172
|
}, [
|
|
1030
1173
|
registry
|
|
1031
1174
|
]);
|
|
1032
|
-
const [referenceSchema, setReferenceSchema] =
|
|
1033
|
-
|
|
1175
|
+
const [referenceSchema, setReferenceSchema] = useState4();
|
|
1176
|
+
useEffect7(() => {
|
|
1034
1177
|
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
1035
1178
|
}, [
|
|
1036
1179
|
schemas,
|
|
1037
1180
|
props?.referenceSchema
|
|
1038
1181
|
]);
|
|
1039
|
-
const [{ fieldSchema }, setFieldSchema] =
|
|
1182
|
+
const [{ fieldSchema }, setFieldSchema] = useState4({
|
|
1040
1183
|
fieldSchema: getFormatSchema(props?.format)
|
|
1041
1184
|
});
|
|
1042
|
-
const handleValuesChanged =
|
|
1185
|
+
const handleValuesChanged = useCallback6((_props) => {
|
|
1043
1186
|
setFieldSchema((prev) => {
|
|
1044
1187
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
1045
1188
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -1074,7 +1217,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1074
1217
|
props.format,
|
|
1075
1218
|
props.referenceSchema
|
|
1076
1219
|
]);
|
|
1077
|
-
const handleValidate =
|
|
1220
|
+
const handleValidate = useCallback6(({ property }) => {
|
|
1078
1221
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
1079
1222
|
return [
|
|
1080
1223
|
{
|
|
@@ -1087,7 +1230,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1087
1230
|
view.fields,
|
|
1088
1231
|
field
|
|
1089
1232
|
]);
|
|
1090
|
-
const handleSave =
|
|
1233
|
+
const handleSave = useCallback6((props2) => {
|
|
1091
1234
|
projection.setFieldProjection({
|
|
1092
1235
|
field,
|
|
1093
1236
|
props: props2
|
|
@@ -1098,13 +1241,13 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1098
1241
|
field,
|
|
1099
1242
|
onSave
|
|
1100
1243
|
]);
|
|
1101
|
-
const handleCancel =
|
|
1244
|
+
const handleCancel = useCallback6(() => {
|
|
1102
1245
|
requestAnimationFrame(() => onCancel?.());
|
|
1103
1246
|
onSave();
|
|
1104
1247
|
}, [
|
|
1105
1248
|
onSave
|
|
1106
1249
|
]);
|
|
1107
|
-
const Custom =
|
|
1250
|
+
const Custom = useMemo7(() => ({
|
|
1108
1251
|
["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1109
1252
|
...props2,
|
|
1110
1253
|
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
@@ -1130,7 +1273,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1130
1273
|
schemas,
|
|
1131
1274
|
referenceSchema
|
|
1132
1275
|
]);
|
|
1133
|
-
const propIsNotType =
|
|
1276
|
+
const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
1134
1277
|
if (!fieldSchema) {
|
|
1135
1278
|
log2.warn("invalid format", {
|
|
1136
1279
|
props
|
|
@@ -1161,7 +1304,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1161
1304
|
};
|
|
1162
1305
|
|
|
1163
1306
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
1164
|
-
import React13, { useCallback as
|
|
1307
|
+
import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
|
|
1165
1308
|
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
1166
1309
|
import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
|
|
1167
1310
|
import { List as List2 } from "@dxos/react-ui-list";
|
|
@@ -1172,18 +1315,18 @@ var ViewMetaSchema = S2.Struct({
|
|
|
1172
1315
|
name: S2.String.annotations({
|
|
1173
1316
|
[AST4.TitleAnnotationId]: "View"
|
|
1174
1317
|
}),
|
|
1175
|
-
typename: Format.
|
|
1318
|
+
typename: Format.URL.annotations({
|
|
1176
1319
|
[AST4.TitleAnnotationId]: "Typename"
|
|
1177
1320
|
})
|
|
1178
1321
|
}).pipe(S2.mutable);
|
|
1179
1322
|
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
1180
1323
|
const { t } = useTranslation6(translationKey);
|
|
1181
|
-
const projection =
|
|
1324
|
+
const projection = useMemo8(() => new ViewProjection(schema, view), [
|
|
1182
1325
|
schema,
|
|
1183
1326
|
view
|
|
1184
1327
|
]);
|
|
1185
|
-
const [field, setField] =
|
|
1186
|
-
const viewValues =
|
|
1328
|
+
const [field, setField] = useState5();
|
|
1329
|
+
const viewValues = useMemo8(() => {
|
|
1187
1330
|
return {
|
|
1188
1331
|
name: view.name,
|
|
1189
1332
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -1193,7 +1336,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1193
1336
|
}, [
|
|
1194
1337
|
view
|
|
1195
1338
|
]);
|
|
1196
|
-
const handleViewUpdate =
|
|
1339
|
+
const handleViewUpdate = useCallback7(({ name, typename }) => {
|
|
1197
1340
|
requestAnimationFrame(() => {
|
|
1198
1341
|
if (view.name !== name) {
|
|
1199
1342
|
view.name = name;
|
|
@@ -1207,16 +1350,16 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1207
1350
|
schema,
|
|
1208
1351
|
onTypenameChanged
|
|
1209
1352
|
]);
|
|
1210
|
-
const handleSelect =
|
|
1353
|
+
const handleSelect = useCallback7((field2) => {
|
|
1211
1354
|
setField((f) => f === field2 ? void 0 : field2);
|
|
1212
1355
|
}, []);
|
|
1213
|
-
const handleAdd =
|
|
1356
|
+
const handleAdd = useCallback7(() => {
|
|
1214
1357
|
const field2 = projection.createFieldProjection();
|
|
1215
1358
|
setField(field2);
|
|
1216
1359
|
}, [
|
|
1217
1360
|
view
|
|
1218
1361
|
]);
|
|
1219
|
-
const handleMove =
|
|
1362
|
+
const handleMove = useCallback7((fromIndex, toIndex) => {
|
|
1220
1363
|
const fields = [
|
|
1221
1364
|
...view.fields
|
|
1222
1365
|
];
|
|
@@ -1226,12 +1369,11 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1226
1369
|
}, [
|
|
1227
1370
|
view.fields
|
|
1228
1371
|
]);
|
|
1229
|
-
const handleClose =
|
|
1372
|
+
const handleClose = useCallback7(() => setField(void 0), []);
|
|
1230
1373
|
return /* @__PURE__ */ React13.createElement("div", {
|
|
1231
1374
|
role: "none",
|
|
1232
1375
|
className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
|
|
1233
1376
|
}, /* @__PURE__ */ React13.createElement(Form, {
|
|
1234
|
-
autoFocus: true,
|
|
1235
1377
|
autoSave: true,
|
|
1236
1378
|
schema: ViewMetaSchema,
|
|
1237
1379
|
values: viewValues,
|
|
@@ -1282,7 +1424,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1282
1424
|
};
|
|
1283
1425
|
|
|
1284
1426
|
// packages/ui/react-ui-form/src/util/field.ts
|
|
1285
|
-
import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
|
|
1427
|
+
import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
|
|
1286
1428
|
var parseValue = ({ type, format, value }) => {
|
|
1287
1429
|
if (value === void 0 || value === null || value === "") {
|
|
1288
1430
|
return void 0;
|
|
@@ -1336,6 +1478,26 @@ var parseValue = ({ type, format, value }) => {
|
|
|
1336
1478
|
const date = new Date(value);
|
|
1337
1479
|
return isNaN(date.getTime()) ? null : date;
|
|
1338
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
|
+
}
|
|
1339
1501
|
default: {
|
|
1340
1502
|
return value;
|
|
1341
1503
|
}
|
|
@@ -1394,7 +1556,7 @@ var pathNotUniqueError = (path) => ({
|
|
|
1394
1556
|
});
|
|
1395
1557
|
|
|
1396
1558
|
// packages/ui/react-ui-form/src/util/formatting.ts
|
|
1397
|
-
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";
|
|
1398
1560
|
var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
1399
1561
|
const formatScalar = (type2) => {
|
|
1400
1562
|
switch (type2) {
|
|
@@ -1447,6 +1609,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
|
1447
1609
|
return date.toLocaleTimeString(locale);
|
|
1448
1610
|
}
|
|
1449
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
|
+
}
|
|
1450
1622
|
default: {
|
|
1451
1623
|
if (value === null || value === "undefined") {
|
|
1452
1624
|
return "";
|
|
@@ -1495,6 +1667,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1495
1667
|
const date = new Date(value);
|
|
1496
1668
|
return date.toISOString().split("T")[1].split(".")[0];
|
|
1497
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
|
+
}
|
|
1498
1680
|
default: {
|
|
1499
1681
|
return String(value);
|
|
1500
1682
|
}
|
|
@@ -1508,6 +1690,7 @@ export {
|
|
|
1508
1690
|
Form,
|
|
1509
1691
|
GeoPointInput,
|
|
1510
1692
|
InputHeader,
|
|
1693
|
+
MarkdownInput,
|
|
1511
1694
|
NumberInput,
|
|
1512
1695
|
SelectInput,
|
|
1513
1696
|
SelectOptionInput,
|
|
@@ -1521,6 +1704,7 @@ export {
|
|
|
1521
1704
|
parseValue,
|
|
1522
1705
|
pathNotUniqueError,
|
|
1523
1706
|
translations_default as translations,
|
|
1524
|
-
useForm
|
|
1707
|
+
useForm,
|
|
1708
|
+
useFormValues
|
|
1525
1709
|
};
|
|
1526
1710
|
//# sourceMappingURL=index.mjs.map
|