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