@dxos/react-ui-form 0.7.5-labs.8a82073 → 0.7.5-labs.a8b535d
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 +542 -359
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +585 -398
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +542 -359
- 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 +59 -3
- package/src/components/Form/Form.tsx +8 -5
- package/src/components/Form/FormContent.tsx +9 -12
- 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,286 +691,118 @@ 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());
|
|
419
|
-
return /* @__PURE__ */
|
|
728
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
420
729
|
validationValence: status
|
|
421
|
-
}, !inputOnly && /* @__PURE__ */
|
|
730
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
422
731
|
error
|
|
423
|
-
}, /* @__PURE__ */
|
|
732
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
|
|
424
733
|
checked,
|
|
425
734
|
onCheckedChange: (value) => onValueChange(type, value)
|
|
426
|
-
}), inputOnly && /* @__PURE__ */
|
|
735
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
427
736
|
};
|
|
428
737
|
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
429
738
|
const { status, error } = getStatus();
|
|
430
|
-
return /* @__PURE__ */
|
|
739
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
431
740
|
validationValence: status
|
|
432
|
-
}, !inputOnly && /* @__PURE__ */
|
|
741
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
433
742
|
error
|
|
434
|
-
}, /* @__PURE__ */
|
|
743
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
|
|
435
744
|
value: getValue3(),
|
|
436
745
|
onValueChange: (value) => onValueChange(type, value)
|
|
437
|
-
}, /* @__PURE__ */
|
|
746
|
+
}, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
|
|
438
747
|
classNames: "is-full",
|
|
439
748
|
disabled,
|
|
440
749
|
placeholder
|
|
441
|
-
}), /* @__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 }) => (
|
|
442
751
|
// NOTE: Numeric values are converted to and from strings.
|
|
443
|
-
/* @__PURE__ */
|
|
752
|
+
/* @__PURE__ */ React7.createElement(Select.Option, {
|
|
444
753
|
key: String(value),
|
|
445
754
|
value: String(value)
|
|
446
755
|
}, label2 ?? String(value))
|
|
447
|
-
)))))), inputOnly && /* @__PURE__ */
|
|
756
|
+
)))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
448
757
|
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
458
|
-
|
|
459
|
-
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
460
|
-
import React7, { createContext, useContext } from "react";
|
|
461
|
-
import { raise } from "@dxos/debug";
|
|
462
|
-
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
463
|
-
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
464
|
-
|
|
465
|
-
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
466
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
467
|
-
import { getValue, setValue } from "@dxos/echo-schema";
|
|
468
|
-
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
469
|
-
import { invariant } from "@dxos/invariant";
|
|
470
|
-
import { log } from "@dxos/log";
|
|
471
|
-
import { validateSchema } from "@dxos/schema";
|
|
472
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
473
|
-
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
474
|
-
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
475
|
-
F: __dxlog_file,
|
|
476
|
-
L: 91,
|
|
477
|
-
S: void 0,
|
|
478
|
-
A: [
|
|
479
|
-
"AST.isTypeLiteral(schema.ast)",
|
|
480
|
-
""
|
|
481
|
-
]
|
|
482
|
-
});
|
|
483
|
-
const [values, setValues] = useState2(initialValues);
|
|
484
|
-
useEffect2(() => {
|
|
485
|
-
setValues(initialValues);
|
|
486
|
-
}, [
|
|
487
|
-
initialValues
|
|
488
|
-
]);
|
|
489
|
-
const [touched, setTouched] = useState2(createKeySet(initialValues, false));
|
|
490
|
-
const [changed, setChanged] = useState2(createKeySet(initialValues, false));
|
|
491
|
-
const [errors, setErrors] = useState2({});
|
|
492
|
-
const [saving, setSaving] = useState2(false);
|
|
493
|
-
const validate = useCallback2((values2) => {
|
|
494
|
-
let errors2 = validateSchema(schema, values2) ?? [];
|
|
495
|
-
if (errors2.length === 0 && onValidate) {
|
|
496
|
-
const validatedValues = values2;
|
|
497
|
-
errors2 = onValidate(validatedValues) ?? [];
|
|
498
|
-
}
|
|
499
|
-
setErrors(flatMap(errors2));
|
|
500
|
-
const valid = errors2.length === 0;
|
|
501
|
-
return valid;
|
|
502
|
-
}, [
|
|
503
|
-
schema,
|
|
504
|
-
onValidate
|
|
505
|
-
]);
|
|
506
|
-
const [schemaDirty, setSchemaDirty] = useState2(false);
|
|
507
|
-
useEffect2(() => setSchemaDirty(true), [
|
|
508
|
-
schema
|
|
509
|
-
]);
|
|
510
|
-
useEffect2(() => {
|
|
511
|
-
if (schemaDirty) {
|
|
512
|
-
validate(values);
|
|
513
|
-
setSchemaDirty(false);
|
|
514
|
-
}
|
|
515
|
-
}, [
|
|
516
|
-
values,
|
|
517
|
-
schemaDirty
|
|
518
|
-
]);
|
|
519
|
-
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
520
|
-
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
521
|
-
touched,
|
|
522
|
-
errors,
|
|
523
|
-
saving
|
|
524
|
-
]);
|
|
525
|
-
const handleSave = useCallback2(async () => {
|
|
526
|
-
if (validate(values)) {
|
|
527
|
-
setSaving(true);
|
|
528
|
-
try {
|
|
529
|
-
await onSave?.(values, {
|
|
530
|
-
changed
|
|
531
|
-
});
|
|
532
|
-
} finally {
|
|
533
|
-
setSaving(false);
|
|
534
|
-
}
|
|
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`;
|
|
535
766
|
}
|
|
767
|
+
}, []);
|
|
768
|
+
useEffect5(() => {
|
|
769
|
+
adjustHeight();
|
|
536
770
|
}, [
|
|
537
|
-
|
|
538
|
-
validate,
|
|
539
|
-
onSave,
|
|
540
|
-
changed
|
|
541
|
-
]);
|
|
542
|
-
const getStatus = useCallback2((path) => {
|
|
543
|
-
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
544
|
-
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
545
|
-
return {
|
|
546
|
-
status: matchingError ? "error" : void 0,
|
|
547
|
-
error: matchingError ? matchingError[1] : void 0
|
|
548
|
-
};
|
|
549
|
-
}, [
|
|
550
|
-
errors
|
|
771
|
+
adjustHeight
|
|
551
772
|
]);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
};
|
|
555
|
-
const onValueChange = (path, type, value) => {
|
|
556
|
-
const jsonPath = createJsonPath(path);
|
|
557
|
-
let parsedValue = value;
|
|
558
|
-
try {
|
|
559
|
-
if (type === "number") {
|
|
560
|
-
parsedValue = parseFloat(value) || 0;
|
|
561
|
-
}
|
|
562
|
-
} catch (err) {
|
|
563
|
-
log.catch(err, void 0, {
|
|
564
|
-
F: __dxlog_file,
|
|
565
|
-
L: 193,
|
|
566
|
-
S: void 0,
|
|
567
|
-
C: (f, a) => f(...a)
|
|
568
|
-
});
|
|
569
|
-
parsedValue = void 0;
|
|
570
|
-
}
|
|
571
|
-
const newValues = {
|
|
572
|
-
...setValue(values, jsonPath, parsedValue)
|
|
573
|
-
};
|
|
574
|
-
setValues(newValues);
|
|
575
|
-
setChanged((prev) => ({
|
|
576
|
-
...prev,
|
|
577
|
-
[jsonPath]: true
|
|
578
|
-
}));
|
|
579
|
-
onValuesChanged?.(newValues);
|
|
580
|
-
const isValid = validate(newValues);
|
|
581
|
-
if (isValid && onValid) {
|
|
582
|
-
onValid(newValues, {
|
|
583
|
-
changed
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
const onTouched = useCallback2((path) => {
|
|
588
|
-
const jsonPath = createJsonPath(path);
|
|
589
|
-
setTouched((touched2) => ({
|
|
590
|
-
...touched2,
|
|
591
|
-
[jsonPath]: true
|
|
592
|
-
}));
|
|
593
|
-
validate(values);
|
|
773
|
+
useEffect5(() => {
|
|
774
|
+
adjustHeight();
|
|
594
775
|
}, [
|
|
595
|
-
|
|
596
|
-
|
|
776
|
+
getValue3(),
|
|
777
|
+
adjustHeight
|
|
597
778
|
]);
|
|
598
|
-
return {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
F: __dxlog_file,
|
|
616
|
-
L: 235,
|
|
617
|
-
S: void 0,
|
|
618
|
-
A: [
|
|
619
|
-
"obj",
|
|
620
|
-
""
|
|
621
|
-
]
|
|
622
|
-
});
|
|
623
|
-
return Object.keys(obj).reduce((acc, key) => ({
|
|
624
|
-
...acc,
|
|
625
|
-
[key]: value
|
|
626
|
-
}), {});
|
|
627
|
-
};
|
|
628
|
-
var flatMap = (errors) => {
|
|
629
|
-
return errors.reduce((result, { path, message }) => {
|
|
630
|
-
const jsonPath = fromEffectValidationPath(path);
|
|
631
|
-
if (!(jsonPath in result)) {
|
|
632
|
-
result[jsonPath] = message;
|
|
633
|
-
}
|
|
634
|
-
return result;
|
|
635
|
-
}, {});
|
|
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));
|
|
636
796
|
};
|
|
637
797
|
|
|
638
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
642
|
-
};
|
|
643
|
-
var useFormValues = (path = []) => {
|
|
644
|
-
const { values: formValues } = useFormContext();
|
|
645
|
-
const jsonPath = createJsonPath2(path);
|
|
646
|
-
return getValue2(formValues, jsonPath);
|
|
647
|
-
};
|
|
648
|
-
var useInputProps = (path = []) => {
|
|
649
|
-
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
650
|
-
return {
|
|
651
|
-
getStatus: () => getStatus(path),
|
|
652
|
-
getValue: () => getFormValue(path),
|
|
653
|
-
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
654
|
-
onBlur: () => onTouched(path)
|
|
655
|
-
};
|
|
656
|
-
};
|
|
657
|
-
var FormProvider = ({ children, ...formOptions }) => {
|
|
658
|
-
const formHandler = useForm(formOptions);
|
|
659
|
-
return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
|
|
660
|
-
value: formHandler
|
|
661
|
-
}, children);
|
|
662
|
-
};
|
|
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";
|
|
663
801
|
|
|
664
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";
|
|
665
806
|
var FormActions = ({ onCancel, readonly }) => {
|
|
666
807
|
const { t } = useTranslation3(translationKey);
|
|
667
808
|
const { canSave, handleSave } = useFormContext();
|
|
@@ -689,7 +830,7 @@ var FormActions = ({ onCancel, readonly }) => {
|
|
|
689
830
|
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
690
831
|
import { pipe as pipe2 } from "effect";
|
|
691
832
|
import { capitalize as capitalize2 } from "effect/String";
|
|
692
|
-
import React10, { useMemo as
|
|
833
|
+
import React10, { useMemo as useMemo5 } from "react";
|
|
693
834
|
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
694
835
|
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
695
836
|
import { mx as mx6 } from "@dxos/react-ui-theme";
|
|
@@ -699,7 +840,7 @@ import { isNotFalsy } from "@dxos/util";
|
|
|
699
840
|
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
700
841
|
import { pipe } from "effect";
|
|
701
842
|
import { capitalize } from "effect/String";
|
|
702
|
-
import React9, { useCallback as
|
|
843
|
+
import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
|
|
703
844
|
import { AST as AST2 } from "@dxos/echo-schema";
|
|
704
845
|
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
705
846
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
@@ -735,7 +876,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
735
876
|
prop.defaultValue
|
|
736
877
|
]));
|
|
737
878
|
};
|
|
738
|
-
const newValue =
|
|
879
|
+
const newValue = useMemo4(() => {
|
|
739
880
|
if (type === "object" && elementType) {
|
|
740
881
|
return getDefaultObjectValue(elementType);
|
|
741
882
|
} else {
|
|
@@ -745,7 +886,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
745
886
|
type,
|
|
746
887
|
elementType
|
|
747
888
|
]);
|
|
748
|
-
const handleAdd =
|
|
889
|
+
const handleAdd = useCallback5(() => {
|
|
749
890
|
inputProps.onValueChange(type, [
|
|
750
891
|
...values,
|
|
751
892
|
newValue
|
|
@@ -757,7 +898,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
757
898
|
newValue,
|
|
758
899
|
values
|
|
759
900
|
]);
|
|
760
|
-
const handleRemove =
|
|
901
|
+
const handleRemove = useCallback5((index) => {
|
|
761
902
|
const newValues = values.filter((_, i) => i !== index);
|
|
762
903
|
inputProps.onValueChange(type, newValues);
|
|
763
904
|
}, [
|
|
@@ -815,8 +956,10 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
815
956
|
import { FormatEnum } from "@dxos/echo-schema";
|
|
816
957
|
var getInputComponent = (type, format) => {
|
|
817
958
|
switch (format) {
|
|
818
|
-
case FormatEnum.
|
|
959
|
+
case FormatEnum.GeoPoint:
|
|
819
960
|
return GeoPointInput;
|
|
961
|
+
case FormatEnum.Markdown:
|
|
962
|
+
return MarkdownInput;
|
|
820
963
|
}
|
|
821
964
|
switch (type) {
|
|
822
965
|
case "string":
|
|
@@ -832,11 +975,11 @@ var getInputComponent = (type, format) => {
|
|
|
832
975
|
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
833
976
|
const inputProps = useInputProps(path);
|
|
834
977
|
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
835
|
-
const label =
|
|
978
|
+
const label = useMemo5(() => title ?? pipe2(name, capitalize2), [
|
|
836
979
|
title,
|
|
837
980
|
name
|
|
838
981
|
]);
|
|
839
|
-
const placeholder =
|
|
982
|
+
const placeholder = useMemo5(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
840
983
|
examples,
|
|
841
984
|
description
|
|
842
985
|
]);
|
|
@@ -860,20 +1003,15 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
860
1003
|
const jsonPath = createJsonPath3(path ?? []);
|
|
861
1004
|
const CustomComponent = Custom?.[jsonPath];
|
|
862
1005
|
if (CustomComponent) {
|
|
863
|
-
return (
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
placeholder,
|
|
873
|
-
disabled: readonly,
|
|
874
|
-
...inputProps
|
|
875
|
-
}))
|
|
876
|
-
);
|
|
1006
|
+
return /* @__PURE__ */ React10.createElement(CustomComponent, {
|
|
1007
|
+
type,
|
|
1008
|
+
format,
|
|
1009
|
+
label,
|
|
1010
|
+
inputOnly: inline,
|
|
1011
|
+
placeholder,
|
|
1012
|
+
disabled: readonly,
|
|
1013
|
+
...inputProps
|
|
1014
|
+
});
|
|
877
1015
|
}
|
|
878
1016
|
if (array) {
|
|
879
1017
|
return /* @__PURE__ */ React10.createElement(ArrayField, {
|
|
@@ -933,7 +1071,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
933
1071
|
};
|
|
934
1072
|
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
935
1073
|
const values = useFormValues(path);
|
|
936
|
-
const properties =
|
|
1074
|
+
const properties = useMemo5(() => {
|
|
937
1075
|
const props = getSchemaProperties2(schema.ast, values);
|
|
938
1076
|
const filtered = filter ? filter(props) : props;
|
|
939
1077
|
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
@@ -963,13 +1101,14 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
963
1101
|
|
|
964
1102
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
965
1103
|
var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
966
|
-
const
|
|
1104
|
+
const formRef = useRef3(null);
|
|
1105
|
+
const onValid = useMemo6(() => autoSave ? onSave : void 0, [
|
|
967
1106
|
autoSave,
|
|
968
1107
|
onSave
|
|
969
1108
|
]);
|
|
970
|
-
|
|
971
|
-
if (autoFocus) {
|
|
972
|
-
const input =
|
|
1109
|
+
useEffect6(() => {
|
|
1110
|
+
if (autoFocus && formRef.current) {
|
|
1111
|
+
const input = formRef.current.querySelector("input");
|
|
973
1112
|
if (input) {
|
|
974
1113
|
input.focus();
|
|
975
1114
|
}
|
|
@@ -978,13 +1117,16 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
978
1117
|
autoFocus
|
|
979
1118
|
]);
|
|
980
1119
|
return /* @__PURE__ */ React11.createElement(FormProvider, {
|
|
1120
|
+
formRef,
|
|
981
1121
|
schema,
|
|
1122
|
+
autoSave,
|
|
982
1123
|
initialValues,
|
|
983
1124
|
onValuesChanged,
|
|
984
1125
|
onValidate,
|
|
985
1126
|
onValid,
|
|
986
1127
|
onSave
|
|
987
1128
|
}, /* @__PURE__ */ React11.createElement("div", {
|
|
1129
|
+
ref: formRef,
|
|
988
1130
|
role: "none",
|
|
989
1131
|
className: mx7("p-2", classNames),
|
|
990
1132
|
"data-testid": testId
|
|
@@ -1006,13 +1148,13 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
1006
1148
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
1007
1149
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
1008
1150
|
const { t } = useTranslation5(translationKey);
|
|
1009
|
-
const [props, setProps] =
|
|
1010
|
-
|
|
1151
|
+
const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
|
|
1152
|
+
useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
1011
1153
|
field,
|
|
1012
1154
|
projection
|
|
1013
1155
|
]);
|
|
1014
|
-
const [schemas, setSchemas] =
|
|
1015
|
-
|
|
1156
|
+
const [schemas, setSchemas] = useState4([]);
|
|
1157
|
+
useEffect7(() => {
|
|
1016
1158
|
if (!registry) {
|
|
1017
1159
|
return;
|
|
1018
1160
|
}
|
|
@@ -1030,17 +1172,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1030
1172
|
}, [
|
|
1031
1173
|
registry
|
|
1032
1174
|
]);
|
|
1033
|
-
const [referenceSchema, setReferenceSchema] =
|
|
1034
|
-
|
|
1175
|
+
const [referenceSchema, setReferenceSchema] = useState4();
|
|
1176
|
+
useEffect7(() => {
|
|
1035
1177
|
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
1036
1178
|
}, [
|
|
1037
1179
|
schemas,
|
|
1038
1180
|
props?.referenceSchema
|
|
1039
1181
|
]);
|
|
1040
|
-
const [{ fieldSchema }, setFieldSchema] =
|
|
1182
|
+
const [{ fieldSchema }, setFieldSchema] = useState4({
|
|
1041
1183
|
fieldSchema: getFormatSchema(props?.format)
|
|
1042
1184
|
});
|
|
1043
|
-
const handleValuesChanged =
|
|
1185
|
+
const handleValuesChanged = useCallback6((_props) => {
|
|
1044
1186
|
setFieldSchema((prev) => {
|
|
1045
1187
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
1046
1188
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -1075,7 +1217,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1075
1217
|
props.format,
|
|
1076
1218
|
props.referenceSchema
|
|
1077
1219
|
]);
|
|
1078
|
-
const handleValidate =
|
|
1220
|
+
const handleValidate = useCallback6(({ property }) => {
|
|
1079
1221
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
1080
1222
|
return [
|
|
1081
1223
|
{
|
|
@@ -1088,7 +1230,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1088
1230
|
view.fields,
|
|
1089
1231
|
field
|
|
1090
1232
|
]);
|
|
1091
|
-
const handleSave =
|
|
1233
|
+
const handleSave = useCallback6((props2) => {
|
|
1092
1234
|
projection.setFieldProjection({
|
|
1093
1235
|
field,
|
|
1094
1236
|
props: props2
|
|
@@ -1099,13 +1241,13 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1099
1241
|
field,
|
|
1100
1242
|
onSave
|
|
1101
1243
|
]);
|
|
1102
|
-
const handleCancel =
|
|
1244
|
+
const handleCancel = useCallback6(() => {
|
|
1103
1245
|
requestAnimationFrame(() => onCancel?.());
|
|
1104
1246
|
onSave();
|
|
1105
1247
|
}, [
|
|
1106
1248
|
onSave
|
|
1107
1249
|
]);
|
|
1108
|
-
const Custom =
|
|
1250
|
+
const Custom = useMemo7(() => ({
|
|
1109
1251
|
["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1110
1252
|
...props2,
|
|
1111
1253
|
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
@@ -1131,7 +1273,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1131
1273
|
schemas,
|
|
1132
1274
|
referenceSchema
|
|
1133
1275
|
]);
|
|
1134
|
-
const propIsNotType =
|
|
1276
|
+
const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
1135
1277
|
if (!fieldSchema) {
|
|
1136
1278
|
log2.warn("invalid format", {
|
|
1137
1279
|
props
|
|
@@ -1162,7 +1304,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1162
1304
|
};
|
|
1163
1305
|
|
|
1164
1306
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
1165
|
-
import React13, { useCallback as
|
|
1307
|
+
import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
|
|
1166
1308
|
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
1167
1309
|
import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
|
|
1168
1310
|
import { List as List2 } from "@dxos/react-ui-list";
|
|
@@ -1173,18 +1315,18 @@ var ViewMetaSchema = S2.Struct({
|
|
|
1173
1315
|
name: S2.String.annotations({
|
|
1174
1316
|
[AST4.TitleAnnotationId]: "View"
|
|
1175
1317
|
}),
|
|
1176
|
-
typename: Format.
|
|
1318
|
+
typename: Format.URL.annotations({
|
|
1177
1319
|
[AST4.TitleAnnotationId]: "Typename"
|
|
1178
1320
|
})
|
|
1179
1321
|
}).pipe(S2.mutable);
|
|
1180
1322
|
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
1181
1323
|
const { t } = useTranslation6(translationKey);
|
|
1182
|
-
const projection =
|
|
1324
|
+
const projection = useMemo8(() => new ViewProjection(schema, view), [
|
|
1183
1325
|
schema,
|
|
1184
1326
|
view
|
|
1185
1327
|
]);
|
|
1186
|
-
const [field, setField] =
|
|
1187
|
-
const viewValues =
|
|
1328
|
+
const [field, setField] = useState5();
|
|
1329
|
+
const viewValues = useMemo8(() => {
|
|
1188
1330
|
return {
|
|
1189
1331
|
name: view.name,
|
|
1190
1332
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -1194,7 +1336,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1194
1336
|
}, [
|
|
1195
1337
|
view
|
|
1196
1338
|
]);
|
|
1197
|
-
const handleViewUpdate =
|
|
1339
|
+
const handleViewUpdate = useCallback7(({ name, typename }) => {
|
|
1198
1340
|
requestAnimationFrame(() => {
|
|
1199
1341
|
if (view.name !== name) {
|
|
1200
1342
|
view.name = name;
|
|
@@ -1208,16 +1350,16 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1208
1350
|
schema,
|
|
1209
1351
|
onTypenameChanged
|
|
1210
1352
|
]);
|
|
1211
|
-
const handleSelect =
|
|
1353
|
+
const handleSelect = useCallback7((field2) => {
|
|
1212
1354
|
setField((f) => f === field2 ? void 0 : field2);
|
|
1213
1355
|
}, []);
|
|
1214
|
-
const handleAdd =
|
|
1356
|
+
const handleAdd = useCallback7(() => {
|
|
1215
1357
|
const field2 = projection.createFieldProjection();
|
|
1216
1358
|
setField(field2);
|
|
1217
1359
|
}, [
|
|
1218
1360
|
view
|
|
1219
1361
|
]);
|
|
1220
|
-
const handleMove =
|
|
1362
|
+
const handleMove = useCallback7((fromIndex, toIndex) => {
|
|
1221
1363
|
const fields = [
|
|
1222
1364
|
...view.fields
|
|
1223
1365
|
];
|
|
@@ -1227,12 +1369,11 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1227
1369
|
}, [
|
|
1228
1370
|
view.fields
|
|
1229
1371
|
]);
|
|
1230
|
-
const handleClose =
|
|
1372
|
+
const handleClose = useCallback7(() => setField(void 0), []);
|
|
1231
1373
|
return /* @__PURE__ */ React13.createElement("div", {
|
|
1232
1374
|
role: "none",
|
|
1233
1375
|
className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
|
|
1234
1376
|
}, /* @__PURE__ */ React13.createElement(Form, {
|
|
1235
|
-
autoFocus: true,
|
|
1236
1377
|
autoSave: true,
|
|
1237
1378
|
schema: ViewMetaSchema,
|
|
1238
1379
|
values: viewValues,
|
|
@@ -1283,7 +1424,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1283
1424
|
};
|
|
1284
1425
|
|
|
1285
1426
|
// packages/ui/react-ui-form/src/util/field.ts
|
|
1286
|
-
import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
|
|
1427
|
+
import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
|
|
1287
1428
|
var parseValue = ({ type, format, value }) => {
|
|
1288
1429
|
if (value === void 0 || value === null || value === "") {
|
|
1289
1430
|
return void 0;
|
|
@@ -1337,6 +1478,26 @@ var parseValue = ({ type, format, value }) => {
|
|
|
1337
1478
|
const date = new Date(value);
|
|
1338
1479
|
return isNaN(date.getTime()) ? null : date;
|
|
1339
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
|
+
}
|
|
1340
1501
|
default: {
|
|
1341
1502
|
return value;
|
|
1342
1503
|
}
|
|
@@ -1395,7 +1556,7 @@ var pathNotUniqueError = (path) => ({
|
|
|
1395
1556
|
});
|
|
1396
1557
|
|
|
1397
1558
|
// packages/ui/react-ui-form/src/util/formatting.ts
|
|
1398
|
-
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";
|
|
1399
1560
|
var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
1400
1561
|
const formatScalar = (type2) => {
|
|
1401
1562
|
switch (type2) {
|
|
@@ -1448,6 +1609,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
|
1448
1609
|
return date.toLocaleTimeString(locale);
|
|
1449
1610
|
}
|
|
1450
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
|
+
}
|
|
1451
1622
|
default: {
|
|
1452
1623
|
if (value === null || value === "undefined") {
|
|
1453
1624
|
return "";
|
|
@@ -1496,6 +1667,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1496
1667
|
const date = new Date(value);
|
|
1497
1668
|
return date.toISOString().split("T")[1].split(".")[0];
|
|
1498
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
|
+
}
|
|
1499
1680
|
default: {
|
|
1500
1681
|
return String(value);
|
|
1501
1682
|
}
|
|
@@ -1509,6 +1690,7 @@ export {
|
|
|
1509
1690
|
Form,
|
|
1510
1691
|
GeoPointInput,
|
|
1511
1692
|
InputHeader,
|
|
1693
|
+
MarkdownInput,
|
|
1512
1694
|
NumberInput,
|
|
1513
1695
|
SelectInput,
|
|
1514
1696
|
SelectOptionInput,
|
|
@@ -1522,6 +1704,7 @@ export {
|
|
|
1522
1704
|
parseValue,
|
|
1523
1705
|
pathNotUniqueError,
|
|
1524
1706
|
translations_default as translations,
|
|
1525
|
-
useForm
|
|
1707
|
+
useForm,
|
|
1708
|
+
useFormValues
|
|
1526
1709
|
};
|
|
1527
1710
|
//# sourceMappingURL=index.mjs.map
|