@dxos/react-ui-form 0.8.0 → 0.8.1-main.013e445

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.
Files changed (58) hide show
  1. package/dist/lib/browser/index.mjs +571 -507
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +449 -384
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +571 -507
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts +2 -1
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Defaults.d.ts +6 -5
  15. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +2 -2
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.stories.d.ts +7 -7
  19. package/dist/types/src/components/Form/FormActions.d.ts +2 -1
  20. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormContent.d.ts +3 -2
  22. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/FormContext.d.ts +1 -1
  24. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/Input.d.ts +2 -2
  26. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  27. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +2 -1
  28. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +2 -1
  30. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  31. package/dist/types/src/components/Form/custom/TupleInput.d.ts +2 -1
  32. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  33. package/dist/types/src/components/Form/index.d.ts +1 -2
  34. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  35. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +2 -1
  36. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -1
  38. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  39. package/dist/types/src/components/deprecated/Form.d.ts +3 -2
  40. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  41. package/dist/types/src/components/deprecated/Form.stories.d.ts +2 -1
  42. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/deprecated/FormInput.d.ts +2 -2
  44. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  45. package/dist/types/src/components/testing.d.ts +3 -3
  46. package/dist/types/src/components/testing.d.ts.map +1 -1
  47. package/dist/types/src/hooks/useForm.d.ts +2 -1
  48. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  49. package/dist/types/src/translations.d.ts +1 -0
  50. package/dist/types/src/translations.d.ts.map +1 -1
  51. package/dist/types/src/util/field.d.ts.map +1 -1
  52. package/package.json +26 -28
  53. package/src/components/Form/index.ts +1 -3
  54. package/src/components/ViewEditor/ViewEditor.tsx +71 -2
  55. package/src/components/deprecated/Form.tsx +2 -2
  56. package/src/hooks/useForm.ts +12 -2
  57. package/src/translations.ts +1 -0
  58. package/src/util/field.ts +1 -0
@@ -7,6 +7,7 @@ var translations_default = [
7
7
  "en-US": {
8
8
  [translationKey]: {
9
9
  "fields label": "Fields",
10
+ "hidden fields label": "Hidden Fields",
10
11
  // TODO(burdon): Standardize field/property.
11
12
  "button add property": "Add property",
12
13
  "field property label": "Property",
@@ -107,325 +108,54 @@ import { log as log2 } from "@dxos/log";
107
108
  import { useTranslation as useTranslation5 } from "@dxos/react-ui";
108
109
  import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
109
110
 
110
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
111
- import React2, { createContext, useContext, useEffect as useEffect2, useMemo as useMemo2 } from "react";
112
- import { raise } from "@dxos/debug";
113
- import { getValue as getValue2 } from "@dxos/echo-schema";
114
- import { createJsonPath as createJsonPath2 } from "@dxos/effect";
115
-
116
- // packages/ui/react-ui-form/src/hooks/useForm.ts
117
- import { useCallback, useEffect, useMemo, useState } from "react";
118
- import { getValue, setValue } from "@dxos/echo-schema";
119
- import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
120
- import { invariant } from "@dxos/invariant";
121
- import { log } from "@dxos/log";
122
- import { validateSchema } from "@dxos/schema";
123
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
124
- var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
125
- invariant(AST.isTypeLiteral(schema.ast), void 0, {
126
- F: __dxlog_file,
127
- L: 92,
128
- S: void 0,
129
- A: [
130
- "AST.isTypeLiteral(schema.ast)",
131
- ""
132
- ]
133
- });
134
- const [values, setValues] = useState(initialValues);
135
- useEffect(() => {
136
- setValues(initialValues);
137
- }, [
138
- initialValues
139
- ]);
140
- const [touched, setTouched] = useState(createKeySet(initialValues, false));
141
- const [changed, setChanged] = useState(createKeySet(initialValues, false));
142
- const [errors, setErrors] = useState({});
143
- const [saving, setSaving] = useState(false);
144
- const validate = useCallback((values2) => {
145
- let errors2 = validateSchema(schema, values2) ?? [];
146
- if (errors2.length === 0 && onValidate) {
147
- const validatedValues = values2;
148
- errors2 = onValidate(validatedValues) ?? [];
149
- }
150
- setErrors(flatMap(errors2));
151
- const valid = errors2.length === 0;
152
- return valid;
153
- }, [
154
- schema,
155
- onValidate
156
- ]);
157
- const [schemaDirty, setSchemaDirty] = useState(false);
158
- useEffect(() => setSchemaDirty(true), [
159
- schema
160
- ]);
161
- useEffect(() => {
162
- if (schemaDirty) {
163
- validate(values);
164
- setSchemaDirty(false);
165
- }
166
- }, [
167
- values,
168
- schemaDirty
169
- ]);
170
- const formIsValid = useMemo(() => {
171
- return Object.keys(errors).length === 0;
172
- }, [
173
- errors
174
- ]);
175
- const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors.
176
- !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
177
- touched,
178
- errors,
179
- saving
180
- ]);
181
- const handleSave = useCallback(async () => {
182
- if (validate(values)) {
183
- setSaving(true);
184
- try {
185
- await onSave?.(values, {
186
- changed
187
- });
188
- } finally {
189
- setSaving(false);
190
- }
191
- }
192
- }, [
193
- values,
194
- validate,
195
- onSave,
196
- changed
197
- ]);
198
- const getStatus = useCallback((path) => {
199
- const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
200
- const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
201
- return {
202
- status: matchingError ? "error" : void 0,
203
- error: matchingError ? matchingError[1] : void 0
204
- };
205
- }, [
206
- errors
207
- ]);
208
- const getFormValue = useCallback((path) => {
209
- return getValue(values, createJsonPath(path));
210
- }, [
211
- values
212
- ]);
213
- const onValueChange = useCallback((path, type, value) => {
214
- const jsonPath = createJsonPath(path);
215
- let parsedValue = value;
216
- try {
217
- if (type === "number") {
218
- parsedValue = parseFloat(value) || 0;
219
- }
220
- } catch (err) {
221
- log.catch(err, void 0, {
222
- F: __dxlog_file,
223
- L: 202,
224
- S: void 0,
225
- C: (f, a) => f(...a)
226
- });
227
- parsedValue = void 0;
228
- }
229
- const newValues = {
230
- ...setValue(values, jsonPath, parsedValue)
231
- };
232
- setValues(newValues);
233
- setChanged((prev) => ({
234
- ...prev,
235
- [jsonPath]: true
236
- }));
237
- onValuesChanged?.(newValues);
238
- const isValid = validate(newValues);
239
- if (isValid && onValid) {
240
- onValid(newValues, {
241
- changed
242
- });
243
- }
244
- }, [
245
- values,
246
- onValuesChanged,
247
- validate,
248
- onValid,
249
- changed
250
- ]);
251
- const onTouched = useCallback((path) => {
252
- const jsonPath = createJsonPath(path);
253
- setTouched((touched2) => ({
254
- ...touched2,
255
- [jsonPath]: true
256
- }));
257
- validate(values);
258
- }, [
259
- validate,
260
- values
261
- ]);
262
- return useMemo(() => ({
263
- // State.
264
- values,
265
- errors,
266
- touched,
267
- changed,
268
- canSave,
269
- formIsValid,
270
- // Actions.
271
- handleSave,
272
- // Field utils.
273
- getStatus,
274
- getValue: getFormValue,
275
- onValueChange,
276
- onTouched
277
- }), [
278
- values,
279
- errors,
280
- touched,
281
- changed,
282
- canSave,
283
- formIsValid,
284
- handleSave,
285
- getStatus,
286
- getFormValue,
287
- onValueChange,
288
- onTouched
289
- ]);
290
- };
291
- var createKeySet = (obj, value) => {
292
- invariant(obj, void 0, {
293
- F: __dxlog_file,
294
- L: 264,
295
- S: void 0,
296
- A: [
297
- "obj",
298
- ""
299
- ]
300
- });
301
- return Object.keys(obj).reduce((acc, key) => ({
302
- ...acc,
303
- [key]: value
304
- }), {});
305
- };
306
- var flatMap = (errors) => {
307
- return errors.reduce((result, { path, message }) => {
308
- const jsonPath = fromEffectValidationPath(path);
309
- if (!(jsonPath in result)) {
310
- result[jsonPath] = message;
311
- }
312
- return result;
313
- }, {});
314
- };
315
-
316
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
317
- var FormContext = /* @__PURE__ */ createContext(void 0);
318
- var useFormContext = () => {
319
- return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
320
- };
321
- var useFormValues = (path = []) => {
322
- const { values: formValues } = useFormContext();
323
- const jsonPath = createJsonPath2(path);
324
- return getValue2(formValues, jsonPath);
325
- };
326
- var useInputProps = (path = []) => {
327
- const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
328
- const stablePath = useMemo2(() => path, [
329
- Array.isArray(path) ? path.join(".") : path
330
- ]);
331
- return useMemo2(() => ({
332
- getStatus: () => getStatus(stablePath),
333
- getValue: () => getFormValue(stablePath),
334
- onValueChange: (type, value) => onValueChange(stablePath, type, value),
335
- onBlur: () => onTouched(stablePath)
336
- }), [
337
- getStatus,
338
- getFormValue,
339
- onValueChange,
340
- onTouched,
341
- stablePath
342
- ]);
343
- };
344
- var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
345
- const form = useForm(formOptions);
346
- useEffect2(() => {
347
- if (!formRef?.current) {
348
- return;
349
- }
350
- const handleKeyDown = (event) => {
351
- const keyIsEnter = event.key === "Enter";
352
- const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
353
- const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
354
- const inputOptOut = event.target.hasAttribute("data-no-submit");
355
- const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
356
- const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
357
- if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
358
- if (!autoSave && form.canSave) {
359
- form.handleSave();
360
- }
361
- if (autoSave && form.formIsValid) {
362
- event.target.blur();
363
- }
364
- }
365
- };
366
- const formElement = formRef.current;
367
- formElement.addEventListener("keydown", handleKeyDown);
368
- return () => formElement.removeEventListener("keydown", handleKeyDown);
369
- }, [
370
- formRef,
371
- form.canSave,
372
- form.formIsValid,
373
- form.handleSave,
374
- autoSave
375
- ]);
376
- return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
377
- value: form
378
- }, children);
379
- };
380
-
381
111
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
382
- import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo3 } from "react";
112
+ import React3, { useCallback, useState, useEffect, useMemo } from "react";
383
113
  import { GeoLocation } from "@dxos/echo-schema";
384
114
  import { Input as Input2, useTranslation } from "@dxos/react-ui";
385
115
  import { safeParseFloat } from "@dxos/util";
386
116
 
387
117
  // packages/ui/react-ui-form/src/components/Form/Input.tsx
388
- import React3 from "react";
118
+ import React2 from "react";
389
119
  import { Icon, Tooltip } from "@dxos/react-ui";
390
120
  import { errorText } from "@dxos/react-ui-theme";
391
121
  var InputHeader = ({ children, error }) => {
392
- return /* @__PURE__ */ React3.createElement("div", {
122
+ return /* @__PURE__ */ React2.createElement("div", {
393
123
  role: "none",
394
124
  className: "flex justify-between items-center mb-1"
395
- }, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
125
+ }, children, error && /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
396
126
  asChild: true
397
- }, /* @__PURE__ */ React3.createElement(Icon, {
127
+ }, /* @__PURE__ */ React2.createElement(Icon, {
398
128
  icon: "ph--warning--regular",
399
129
  size: 4,
400
130
  classNames: errorText
401
- })), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
131
+ })), /* @__PURE__ */ React2.createElement(Tooltip.Content, {
402
132
  side: "bottom"
403
- }, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
133
+ }, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
404
134
  };
405
135
 
406
136
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
407
137
  var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
408
138
  const { t } = useTranslation(translationKey);
409
139
  const { status, error } = getStatus();
410
- const geoPoint = useMemo3(() => getValue3() ?? [
140
+ const geoPoint = useMemo(() => getValue3() ?? [
411
141
  0,
412
142
  0
413
143
  ], [
414
144
  getValue3
415
145
  ]);
416
- const geoLocation = useMemo3(() => GeoLocation.fromGeoPoint(geoPoint), [
146
+ const geoLocation = useMemo(() => GeoLocation.fromGeoPoint(geoPoint), [
417
147
  geoPoint
418
148
  ]);
419
- const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
420
- const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
421
- useEffect3(() => {
149
+ const [longitudeText, setLongitudeText] = useState(geoLocation.longitude?.toString());
150
+ const [latitudeText, setLatitudeText] = useState(geoLocation.latitude?.toString());
151
+ useEffect(() => {
422
152
  const location = GeoLocation.fromGeoPoint(geoPoint);
423
153
  setLongitudeText(location.longitude.toString());
424
154
  setLatitudeText(location.latitude.toString());
425
155
  }, [
426
156
  geoPoint
427
157
  ]);
428
- const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
158
+ const handleCoordinateChange = useCallback((coordinateType, setText) => (event) => {
429
159
  const inputText = event.target.value;
430
160
  setText(inputText);
431
161
  if (inputText !== "" && inputText !== "-") {
@@ -448,14 +178,14 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
448
178
  onValueChange,
449
179
  type
450
180
  ]);
451
- return /* @__PURE__ */ React4.createElement(Input2.Root, {
181
+ return /* @__PURE__ */ React3.createElement(Input2.Root, {
452
182
  validationValence: status
453
- }, /* @__PURE__ */ React4.createElement(InputHeader, {
183
+ }, /* @__PURE__ */ React3.createElement(InputHeader, {
454
184
  error
455
- }, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
185
+ }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
456
186
  role: "none",
457
187
  className: "grid grid-cols-2 gap-2"
458
- }, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
188
+ }, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
459
189
  type: "text",
460
190
  pattern: "^-?[0-9]*\\.?[0-9]*$",
461
191
  disabled,
@@ -463,7 +193,7 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
463
193
  value: latitudeText,
464
194
  onChange: handleCoordinateChange("latitude", setLatitudeText),
465
195
  onBlur
466
- }), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
196
+ }), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
467
197
  type: "text",
468
198
  pattern: "^-?[0-9]*\\.?[0-9]*$",
469
199
  disabled,
@@ -475,20 +205,20 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
475
205
  };
476
206
 
477
207
  // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
478
- import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
208
+ import React4, { useCallback as useCallback2, useEffect as useEffect2, useState as useState2, useRef } from "react";
479
209
  import { PublicKey } from "@dxos/keys";
480
210
  import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
481
211
  import { List } from "@dxos/react-ui-list";
482
212
  import { HuePicker } from "@dxos/react-ui-pickers";
483
213
  import { hues, mx as mx2 } from "@dxos/react-ui-theme";
484
214
  var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
485
- const [selected, setSelectedId] = useState3(null);
486
- const [isNewOption, setIsNewOption] = useState3(false);
215
+ const [selected, setSelectedId] = useState2(null);
216
+ const [isNewOption, setIsNewOption] = useState2(false);
487
217
  const inputRef = useRef(null);
488
218
  const { t } = useTranslation2(translationKey);
489
219
  const { status, error } = getStatus();
490
220
  const options = getValue3();
491
- useEffect4(() => {
221
+ useEffect2(() => {
492
222
  if (options === void 0) {
493
223
  onValueChange(type, []);
494
224
  }
@@ -507,7 +237,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
507
237
  const availableHues = hues.filter((hue) => !lastThree.has(hue));
508
238
  return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
509
239
  };
510
- const handleAdd = useCallback3(() => {
240
+ const handleAdd = useCallback2(() => {
511
241
  const newId = PublicKey.random().truncate();
512
242
  const newOption = {
513
243
  id: newId,
@@ -525,7 +255,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
525
255
  type,
526
256
  onValueChange
527
257
  ]);
528
- const handleDelete = useCallback3((id) => {
258
+ const handleDelete = useCallback2((id) => {
529
259
  const newOptions = options?.filter((option) => option.id !== id) ?? [];
530
260
  onValueChange(type, newOptions);
531
261
  setSelectedId(null);
@@ -534,7 +264,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
534
264
  type,
535
265
  onValueChange
536
266
  ]);
537
- const handleMove = useCallback3((from, to) => {
267
+ const handleMove = useCallback2((from, to) => {
538
268
  if (!options) {
539
269
  return;
540
270
  }
@@ -549,10 +279,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
549
279
  type,
550
280
  onValueChange
551
281
  ]);
552
- const handleClick = useCallback3((id) => {
282
+ const handleClick = useCallback2((id) => {
553
283
  setSelectedId((current) => current === id ? null : id);
554
284
  }, []);
555
- const handleTitleChange = useCallback3((id) => (e) => {
285
+ const handleTitleChange = useCallback2((id) => (e) => {
556
286
  const newOptions = options?.map((o) => o.id === id ? {
557
287
  ...o,
558
288
  title: e.target.value
@@ -563,12 +293,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
563
293
  type,
564
294
  onValueChange
565
295
  ]);
566
- const handleKeyDown = useCallback3((e) => {
296
+ const handleKeyDown = useCallback2((e) => {
567
297
  if (e.key === "Enter") {
568
298
  setSelectedId(null);
569
299
  }
570
300
  }, []);
571
- const handleColorChange = useCallback3((id) => (hue) => {
301
+ const handleColorChange = useCallback2((id) => (hue) => {
572
302
  const newOptions = options?.map((o) => o.id === id ? {
573
303
  ...o,
574
304
  color: hue
@@ -579,7 +309,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
579
309
  type,
580
310
  onValueChange
581
311
  ]);
582
- useEffect4(() => {
312
+ useEffect2(() => {
583
313
  if (selected && isNewOption && inputRef.current) {
584
314
  inputRef.current.focus();
585
315
  setIsNewOption(false);
@@ -588,221 +318,503 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
588
318
  selected,
589
319
  isNewOption
590
320
  ]);
591
- return /* @__PURE__ */ React5.createElement(Input3.Root, {
592
- validationValence: status
593
- }, /* @__PURE__ */ React5.createElement(InputHeader, {
594
- error
595
- }, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
596
- role: "none"
597
- }, options && /* @__PURE__ */ React5.createElement(List.Root, {
598
- items: options,
599
- isItem: (item) => true,
600
- onMove: handleMove
601
- }, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
602
- role: "list",
603
- className: "w-full overflow-auto"
604
- }, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
605
- key: item.id
606
- }, /* @__PURE__ */ React5.createElement(List.Item, {
607
- role: "button",
608
- item,
609
- classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
610
- "aria-expanded": selected === item.id
611
- }, /* @__PURE__ */ React5.createElement("div", {
612
- className: "flex items-center"
613
- }, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
614
- onClick: () => handleClick(item.id),
615
- classNames: "flex-1"
616
- }, /* @__PURE__ */ React5.createElement(Tag, {
617
- palette: item.color
618
- }, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
619
- icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
620
- onClick: () => handleClick(item.id)
621
- })), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
622
- className: "mis-4 mbs-2 flex flex-col gap-1"
623
- }, /* @__PURE__ */ React5.createElement(Input3.Label, {
624
- classNames: "text-xs"
625
- }, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
626
- className: "flex flex-row items-center gap-1"
627
- }, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
628
- placeholder: t("select option label placeholder"),
629
- ref: selected === item.id ? inputRef : void 0,
630
- value: item.title,
631
- onChange: handleTitleChange(item.id),
632
- onKeyDown: handleKeyDown,
633
- classNames: "flex-1",
634
- "data-no-submit": true
635
- }), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
636
- classNames: "p-0 m-0 !is-auto"
637
- }, /* @__PURE__ */ React5.createElement(HuePicker, {
638
- value: item.color,
639
- onChange: handleColorChange(item.id)
640
- })), /* @__PURE__ */ React5.createElement(IconButton, {
641
- icon: "ph--trash--fill",
642
- iconOnly: true,
643
- label: t("select option delete"),
644
- onClick: () => handleDelete(item.id)
645
- })))))), /* @__PURE__ */ React5.createElement("div", {
646
- role: "listitem"
647
- }, /* @__PURE__ */ React5.createElement("div", {
648
- role: "button",
649
- className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
650
- onClick: handleAdd
651
- }, /* @__PURE__ */ React5.createElement("div", {
652
- className: "flex items-center gap-1"
653
- }, /* @__PURE__ */ React5.createElement(Icon2, {
654
- icon: "ph--plus--regular"
655
- }), /* @__PURE__ */ React5.createElement("span", {
656
- className: "text-sm"
657
- }, t("select option add")))))))));
658
- };
659
-
660
- // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
661
- import React6 from "react";
662
- import { Input as Input4 } from "@dxos/react-ui";
663
- import { mx as mx3 } from "@dxos/react-ui-theme";
664
- import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
665
- var gridCols = [
666
- "grid-cols-1",
667
- "grid-cols-2",
668
- "grid-cols-3",
669
- "grid-cols-4"
670
- ];
671
- var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
672
- const { status, error } = getStatus();
673
- const values = getValue3() ?? {};
674
- return /* @__PURE__ */ React6.createElement(Input4.Root, {
675
- validationValence: status
676
- }, /* @__PURE__ */ React6.createElement(InputHeader, {
677
- error
678
- }, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
679
- className: mx3("grid gap-2", gridCols[binding.length - 1])
680
- }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
681
- key: prop,
682
- type: "number",
683
- disabled,
684
- value: values[prop],
685
- onChange: (event) => onValueChange(type, {
686
- ...values,
687
- [prop]: safeParseFloat2(event.target.value, 0)
688
- }),
689
- onBlur
690
- }))));
321
+ return /* @__PURE__ */ React4.createElement(Input3.Root, {
322
+ validationValence: status
323
+ }, /* @__PURE__ */ React4.createElement(InputHeader, {
324
+ error
325
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
326
+ role: "none"
327
+ }, options && /* @__PURE__ */ React4.createElement(List.Root, {
328
+ items: options,
329
+ isItem: (item) => true,
330
+ onMove: handleMove
331
+ }, ({ items }) => /* @__PURE__ */ React4.createElement("div", {
332
+ role: "list",
333
+ className: "w-full overflow-auto"
334
+ }, items?.map((item) => /* @__PURE__ */ React4.createElement("div", {
335
+ key: item.id
336
+ }, /* @__PURE__ */ React4.createElement(List.Item, {
337
+ role: "button",
338
+ item,
339
+ classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
340
+ "aria-expanded": selected === item.id
341
+ }, /* @__PURE__ */ React4.createElement("div", {
342
+ className: "flex items-center"
343
+ }, /* @__PURE__ */ React4.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React4.createElement(List.ItemTitle, {
344
+ onClick: () => handleClick(item.id),
345
+ classNames: "flex-1"
346
+ }, /* @__PURE__ */ React4.createElement(Tag, {
347
+ palette: item.color
348
+ }, item.title || "\u200B")), /* @__PURE__ */ React4.createElement(Icon2, {
349
+ icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
350
+ onClick: () => handleClick(item.id)
351
+ })), selected === item.id && /* @__PURE__ */ React4.createElement("div", {
352
+ className: "mis-4 mbs-2 flex flex-col gap-1"
353
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, {
354
+ classNames: "text-xs"
355
+ }, t("select option label")), /* @__PURE__ */ React4.createElement("div", {
356
+ className: "flex flex-row items-center gap-1"
357
+ }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
358
+ placeholder: t("select option label placeholder"),
359
+ ref: selected === item.id ? inputRef : void 0,
360
+ value: item.title,
361
+ onChange: handleTitleChange(item.id),
362
+ onKeyDown: handleKeyDown,
363
+ classNames: "flex-1",
364
+ "data-no-submit": true
365
+ }), /* @__PURE__ */ React4.createElement(Toolbar.Root, {
366
+ classNames: "p-0 m-0 !is-auto"
367
+ }, /* @__PURE__ */ React4.createElement(HuePicker, {
368
+ value: item.color,
369
+ onChange: handleColorChange(item.id)
370
+ })), /* @__PURE__ */ React4.createElement(IconButton, {
371
+ icon: "ph--trash--fill",
372
+ iconOnly: true,
373
+ label: t("select option delete"),
374
+ onClick: () => handleDelete(item.id)
375
+ })))))), /* @__PURE__ */ React4.createElement("div", {
376
+ role: "listitem"
377
+ }, /* @__PURE__ */ React4.createElement("div", {
378
+ role: "button",
379
+ className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
380
+ onClick: handleAdd
381
+ }, /* @__PURE__ */ React4.createElement("div", {
382
+ className: "flex items-center gap-1"
383
+ }, /* @__PURE__ */ React4.createElement(Icon2, {
384
+ icon: "ph--plus--regular"
385
+ }), /* @__PURE__ */ React4.createElement("span", {
386
+ className: "text-sm"
387
+ }, t("select option add")))))))));
388
+ };
389
+
390
+ // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
391
+ import React5 from "react";
392
+ import { Input as Input4 } from "@dxos/react-ui";
393
+ import { mx as mx3 } from "@dxos/react-ui-theme";
394
+ import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
395
+ var gridCols = [
396
+ "grid-cols-1",
397
+ "grid-cols-2",
398
+ "grid-cols-3",
399
+ "grid-cols-4"
400
+ ];
401
+ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
402
+ const { status, error } = getStatus();
403
+ const values = getValue3() ?? {};
404
+ return /* @__PURE__ */ React5.createElement(Input4.Root, {
405
+ validationValence: status
406
+ }, /* @__PURE__ */ React5.createElement(InputHeader, {
407
+ error
408
+ }, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
409
+ className: mx3("grid gap-2", gridCols[binding.length - 1])
410
+ }, binding.map((prop) => /* @__PURE__ */ React5.createElement(Input4.TextInput, {
411
+ key: prop,
412
+ type: "number",
413
+ disabled,
414
+ value: values[prop],
415
+ onChange: (event) => onValueChange(type, {
416
+ ...values,
417
+ [prop]: safeParseFloat2(event.target.value, 0)
418
+ }),
419
+ onBlur
420
+ }))));
421
+ };
422
+
423
+ // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
424
+ import React6, { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2 } from "react";
425
+ import { Input as Input5, Select } from "@dxos/react-ui";
426
+ var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
427
+ const { status, error } = getStatus();
428
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
429
+ validationValence: status
430
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
431
+ error
432
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
433
+ disabled,
434
+ placeholder,
435
+ value: getValue3() ?? "",
436
+ onChange: (event) => onValueChange(type, event.target.value),
437
+ onBlur
438
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.Validation, null, error));
439
+ };
440
+ var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
441
+ const { status, error } = getStatus();
442
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
443
+ validationValence: status
444
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
445
+ error
446
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
447
+ type: "number",
448
+ disabled,
449
+ placeholder,
450
+ value: getValue3() ?? 0,
451
+ onChange: (event) => onValueChange(type, event.target.value),
452
+ onBlur
453
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
454
+ };
455
+ var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
456
+ const { status, error } = getStatus();
457
+ const checked = Boolean(getValue3());
458
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
459
+ validationValence: status
460
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
461
+ error
462
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.Switch, {
463
+ checked,
464
+ onCheckedChange: (value) => onValueChange(type, value)
465
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
466
+ };
467
+ var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
468
+ const { status, error } = getStatus();
469
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
470
+ validationValence: status
471
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
472
+ error
473
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Select.Root, {
474
+ value: getValue3(),
475
+ onValueChange: (value) => onValueChange(type, value)
476
+ }, /* @__PURE__ */ React6.createElement(Select.TriggerButton, {
477
+ classNames: "is-full",
478
+ disabled,
479
+ placeholder
480
+ }), /* @__PURE__ */ React6.createElement(Select.Portal, null, /* @__PURE__ */ React6.createElement(Select.Content, null, /* @__PURE__ */ React6.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
481
+ // NOTE: Numeric values are converted to and from strings.
482
+ /* @__PURE__ */ React6.createElement(Select.Option, {
483
+ key: String(value),
484
+ value: String(value)
485
+ }, label2 ?? String(value))
486
+ )))))), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
487
+ };
488
+ var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
489
+ const { status, error } = getStatus();
490
+ const textareaRef = useRef2(null);
491
+ const adjustHeight = useCallback3(() => {
492
+ const textarea = textareaRef.current;
493
+ if (textarea) {
494
+ textarea.style.blockSize = "auto";
495
+ textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
496
+ }
497
+ }, []);
498
+ useEffect3(() => {
499
+ adjustHeight();
500
+ }, [
501
+ adjustHeight
502
+ ]);
503
+ useEffect3(() => {
504
+ adjustHeight();
505
+ }, [
506
+ getValue3(),
507
+ adjustHeight
508
+ ]);
509
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
510
+ validationValence: status
511
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
512
+ error
513
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextArea, {
514
+ ref: textareaRef,
515
+ disabled,
516
+ placeholder,
517
+ value: getValue3() ?? "",
518
+ classNames: "min-bs-auto max-h-40 overflow-auto",
519
+ onChange: (event) => onValueChange(type, event.target.value),
520
+ onBlur,
521
+ style: {
522
+ resize: "none"
523
+ },
524
+ spellCheck: false
525
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.Validation, null, error));
526
+ };
527
+
528
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
529
+ import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
530
+ import { mx as mx7 } from "@dxos/react-ui-theme";
531
+
532
+ // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
533
+ import React8 from "react";
534
+ import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
535
+ import { mx as mx4 } from "@dxos/react-ui-theme";
536
+
537
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
538
+ import React7, { createContext, useContext, useEffect as useEffect5, useMemo as useMemo3 } from "react";
539
+ import { raise } from "@dxos/debug";
540
+ import { getValue as getValue2 } from "@dxos/echo-schema";
541
+ import { createJsonPath as createJsonPath2 } from "@dxos/effect";
542
+
543
+ // packages/ui/react-ui-form/src/hooks/useForm.ts
544
+ import { SchemaAST as AST } from "effect";
545
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo2, useState as useState3 } from "react";
546
+ import { getValue, setValue } from "@dxos/echo-schema";
547
+ import { createJsonPath, fromEffectValidationPath } from "@dxos/effect";
548
+ import { invariant } from "@dxos/invariant";
549
+ import { log } from "@dxos/log";
550
+ import { validateSchema } from "@dxos/schema";
551
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
552
+ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
553
+ invariant(AST.isTypeLiteral(schema.ast), void 0, {
554
+ F: __dxlog_file,
555
+ L: 93,
556
+ S: void 0,
557
+ A: [
558
+ "AST.isTypeLiteral(schema.ast)",
559
+ ""
560
+ ]
561
+ });
562
+ const [values, setValues] = useState3(initialValues);
563
+ useEffect4(() => {
564
+ setValues(initialValues);
565
+ }, [
566
+ initialValues
567
+ ]);
568
+ const [touched, setTouched] = useState3(createKeySet(initialValues, false));
569
+ const [changed, setChanged] = useState3(createKeySet(initialValues, false));
570
+ const [errors, setErrors] = useState3({});
571
+ const [saving, setSaving] = useState3(false);
572
+ const validate = useCallback4((values2) => {
573
+ let errors2 = validateSchema(schema, values2) ?? [];
574
+ if (errors2.length === 0 && onValidate) {
575
+ const validatedValues = values2;
576
+ errors2 = onValidate(validatedValues) ?? [];
577
+ }
578
+ setErrors(flatMap(errors2));
579
+ const valid = errors2.length === 0;
580
+ return valid;
581
+ }, [
582
+ schema,
583
+ onValidate
584
+ ]);
585
+ const [schemaDirty, setSchemaDirty] = useState3(false);
586
+ useEffect4(() => setSchemaDirty(true), [
587
+ schema
588
+ ]);
589
+ useEffect4(() => {
590
+ if (schemaDirty) {
591
+ validate(values);
592
+ setSchemaDirty(false);
593
+ }
594
+ }, [
595
+ values,
596
+ schemaDirty
597
+ ]);
598
+ const formIsValid = useMemo2(() => {
599
+ return Object.keys(errors).length === 0;
600
+ }, [
601
+ errors
602
+ ]);
603
+ const canSave = useMemo2(() => !saving && // Check if any error paths that are touched have errors.
604
+ !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
605
+ touched,
606
+ errors,
607
+ saving
608
+ ]);
609
+ const handleSave = useCallback4(async () => {
610
+ if (validate(values)) {
611
+ setSaving(true);
612
+ try {
613
+ await onSave?.(values, {
614
+ changed
615
+ });
616
+ } finally {
617
+ setSaving(false);
618
+ }
619
+ }
620
+ }, [
621
+ values,
622
+ validate,
623
+ onSave,
624
+ changed
625
+ ]);
626
+ const getStatus = useCallback4((path) => {
627
+ const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
628
+ const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
629
+ const isTouched = touched[jsonPath];
630
+ if (!isTouched) {
631
+ return {
632
+ status: void 0,
633
+ error: void 0
634
+ };
635
+ }
636
+ return {
637
+ status: matchingError ? "error" : void 0,
638
+ error: matchingError ? matchingError[1] : void 0
639
+ };
640
+ }, [
641
+ errors,
642
+ touched
643
+ ]);
644
+ const getFormValue = useCallback4((path) => {
645
+ return getValue(values, createJsonPath(path));
646
+ }, [
647
+ values
648
+ ]);
649
+ const onValueChange = useCallback4((path, type, value) => {
650
+ const jsonPath = createJsonPath(path);
651
+ let parsedValue = value;
652
+ try {
653
+ if (type === "number") {
654
+ parsedValue = parseFloat(value) || 0;
655
+ }
656
+ } catch (err) {
657
+ log.catch(err, void 0, {
658
+ F: __dxlog_file,
659
+ L: 212,
660
+ S: void 0,
661
+ C: (f, a) => f(...a)
662
+ });
663
+ parsedValue = void 0;
664
+ }
665
+ const newValues = {
666
+ ...setValue(values, jsonPath, parsedValue)
667
+ };
668
+ setValues(newValues);
669
+ setChanged((prev) => ({
670
+ ...prev,
671
+ [jsonPath]: true
672
+ }));
673
+ onValuesChanged?.(newValues);
674
+ const isValid = validate(newValues);
675
+ if (isValid && onValid) {
676
+ onValid(newValues, {
677
+ changed
678
+ });
679
+ }
680
+ }, [
681
+ values,
682
+ onValuesChanged,
683
+ validate,
684
+ onValid,
685
+ changed
686
+ ]);
687
+ const onTouched = useCallback4((path) => {
688
+ const jsonPath = createJsonPath(path);
689
+ setTouched((touched2) => ({
690
+ ...touched2,
691
+ [jsonPath]: true
692
+ }));
693
+ validate(values);
694
+ }, [
695
+ validate,
696
+ values
697
+ ]);
698
+ return useMemo2(() => ({
699
+ // State.
700
+ values,
701
+ errors,
702
+ touched,
703
+ changed,
704
+ canSave,
705
+ formIsValid,
706
+ // Actions.
707
+ handleSave,
708
+ // Field utils.
709
+ getStatus,
710
+ getValue: getFormValue,
711
+ onValueChange,
712
+ onTouched
713
+ }), [
714
+ values,
715
+ errors,
716
+ touched,
717
+ changed,
718
+ canSave,
719
+ formIsValid,
720
+ handleSave,
721
+ getStatus,
722
+ getFormValue,
723
+ onValueChange,
724
+ onTouched
725
+ ]);
691
726
  };
692
-
693
- // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
694
- import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
695
- import { Input as Input5, Select } from "@dxos/react-ui";
696
- var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
697
- const { status, error } = getStatus();
698
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
699
- validationValence: status
700
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
701
- error
702
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
703
- disabled,
704
- placeholder,
705
- value: getValue3() ?? "",
706
- onChange: (event) => onValueChange(type, event.target.value),
707
- onBlur
708
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
727
+ var createKeySet = (obj, value) => {
728
+ invariant(obj, void 0, {
729
+ F: __dxlog_file,
730
+ L: 274,
731
+ S: void 0,
732
+ A: [
733
+ "obj",
734
+ ""
735
+ ]
736
+ });
737
+ return Object.keys(obj).reduce((acc, key) => ({
738
+ ...acc,
739
+ [key]: value
740
+ }), {});
709
741
  };
710
- var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
711
- const { status, error } = getStatus();
712
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
713
- validationValence: status
714
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
715
- error
716
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
717
- type: "number",
718
- disabled,
719
- placeholder,
720
- value: getValue3() ?? 0,
721
- onChange: (event) => onValueChange(type, event.target.value),
722
- onBlur
723
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
742
+ var flatMap = (errors) => {
743
+ return errors.reduce((result, { path, message }) => {
744
+ const jsonPath = fromEffectValidationPath(path);
745
+ if (!(jsonPath in result)) {
746
+ result[jsonPath] = message;
747
+ }
748
+ return result;
749
+ }, {});
724
750
  };
725
- var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
726
- const { status, error } = getStatus();
727
- const checked = Boolean(getValue3());
728
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
729
- validationValence: status
730
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
731
- error
732
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
733
- checked,
734
- onCheckedChange: (value) => onValueChange(type, value)
735
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
751
+
752
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
753
+ var FormContext = /* @__PURE__ */ createContext(void 0);
754
+ var useFormContext = () => {
755
+ return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
736
756
  };
737
- var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
738
- const { status, error } = getStatus();
739
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
740
- validationValence: status
741
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
742
- error
743
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
744
- value: getValue3(),
745
- onValueChange: (value) => onValueChange(type, value)
746
- }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
747
- classNames: "is-full",
748
- disabled,
749
- placeholder
750
- }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
751
- // NOTE: Numeric values are converted to and from strings.
752
- /* @__PURE__ */ React7.createElement(Select.Option, {
753
- key: String(value),
754
- value: String(value)
755
- }, label2 ?? String(value))
756
- )))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
757
+ var useFormValues = (path = []) => {
758
+ const { values: formValues } = useFormContext();
759
+ const jsonPath = createJsonPath2(path);
760
+ return getValue2(formValues, jsonPath);
757
761
  };
758
- var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
759
- const { status, error } = getStatus();
760
- const textareaRef = useRef2(null);
761
- const adjustHeight = useCallback4(() => {
762
- const textarea = textareaRef.current;
763
- if (textarea) {
764
- textarea.style.blockSize = "auto";
765
- textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
766
- }
767
- }, []);
768
- useEffect5(() => {
769
- adjustHeight();
770
- }, [
771
- adjustHeight
762
+ var useInputProps = (path = []) => {
763
+ const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
764
+ const stablePath = useMemo3(() => path, [
765
+ Array.isArray(path) ? path.join(".") : path
766
+ ]);
767
+ return useMemo3(() => ({
768
+ getStatus: () => getStatus(stablePath),
769
+ getValue: () => getFormValue(stablePath),
770
+ onValueChange: (type, value) => onValueChange(stablePath, type, value),
771
+ onBlur: () => onTouched(stablePath)
772
+ }), [
773
+ getStatus,
774
+ getFormValue,
775
+ onValueChange,
776
+ onTouched,
777
+ stablePath
772
778
  ]);
779
+ };
780
+ var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
781
+ const form = useForm(formOptions);
773
782
  useEffect5(() => {
774
- adjustHeight();
783
+ if (!formRef?.current) {
784
+ return;
785
+ }
786
+ const handleKeyDown = (event) => {
787
+ const keyIsEnter = event.key === "Enter";
788
+ const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
789
+ const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
790
+ const inputOptOut = event.target.hasAttribute("data-no-submit");
791
+ const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
792
+ const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
793
+ if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
794
+ if (!autoSave && form.canSave) {
795
+ form.handleSave();
796
+ }
797
+ if (autoSave && form.formIsValid) {
798
+ event.target.blur();
799
+ }
800
+ }
801
+ };
802
+ const formElement = formRef.current;
803
+ formElement.addEventListener("keydown", handleKeyDown);
804
+ return () => formElement.removeEventListener("keydown", handleKeyDown);
775
805
  }, [
776
- getValue3(),
777
- adjustHeight
806
+ formRef,
807
+ form.canSave,
808
+ form.formIsValid,
809
+ form.handleSave,
810
+ autoSave
778
811
  ]);
779
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
780
- validationValence: status
781
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
782
- error
783
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
784
- ref: textareaRef,
785
- disabled,
786
- placeholder,
787
- value: getValue3() ?? "",
788
- classNames: "min-bs-auto max-h-40 overflow-auto",
789
- onChange: (event) => onValueChange(type, event.target.value),
790
- onBlur,
791
- style: {
792
- resize: "none"
793
- },
794
- spellCheck: false
795
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
812
+ return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
813
+ value: form
814
+ }, children);
796
815
  };
797
816
 
798
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
799
- import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
800
- import { mx as mx7 } from "@dxos/react-ui-theme";
801
-
802
817
  // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
803
- import React8 from "react";
804
- import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
805
- import { mx as mx4 } from "@dxos/react-ui-theme";
806
818
  var FormActions = ({ onCancel, readonly }) => {
807
819
  const { t } = useTranslation3(translationKey);
808
820
  const { canSave, handleSave } = useFormContext();
@@ -1359,6 +1371,15 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1359
1371
  }, [
1360
1372
  view
1361
1373
  ]);
1374
+ const handleDelete = useCallback7((fieldId) => {
1375
+ if (fieldId === field?.id) {
1376
+ setField(void 0);
1377
+ }
1378
+ onDelete(fieldId);
1379
+ }, [
1380
+ onDelete,
1381
+ field
1382
+ ]);
1362
1383
  const handleMove = useCallback7((fromIndex, toIndex) => {
1363
1384
  const fields = [
1364
1385
  ...view.fields
@@ -1370,6 +1391,19 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1370
1391
  view.fields
1371
1392
  ]);
1372
1393
  const handleClose = useCallback7(() => setField(void 0), []);
1394
+ const hiddenProperties = projection.getHiddenProperties();
1395
+ const handleHide = useCallback7((fieldId) => {
1396
+ setField(void 0);
1397
+ projection.hideFieldProjection(fieldId);
1398
+ }, [
1399
+ projection
1400
+ ]);
1401
+ const handleShow = useCallback7((property) => {
1402
+ setField(void 0);
1403
+ projection.showFieldProjection(property);
1404
+ }, [
1405
+ projection
1406
+ ]);
1373
1407
  return /* @__PURE__ */ React13.createElement("div", {
1374
1408
  role: "none",
1375
1409
  className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
@@ -1402,9 +1436,35 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1402
1436
  classNames: mx8(grid, ghostHover, "cursor-pointer")
1403
1437
  }, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
1404
1438
  onClick: () => handleSelect(field2)
1405
- }, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1439
+ }, field2.path), /* @__PURE__ */ React13.createElement("div", {
1440
+ className: "flex items-center gap-2 -ml-4"
1441
+ }, /* @__PURE__ */ React13.createElement(List2.ItemButton, {
1442
+ icon: "ph--eye-slash--regular",
1443
+ disabled: view.fields.length <= 1,
1444
+ onClick: () => handleHide(field2.id)
1445
+ }), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1446
+ icon: "ph--trash--regular",
1406
1447
  disabled: view.fields.length <= 1,
1407
- onClick: () => onDelete(field2.id)
1448
+ onClick: () => handleDelete(field2.id)
1449
+ }))))))), hiddenProperties.length > 0 && /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
1450
+ role: "none",
1451
+ className: "p-2"
1452
+ }, /* @__PURE__ */ React13.createElement("label", {
1453
+ className: mx8(inputTextLabel)
1454
+ }, t("hidden fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
1455
+ items: hiddenProperties,
1456
+ isItem: (item) => typeof item === "string",
1457
+ getId: (property) => property
1458
+ }, ({ items: properties }) => /* @__PURE__ */ React13.createElement("div", {
1459
+ role: "list",
1460
+ className: "flex flex-col w-full"
1461
+ }, properties?.map((property) => /* @__PURE__ */ React13.createElement(List2.Item, {
1462
+ key: property,
1463
+ item: property,
1464
+ classNames: mx8(grid, ghostHover, "cursor-pointer")
1465
+ }, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, null, property), /* @__PURE__ */ React13.createElement(List2.ItemButton, {
1466
+ icon: "ph--eye--regular",
1467
+ onClick: () => handleShow(property)
1408
1468
  }))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
1409
1469
  key: field.id,
1410
1470
  view,
@@ -1540,6 +1600,7 @@ var cellClassesForFieldType = ({ type, format }) => {
1540
1600
  "text-right"
1541
1601
  ];
1542
1602
  case FormatEnum3.JSON:
1603
+ case FormatEnum3.DID:
1543
1604
  return [
1544
1605
  "font-mono"
1545
1606
  ];
@@ -1688,6 +1749,7 @@ export {
1688
1749
  DeprecatedFormInput,
1689
1750
  FieldEditor,
1690
1751
  Form,
1752
+ FormProvider,
1691
1753
  GeoPointInput,
1692
1754
  InputHeader,
1693
1755
  MarkdownInput,
@@ -1705,6 +1767,8 @@ export {
1705
1767
  pathNotUniqueError,
1706
1768
  translations_default as translations,
1707
1769
  useForm,
1708
- useFormValues
1770
+ useFormContext,
1771
+ useFormValues,
1772
+ useInputProps
1709
1773
  };
1710
1774
  //# sourceMappingURL=index.mjs.map