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