@dxos/react-ui-form 0.7.5-labs.e27f9b9 → 0.7.5-labs.f400bbc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +546 -362
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +589 -401
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +546 -362
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +1 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +1 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +2 -2
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +1 -2
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +2 -3
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +4 -1
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +2 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/Form/index.d.ts +2 -0
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts +1 -2
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.d.ts +1 -2
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -2
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +2 -2
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +1 -0
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/package.json +25 -24
- package/src/components/Form/Defaults.tsx +61 -4
- package/src/components/Form/Form.tsx +9 -5
- package/src/components/Form/FormActions.tsx +1 -1
- package/src/components/Form/FormContent.tsx +10 -13
- package/src/components/Form/FormContext.tsx +58 -10
- package/src/components/Form/custom/GeoPointInput.tsx +42 -12
- package/src/components/Form/custom/SelectOptionsInput.tsx +7 -9
- package/src/components/Form/factory.tsx +4 -2
- package/src/components/Form/index.ts +3 -0
- package/src/components/ViewEditor/ViewEditor.tsx +2 -2
- package/src/hooks/useForm.ts +69 -40
- package/src/translations.ts +1 -1
- package/src/util/field.ts +19 -1
- package/src/util/formatting.ts +29 -1
|
@@ -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,291 +689,124 @@ 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());
|
|
726
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
417
727
|
validationValence: status
|
|
418
|
-
}, !inputOnly && /* @__PURE__ */
|
|
728
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
419
729
|
error
|
|
420
|
-
}, /* @__PURE__ */
|
|
421
|
-
checked
|
|
730
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
|
|
731
|
+
checked,
|
|
422
732
|
onCheckedChange: (value) => onValueChange(type, value)
|
|
423
|
-
}), inputOnly && /* @__PURE__ */
|
|
733
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
424
734
|
};
|
|
425
735
|
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
426
736
|
const { status, error } = getStatus();
|
|
427
|
-
return /* @__PURE__ */
|
|
737
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
428
738
|
validationValence: status
|
|
429
|
-
}, !inputOnly && /* @__PURE__ */
|
|
739
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
430
740
|
error
|
|
431
|
-
}, /* @__PURE__ */
|
|
741
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
|
|
432
742
|
value: getValue3(),
|
|
433
743
|
onValueChange: (value) => onValueChange(type, value)
|
|
434
|
-
}, /* @__PURE__ */
|
|
744
|
+
}, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
|
|
435
745
|
classNames: "is-full",
|
|
436
746
|
disabled,
|
|
437
747
|
placeholder
|
|
438
|
-
}), /* @__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 }) => (
|
|
439
749
|
// NOTE: Numeric values are converted to and from strings.
|
|
440
|
-
/* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ React7.createElement(Select.Option, {
|
|
441
751
|
key: String(value),
|
|
442
752
|
value: String(value)
|
|
443
753
|
}, label2 ?? String(value))
|
|
444
|
-
)))))), inputOnly && /* @__PURE__ */
|
|
754
|
+
)))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
445
755
|
};
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
455
|
-
|
|
456
|
-
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
457
|
-
import React7, { createContext, useContext } from "react";
|
|
458
|
-
import { raise } from "@dxos/debug";
|
|
459
|
-
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
460
|
-
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
461
|
-
|
|
462
|
-
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
463
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
464
|
-
import { getValue, setValue } from "@dxos/echo-schema";
|
|
465
|
-
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
466
|
-
import { invariant } from "@dxos/invariant";
|
|
467
|
-
import { log } from "@dxos/log";
|
|
468
|
-
import { validateSchema } from "@dxos/schema";
|
|
469
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
470
|
-
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
471
|
-
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
472
|
-
F: __dxlog_file,
|
|
473
|
-
L: 91,
|
|
474
|
-
S: void 0,
|
|
475
|
-
A: [
|
|
476
|
-
"AST.isTypeLiteral(schema.ast)",
|
|
477
|
-
""
|
|
478
|
-
]
|
|
479
|
-
});
|
|
480
|
-
const [values, setValues] = useState2(initialValues);
|
|
481
|
-
useEffect2(() => {
|
|
482
|
-
setValues(initialValues);
|
|
483
|
-
}, [
|
|
484
|
-
initialValues
|
|
485
|
-
]);
|
|
486
|
-
const [touched, setTouched] = useState2(createKeySet(initialValues, false));
|
|
487
|
-
const [changed, setChanged] = useState2(createKeySet(initialValues, false));
|
|
488
|
-
const [errors, setErrors] = useState2({});
|
|
489
|
-
const [saving, setSaving] = useState2(false);
|
|
490
|
-
const validate = useCallback2((values2) => {
|
|
491
|
-
let errors2 = validateSchema(schema, values2) ?? [];
|
|
492
|
-
if (errors2.length === 0 && onValidate) {
|
|
493
|
-
const validatedValues = values2;
|
|
494
|
-
errors2 = onValidate(validatedValues) ?? [];
|
|
495
|
-
}
|
|
496
|
-
setErrors(flatMap(errors2));
|
|
497
|
-
const valid = errors2.length === 0;
|
|
498
|
-
return valid;
|
|
499
|
-
}, [
|
|
500
|
-
schema,
|
|
501
|
-
onValidate
|
|
502
|
-
]);
|
|
503
|
-
const [schemaDirty, setSchemaDirty] = useState2(false);
|
|
504
|
-
useEffect2(() => setSchemaDirty(true), [
|
|
505
|
-
schema
|
|
506
|
-
]);
|
|
507
|
-
useEffect2(() => {
|
|
508
|
-
if (schemaDirty) {
|
|
509
|
-
validate(values);
|
|
510
|
-
setSchemaDirty(false);
|
|
511
|
-
}
|
|
512
|
-
}, [
|
|
513
|
-
values,
|
|
514
|
-
schemaDirty
|
|
515
|
-
]);
|
|
516
|
-
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
517
|
-
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
518
|
-
touched,
|
|
519
|
-
errors,
|
|
520
|
-
saving
|
|
521
|
-
]);
|
|
522
|
-
const handleSave = useCallback2(async () => {
|
|
523
|
-
if (validate(values)) {
|
|
524
|
-
setSaving(true);
|
|
525
|
-
try {
|
|
526
|
-
await onSave?.(values, {
|
|
527
|
-
changed
|
|
528
|
-
});
|
|
529
|
-
} finally {
|
|
530
|
-
setSaving(false);
|
|
531
|
-
}
|
|
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`;
|
|
532
764
|
}
|
|
765
|
+
}, []);
|
|
766
|
+
useEffect5(() => {
|
|
767
|
+
adjustHeight();
|
|
533
768
|
}, [
|
|
534
|
-
|
|
535
|
-
validate,
|
|
536
|
-
onSave,
|
|
537
|
-
changed
|
|
538
|
-
]);
|
|
539
|
-
const getStatus = useCallback2((path) => {
|
|
540
|
-
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
541
|
-
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
542
|
-
return {
|
|
543
|
-
status: matchingError ? "error" : void 0,
|
|
544
|
-
error: matchingError ? matchingError[1] : void 0
|
|
545
|
-
};
|
|
546
|
-
}, [
|
|
547
|
-
errors
|
|
769
|
+
adjustHeight
|
|
548
770
|
]);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
};
|
|
552
|
-
const onValueChange = (path, type, value) => {
|
|
553
|
-
const jsonPath = createJsonPath(path);
|
|
554
|
-
let parsedValue = value;
|
|
555
|
-
try {
|
|
556
|
-
if (type === "number") {
|
|
557
|
-
parsedValue = parseFloat(value) || 0;
|
|
558
|
-
}
|
|
559
|
-
} catch (err) {
|
|
560
|
-
log.catch(err, void 0, {
|
|
561
|
-
F: __dxlog_file,
|
|
562
|
-
L: 193,
|
|
563
|
-
S: void 0,
|
|
564
|
-
C: (f, a) => f(...a)
|
|
565
|
-
});
|
|
566
|
-
parsedValue = void 0;
|
|
567
|
-
}
|
|
568
|
-
const newValues = {
|
|
569
|
-
...setValue(values, jsonPath, parsedValue)
|
|
570
|
-
};
|
|
571
|
-
setValues(newValues);
|
|
572
|
-
setChanged((prev) => ({
|
|
573
|
-
...prev,
|
|
574
|
-
[jsonPath]: true
|
|
575
|
-
}));
|
|
576
|
-
onValuesChanged?.(newValues);
|
|
577
|
-
const isValid = validate(newValues);
|
|
578
|
-
if (isValid && onValid) {
|
|
579
|
-
onValid(newValues, {
|
|
580
|
-
changed
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
const onTouched = useCallback2((path) => {
|
|
585
|
-
const jsonPath = createJsonPath(path);
|
|
586
|
-
setTouched((touched2) => ({
|
|
587
|
-
...touched2,
|
|
588
|
-
[jsonPath]: true
|
|
589
|
-
}));
|
|
590
|
-
validate(values);
|
|
771
|
+
useEffect5(() => {
|
|
772
|
+
adjustHeight();
|
|
591
773
|
}, [
|
|
592
|
-
|
|
593
|
-
|
|
774
|
+
getValue3(),
|
|
775
|
+
adjustHeight
|
|
594
776
|
]);
|
|
595
|
-
return {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
F: __dxlog_file,
|
|
613
|
-
L: 235,
|
|
614
|
-
S: void 0,
|
|
615
|
-
A: [
|
|
616
|
-
"obj",
|
|
617
|
-
""
|
|
618
|
-
]
|
|
619
|
-
});
|
|
620
|
-
return Object.keys(obj).reduce((acc, key) => ({
|
|
621
|
-
...acc,
|
|
622
|
-
[key]: value
|
|
623
|
-
}), {});
|
|
624
|
-
};
|
|
625
|
-
var flatMap = (errors) => {
|
|
626
|
-
return errors.reduce((result, { path, message }) => {
|
|
627
|
-
const jsonPath = fromEffectValidationPath(path);
|
|
628
|
-
if (!(jsonPath in result)) {
|
|
629
|
-
result[jsonPath] = message;
|
|
630
|
-
}
|
|
631
|
-
return result;
|
|
632
|
-
}, {});
|
|
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));
|
|
633
794
|
};
|
|
634
795
|
|
|
635
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
639
|
-
};
|
|
640
|
-
var useFormValues = (path = []) => {
|
|
641
|
-
const { values: formValues } = useFormContext();
|
|
642
|
-
const jsonPath = createJsonPath2(path);
|
|
643
|
-
return getValue2(formValues, jsonPath);
|
|
644
|
-
};
|
|
645
|
-
var useInputProps = (path = []) => {
|
|
646
|
-
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
647
|
-
return {
|
|
648
|
-
getStatus: () => getStatus(path),
|
|
649
|
-
getValue: () => getFormValue(path),
|
|
650
|
-
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
651
|
-
onBlur: () => onTouched(path)
|
|
652
|
-
};
|
|
653
|
-
};
|
|
654
|
-
var FormProvider = ({ children, ...formOptions }) => {
|
|
655
|
-
const formHandler = useForm(formOptions);
|
|
656
|
-
return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
|
|
657
|
-
value: formHandler
|
|
658
|
-
}, children);
|
|
659
|
-
};
|
|
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";
|
|
660
799
|
|
|
661
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";
|
|
662
804
|
var FormActions = ({ onCancel, readonly }) => {
|
|
663
805
|
const { t } = useTranslation3(translationKey);
|
|
664
806
|
const { canSave, handleSave } = useFormContext();
|
|
665
807
|
return /* @__PURE__ */ React8.createElement("div", {
|
|
666
808
|
role: "none",
|
|
667
|
-
className: "flex justify-center"
|
|
809
|
+
className: "mt-2 flex justify-center"
|
|
668
810
|
}, /* @__PURE__ */ React8.createElement("div", {
|
|
669
811
|
role: "none",
|
|
670
812
|
className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
@@ -686,7 +828,7 @@ var FormActions = ({ onCancel, readonly }) => {
|
|
|
686
828
|
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
687
829
|
import { pipe as pipe2 } from "effect";
|
|
688
830
|
import { capitalize as capitalize2 } from "effect/String";
|
|
689
|
-
import React10, { useMemo as
|
|
831
|
+
import React10, { useMemo as useMemo5 } from "react";
|
|
690
832
|
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
691
833
|
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
692
834
|
import { mx as mx6 } from "@dxos/react-ui-theme";
|
|
@@ -696,7 +838,7 @@ import { isNotFalsy } from "@dxos/util";
|
|
|
696
838
|
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
697
839
|
import { pipe } from "effect";
|
|
698
840
|
import { capitalize } from "effect/String";
|
|
699
|
-
import React9, { useCallback as
|
|
841
|
+
import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
|
|
700
842
|
import { AST as AST2 } from "@dxos/echo-schema";
|
|
701
843
|
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
702
844
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
@@ -732,7 +874,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
732
874
|
prop.defaultValue
|
|
733
875
|
]));
|
|
734
876
|
};
|
|
735
|
-
const newValue =
|
|
877
|
+
const newValue = useMemo4(() => {
|
|
736
878
|
if (type === "object" && elementType) {
|
|
737
879
|
return getDefaultObjectValue(elementType);
|
|
738
880
|
} else {
|
|
@@ -742,7 +884,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
742
884
|
type,
|
|
743
885
|
elementType
|
|
744
886
|
]);
|
|
745
|
-
const handleAdd =
|
|
887
|
+
const handleAdd = useCallback5(() => {
|
|
746
888
|
inputProps.onValueChange(type, [
|
|
747
889
|
...values,
|
|
748
890
|
newValue
|
|
@@ -754,7 +896,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
754
896
|
newValue,
|
|
755
897
|
values
|
|
756
898
|
]);
|
|
757
|
-
const handleRemove =
|
|
899
|
+
const handleRemove = useCallback5((index) => {
|
|
758
900
|
const newValues = values.filter((_, i) => i !== index);
|
|
759
901
|
inputProps.onValueChange(type, newValues);
|
|
760
902
|
}, [
|
|
@@ -812,8 +954,10 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
|
|
|
812
954
|
import { FormatEnum } from "@dxos/echo-schema";
|
|
813
955
|
var getInputComponent = (type, format) => {
|
|
814
956
|
switch (format) {
|
|
815
|
-
case FormatEnum.
|
|
957
|
+
case FormatEnum.GeoPoint:
|
|
816
958
|
return GeoPointInput;
|
|
959
|
+
case FormatEnum.Markdown:
|
|
960
|
+
return MarkdownInput;
|
|
817
961
|
}
|
|
818
962
|
switch (type) {
|
|
819
963
|
case "string":
|
|
@@ -829,11 +973,11 @@ var getInputComponent = (type, format) => {
|
|
|
829
973
|
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
830
974
|
const inputProps = useInputProps(path);
|
|
831
975
|
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
832
|
-
const label =
|
|
976
|
+
const label = useMemo5(() => title ?? pipe2(name, capitalize2), [
|
|
833
977
|
title,
|
|
834
978
|
name
|
|
835
979
|
]);
|
|
836
|
-
const placeholder =
|
|
980
|
+
const placeholder = useMemo5(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
837
981
|
examples,
|
|
838
982
|
description
|
|
839
983
|
]);
|
|
@@ -857,20 +1001,15 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
857
1001
|
const jsonPath = createJsonPath3(path ?? []);
|
|
858
1002
|
const CustomComponent = Custom?.[jsonPath];
|
|
859
1003
|
if (CustomComponent) {
|
|
860
|
-
return (
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
placeholder,
|
|
870
|
-
disabled: readonly,
|
|
871
|
-
...inputProps
|
|
872
|
-
}))
|
|
873
|
-
);
|
|
1004
|
+
return /* @__PURE__ */ React10.createElement(CustomComponent, {
|
|
1005
|
+
type,
|
|
1006
|
+
format,
|
|
1007
|
+
label,
|
|
1008
|
+
inputOnly: inline,
|
|
1009
|
+
placeholder,
|
|
1010
|
+
disabled: readonly,
|
|
1011
|
+
...inputProps
|
|
1012
|
+
});
|
|
874
1013
|
}
|
|
875
1014
|
if (array) {
|
|
876
1015
|
return /* @__PURE__ */ React10.createElement(ArrayField, {
|
|
@@ -930,7 +1069,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
|
|
|
930
1069
|
};
|
|
931
1070
|
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
932
1071
|
const values = useFormValues(path);
|
|
933
|
-
const properties =
|
|
1072
|
+
const properties = useMemo5(() => {
|
|
934
1073
|
const props = getSchemaProperties2(schema.ast, values);
|
|
935
1074
|
const filtered = filter ? filter(props) : props;
|
|
936
1075
|
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
@@ -942,7 +1081,7 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
942
1081
|
]);
|
|
943
1082
|
return /* @__PURE__ */ React10.createElement("div", {
|
|
944
1083
|
role: "form",
|
|
945
|
-
className: mx6("flex flex-col w-full gap-2
|
|
1084
|
+
className: mx6("flex flex-col w-full gap-2")
|
|
946
1085
|
}, properties.map((property) => {
|
|
947
1086
|
return /* @__PURE__ */ React10.createElement(FormField, {
|
|
948
1087
|
key: property.name,
|
|
@@ -960,13 +1099,14 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
960
1099
|
|
|
961
1100
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
962
1101
|
var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
963
|
-
const
|
|
1102
|
+
const formRef = useRef3(null);
|
|
1103
|
+
const onValid = useMemo6(() => autoSave ? onSave : void 0, [
|
|
964
1104
|
autoSave,
|
|
965
1105
|
onSave
|
|
966
1106
|
]);
|
|
967
|
-
|
|
968
|
-
if (autoFocus) {
|
|
969
|
-
const input =
|
|
1107
|
+
useEffect6(() => {
|
|
1108
|
+
if (autoFocus && formRef.current) {
|
|
1109
|
+
const input = formRef.current.querySelector("input");
|
|
970
1110
|
if (input) {
|
|
971
1111
|
input.focus();
|
|
972
1112
|
}
|
|
@@ -975,13 +1115,16 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
975
1115
|
autoFocus
|
|
976
1116
|
]);
|
|
977
1117
|
return /* @__PURE__ */ React11.createElement(FormProvider, {
|
|
1118
|
+
formRef,
|
|
978
1119
|
schema,
|
|
1120
|
+
autoSave,
|
|
979
1121
|
initialValues,
|
|
980
1122
|
onValuesChanged,
|
|
981
1123
|
onValidate,
|
|
982
1124
|
onValid,
|
|
983
1125
|
onSave
|
|
984
1126
|
}, /* @__PURE__ */ React11.createElement("div", {
|
|
1127
|
+
ref: formRef,
|
|
985
1128
|
role: "none",
|
|
986
1129
|
className: mx7("p-2", classNames),
|
|
987
1130
|
"data-testid": testId
|
|
@@ -1003,13 +1146,13 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
|
|
|
1003
1146
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
1004
1147
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
1005
1148
|
const { t } = useTranslation5(translationKey);
|
|
1006
|
-
const [props, setProps] =
|
|
1007
|
-
|
|
1149
|
+
const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
|
|
1150
|
+
useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
1008
1151
|
field,
|
|
1009
1152
|
projection
|
|
1010
1153
|
]);
|
|
1011
|
-
const [schemas, setSchemas] =
|
|
1012
|
-
|
|
1154
|
+
const [schemas, setSchemas] = useState4([]);
|
|
1155
|
+
useEffect7(() => {
|
|
1013
1156
|
if (!registry) {
|
|
1014
1157
|
return;
|
|
1015
1158
|
}
|
|
@@ -1027,17 +1170,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1027
1170
|
}, [
|
|
1028
1171
|
registry
|
|
1029
1172
|
]);
|
|
1030
|
-
const [referenceSchema, setReferenceSchema] =
|
|
1031
|
-
|
|
1173
|
+
const [referenceSchema, setReferenceSchema] = useState4();
|
|
1174
|
+
useEffect7(() => {
|
|
1032
1175
|
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
1033
1176
|
}, [
|
|
1034
1177
|
schemas,
|
|
1035
1178
|
props?.referenceSchema
|
|
1036
1179
|
]);
|
|
1037
|
-
const [{ fieldSchema }, setFieldSchema] =
|
|
1180
|
+
const [{ fieldSchema }, setFieldSchema] = useState4({
|
|
1038
1181
|
fieldSchema: getFormatSchema(props?.format)
|
|
1039
1182
|
});
|
|
1040
|
-
const handleValuesChanged =
|
|
1183
|
+
const handleValuesChanged = useCallback6((_props) => {
|
|
1041
1184
|
setFieldSchema((prev) => {
|
|
1042
1185
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
1043
1186
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -1072,7 +1215,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1072
1215
|
props.format,
|
|
1073
1216
|
props.referenceSchema
|
|
1074
1217
|
]);
|
|
1075
|
-
const handleValidate =
|
|
1218
|
+
const handleValidate = useCallback6(({ property }) => {
|
|
1076
1219
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
1077
1220
|
return [
|
|
1078
1221
|
{
|
|
@@ -1085,7 +1228,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1085
1228
|
view.fields,
|
|
1086
1229
|
field
|
|
1087
1230
|
]);
|
|
1088
|
-
const handleSave =
|
|
1231
|
+
const handleSave = useCallback6((props2) => {
|
|
1089
1232
|
projection.setFieldProjection({
|
|
1090
1233
|
field,
|
|
1091
1234
|
props: props2
|
|
@@ -1096,13 +1239,13 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1096
1239
|
field,
|
|
1097
1240
|
onSave
|
|
1098
1241
|
]);
|
|
1099
|
-
const handleCancel =
|
|
1242
|
+
const handleCancel = useCallback6(() => {
|
|
1100
1243
|
requestAnimationFrame(() => onCancel?.());
|
|
1101
1244
|
onSave();
|
|
1102
1245
|
}, [
|
|
1103
1246
|
onSave
|
|
1104
1247
|
]);
|
|
1105
|
-
const Custom =
|
|
1248
|
+
const Custom = useMemo7(() => ({
|
|
1106
1249
|
["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
|
|
1107
1250
|
...props2,
|
|
1108
1251
|
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
@@ -1128,7 +1271,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1128
1271
|
schemas,
|
|
1129
1272
|
referenceSchema
|
|
1130
1273
|
]);
|
|
1131
|
-
const propIsNotType =
|
|
1274
|
+
const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
1132
1275
|
if (!fieldSchema) {
|
|
1133
1276
|
log2.warn("invalid format", {
|
|
1134
1277
|
props
|
|
@@ -1159,7 +1302,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
1159
1302
|
};
|
|
1160
1303
|
|
|
1161
1304
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
1162
|
-
import React13, { useCallback as
|
|
1305
|
+
import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
|
|
1163
1306
|
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
1164
1307
|
import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
|
|
1165
1308
|
import { List as List2 } from "@dxos/react-ui-list";
|
|
@@ -1170,18 +1313,18 @@ var ViewMetaSchema = S2.Struct({
|
|
|
1170
1313
|
name: S2.String.annotations({
|
|
1171
1314
|
[AST4.TitleAnnotationId]: "View"
|
|
1172
1315
|
}),
|
|
1173
|
-
typename: Format.
|
|
1316
|
+
typename: Format.URL.annotations({
|
|
1174
1317
|
[AST4.TitleAnnotationId]: "Typename"
|
|
1175
1318
|
})
|
|
1176
1319
|
}).pipe(S2.mutable);
|
|
1177
1320
|
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
1178
1321
|
const { t } = useTranslation6(translationKey);
|
|
1179
|
-
const projection =
|
|
1322
|
+
const projection = useMemo8(() => new ViewProjection(schema, view), [
|
|
1180
1323
|
schema,
|
|
1181
1324
|
view
|
|
1182
1325
|
]);
|
|
1183
|
-
const [field, setField] =
|
|
1184
|
-
const viewValues =
|
|
1326
|
+
const [field, setField] = useState5();
|
|
1327
|
+
const viewValues = useMemo8(() => {
|
|
1185
1328
|
return {
|
|
1186
1329
|
name: view.name,
|
|
1187
1330
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -1191,7 +1334,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1191
1334
|
}, [
|
|
1192
1335
|
view
|
|
1193
1336
|
]);
|
|
1194
|
-
const handleViewUpdate =
|
|
1337
|
+
const handleViewUpdate = useCallback7(({ name, typename }) => {
|
|
1195
1338
|
requestAnimationFrame(() => {
|
|
1196
1339
|
if (view.name !== name) {
|
|
1197
1340
|
view.name = name;
|
|
@@ -1205,16 +1348,16 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1205
1348
|
schema,
|
|
1206
1349
|
onTypenameChanged
|
|
1207
1350
|
]);
|
|
1208
|
-
const handleSelect =
|
|
1351
|
+
const handleSelect = useCallback7((field2) => {
|
|
1209
1352
|
setField((f) => f === field2 ? void 0 : field2);
|
|
1210
1353
|
}, []);
|
|
1211
|
-
const handleAdd =
|
|
1354
|
+
const handleAdd = useCallback7(() => {
|
|
1212
1355
|
const field2 = projection.createFieldProjection();
|
|
1213
1356
|
setField(field2);
|
|
1214
1357
|
}, [
|
|
1215
1358
|
view
|
|
1216
1359
|
]);
|
|
1217
|
-
const handleMove =
|
|
1360
|
+
const handleMove = useCallback7((fromIndex, toIndex) => {
|
|
1218
1361
|
const fields = [
|
|
1219
1362
|
...view.fields
|
|
1220
1363
|
];
|
|
@@ -1224,12 +1367,11 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1224
1367
|
}, [
|
|
1225
1368
|
view.fields
|
|
1226
1369
|
]);
|
|
1227
|
-
const handleClose =
|
|
1370
|
+
const handleClose = useCallback7(() => setField(void 0), []);
|
|
1228
1371
|
return /* @__PURE__ */ React13.createElement("div", {
|
|
1229
1372
|
role: "none",
|
|
1230
1373
|
className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
|
|
1231
1374
|
}, /* @__PURE__ */ React13.createElement(Form, {
|
|
1232
|
-
autoFocus: true,
|
|
1233
1375
|
autoSave: true,
|
|
1234
1376
|
schema: ViewMetaSchema,
|
|
1235
1377
|
values: viewValues,
|
|
@@ -1280,7 +1422,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
1280
1422
|
};
|
|
1281
1423
|
|
|
1282
1424
|
// packages/ui/react-ui-form/src/util/field.ts
|
|
1283
|
-
import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
|
|
1425
|
+
import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
|
|
1284
1426
|
var parseValue = ({ type, format, value }) => {
|
|
1285
1427
|
if (value === void 0 || value === null || value === "") {
|
|
1286
1428
|
return void 0;
|
|
@@ -1334,6 +1476,26 @@ var parseValue = ({ type, format, value }) => {
|
|
|
1334
1476
|
const date = new Date(value);
|
|
1335
1477
|
return isNaN(date.getTime()) ? null : date;
|
|
1336
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
|
+
}
|
|
1337
1499
|
default: {
|
|
1338
1500
|
return value;
|
|
1339
1501
|
}
|
|
@@ -1392,7 +1554,7 @@ var pathNotUniqueError = (path) => ({
|
|
|
1392
1554
|
});
|
|
1393
1555
|
|
|
1394
1556
|
// packages/ui/react-ui-form/src/util/formatting.ts
|
|
1395
|
-
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";
|
|
1396
1558
|
var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
1397
1559
|
const formatScalar = (type2) => {
|
|
1398
1560
|
switch (type2) {
|
|
@@ -1445,6 +1607,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
|
|
|
1445
1607
|
return date.toLocaleTimeString(locale);
|
|
1446
1608
|
}
|
|
1447
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
|
+
}
|
|
1448
1620
|
default: {
|
|
1449
1621
|
if (value === null || value === "undefined") {
|
|
1450
1622
|
return "";
|
|
@@ -1493,6 +1665,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
|
|
|
1493
1665
|
const date = new Date(value);
|
|
1494
1666
|
return date.toISOString().split("T")[1].split(".")[0];
|
|
1495
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
|
+
}
|
|
1496
1678
|
default: {
|
|
1497
1679
|
return String(value);
|
|
1498
1680
|
}
|
|
@@ -1506,6 +1688,7 @@ export {
|
|
|
1506
1688
|
Form,
|
|
1507
1689
|
GeoPointInput,
|
|
1508
1690
|
InputHeader,
|
|
1691
|
+
MarkdownInput,
|
|
1509
1692
|
NumberInput,
|
|
1510
1693
|
SelectInput,
|
|
1511
1694
|
SelectOptionInput,
|
|
@@ -1519,6 +1702,7 @@ export {
|
|
|
1519
1702
|
parseValue,
|
|
1520
1703
|
pathNotUniqueError,
|
|
1521
1704
|
translations_default as translations,
|
|
1522
|
-
useForm
|
|
1705
|
+
useForm,
|
|
1706
|
+
useFormValues
|
|
1523
1707
|
};
|
|
1524
1708
|
//# sourceMappingURL=index.mjs.map
|