@dxos/react-ui-form 0.8.1-staging.391c573 → 0.8.1-staging.5be625a

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.
@@ -108,326 +108,54 @@ import { log as log2 } from "@dxos/log";
108
108
  import { useTranslation as useTranslation5 } from "@dxos/react-ui";
109
109
  import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
110
110
 
111
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
112
- import React2, { createContext, useContext, useEffect as useEffect2, useMemo as useMemo2 } from "react";
113
- import { raise } from "@dxos/debug";
114
- import { getValue as getValue2 } from "@dxos/echo-schema";
115
- import { createJsonPath as createJsonPath2 } from "@dxos/effect";
116
-
117
- // packages/ui/react-ui-form/src/hooks/useForm.ts
118
- import { SchemaAST as AST } from "effect";
119
- import { useCallback, useEffect, useMemo, useState } from "react";
120
- import { getValue, setValue } from "@dxos/echo-schema";
121
- import { createJsonPath, fromEffectValidationPath } from "@dxos/effect";
122
- import { invariant } from "@dxos/invariant";
123
- import { log } from "@dxos/log";
124
- import { validateSchema } from "@dxos/schema";
125
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
126
- var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
127
- invariant(AST.isTypeLiteral(schema.ast), void 0, {
128
- F: __dxlog_file,
129
- L: 93,
130
- S: void 0,
131
- A: [
132
- "AST.isTypeLiteral(schema.ast)",
133
- ""
134
- ]
135
- });
136
- const [values, setValues] = useState(initialValues);
137
- useEffect(() => {
138
- setValues(initialValues);
139
- }, [
140
- initialValues
141
- ]);
142
- const [touched, setTouched] = useState(createKeySet(initialValues, false));
143
- const [changed, setChanged] = useState(createKeySet(initialValues, false));
144
- const [errors, setErrors] = useState({});
145
- const [saving, setSaving] = useState(false);
146
- const validate = useCallback((values2) => {
147
- let errors2 = validateSchema(schema, values2) ?? [];
148
- if (errors2.length === 0 && onValidate) {
149
- const validatedValues = values2;
150
- errors2 = onValidate(validatedValues) ?? [];
151
- }
152
- setErrors(flatMap(errors2));
153
- const valid = errors2.length === 0;
154
- return valid;
155
- }, [
156
- schema,
157
- onValidate
158
- ]);
159
- const [schemaDirty, setSchemaDirty] = useState(false);
160
- useEffect(() => setSchemaDirty(true), [
161
- schema
162
- ]);
163
- useEffect(() => {
164
- if (schemaDirty) {
165
- validate(values);
166
- setSchemaDirty(false);
167
- }
168
- }, [
169
- values,
170
- schemaDirty
171
- ]);
172
- const formIsValid = useMemo(() => {
173
- return Object.keys(errors).length === 0;
174
- }, [
175
- errors
176
- ]);
177
- const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors.
178
- !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
179
- touched,
180
- errors,
181
- saving
182
- ]);
183
- const handleSave = useCallback(async () => {
184
- if (validate(values)) {
185
- setSaving(true);
186
- try {
187
- await onSave?.(values, {
188
- changed
189
- });
190
- } finally {
191
- setSaving(false);
192
- }
193
- }
194
- }, [
195
- values,
196
- validate,
197
- onSave,
198
- changed
199
- ]);
200
- const getStatus = useCallback((path) => {
201
- const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
202
- const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
203
- return {
204
- status: matchingError ? "error" : void 0,
205
- error: matchingError ? matchingError[1] : void 0
206
- };
207
- }, [
208
- errors
209
- ]);
210
- const getFormValue = useCallback((path) => {
211
- return getValue(values, createJsonPath(path));
212
- }, [
213
- values
214
- ]);
215
- const onValueChange = useCallback((path, type, value) => {
216
- const jsonPath = createJsonPath(path);
217
- let parsedValue = value;
218
- try {
219
- if (type === "number") {
220
- parsedValue = parseFloat(value) || 0;
221
- }
222
- } catch (err) {
223
- log.catch(err, void 0, {
224
- F: __dxlog_file,
225
- L: 203,
226
- S: void 0,
227
- C: (f, a) => f(...a)
228
- });
229
- parsedValue = void 0;
230
- }
231
- const newValues = {
232
- ...setValue(values, jsonPath, parsedValue)
233
- };
234
- setValues(newValues);
235
- setChanged((prev) => ({
236
- ...prev,
237
- [jsonPath]: true
238
- }));
239
- onValuesChanged?.(newValues);
240
- const isValid = validate(newValues);
241
- if (isValid && onValid) {
242
- onValid(newValues, {
243
- changed
244
- });
245
- }
246
- }, [
247
- values,
248
- onValuesChanged,
249
- validate,
250
- onValid,
251
- changed
252
- ]);
253
- const onTouched = useCallback((path) => {
254
- const jsonPath = createJsonPath(path);
255
- setTouched((touched2) => ({
256
- ...touched2,
257
- [jsonPath]: true
258
- }));
259
- validate(values);
260
- }, [
261
- validate,
262
- values
263
- ]);
264
- return useMemo(() => ({
265
- // State.
266
- values,
267
- errors,
268
- touched,
269
- changed,
270
- canSave,
271
- formIsValid,
272
- // Actions.
273
- handleSave,
274
- // Field utils.
275
- getStatus,
276
- getValue: getFormValue,
277
- onValueChange,
278
- onTouched
279
- }), [
280
- values,
281
- errors,
282
- touched,
283
- changed,
284
- canSave,
285
- formIsValid,
286
- handleSave,
287
- getStatus,
288
- getFormValue,
289
- onValueChange,
290
- onTouched
291
- ]);
292
- };
293
- var createKeySet = (obj, value) => {
294
- invariant(obj, void 0, {
295
- F: __dxlog_file,
296
- L: 265,
297
- S: void 0,
298
- A: [
299
- "obj",
300
- ""
301
- ]
302
- });
303
- return Object.keys(obj).reduce((acc, key) => ({
304
- ...acc,
305
- [key]: value
306
- }), {});
307
- };
308
- var flatMap = (errors) => {
309
- return errors.reduce((result, { path, message }) => {
310
- const jsonPath = fromEffectValidationPath(path);
311
- if (!(jsonPath in result)) {
312
- result[jsonPath] = message;
313
- }
314
- return result;
315
- }, {});
316
- };
317
-
318
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
319
- var FormContext = /* @__PURE__ */ createContext(void 0);
320
- var useFormContext = () => {
321
- return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
322
- };
323
- var useFormValues = (path = []) => {
324
- const { values: formValues } = useFormContext();
325
- const jsonPath = createJsonPath2(path);
326
- return getValue2(formValues, jsonPath);
327
- };
328
- var useInputProps = (path = []) => {
329
- const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
330
- const stablePath = useMemo2(() => path, [
331
- Array.isArray(path) ? path.join(".") : path
332
- ]);
333
- return useMemo2(() => ({
334
- getStatus: () => getStatus(stablePath),
335
- getValue: () => getFormValue(stablePath),
336
- onValueChange: (type, value) => onValueChange(stablePath, type, value),
337
- onBlur: () => onTouched(stablePath)
338
- }), [
339
- getStatus,
340
- getFormValue,
341
- onValueChange,
342
- onTouched,
343
- stablePath
344
- ]);
345
- };
346
- var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
347
- const form = useForm(formOptions);
348
- useEffect2(() => {
349
- if (!formRef?.current) {
350
- return;
351
- }
352
- const handleKeyDown = (event) => {
353
- const keyIsEnter = event.key === "Enter";
354
- const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
355
- const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
356
- const inputOptOut = event.target.hasAttribute("data-no-submit");
357
- const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
358
- const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
359
- if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
360
- if (!autoSave && form.canSave) {
361
- form.handleSave();
362
- }
363
- if (autoSave && form.formIsValid) {
364
- event.target.blur();
365
- }
366
- }
367
- };
368
- const formElement = formRef.current;
369
- formElement.addEventListener("keydown", handleKeyDown);
370
- return () => formElement.removeEventListener("keydown", handleKeyDown);
371
- }, [
372
- formRef,
373
- form.canSave,
374
- form.formIsValid,
375
- form.handleSave,
376
- autoSave
377
- ]);
378
- return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
379
- value: form
380
- }, children);
381
- };
382
-
383
111
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
384
- 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";
385
113
  import { GeoLocation } from "@dxos/echo-schema";
386
114
  import { Input as Input2, useTranslation } from "@dxos/react-ui";
387
115
  import { safeParseFloat } from "@dxos/util";
388
116
 
389
117
  // packages/ui/react-ui-form/src/components/Form/Input.tsx
390
- import React3 from "react";
118
+ import React2 from "react";
391
119
  import { Icon, Tooltip } from "@dxos/react-ui";
392
120
  import { errorText } from "@dxos/react-ui-theme";
393
121
  var InputHeader = ({ children, error }) => {
394
- return /* @__PURE__ */ React3.createElement("div", {
122
+ return /* @__PURE__ */ React2.createElement("div", {
395
123
  role: "none",
396
124
  className: "flex justify-between items-center mb-1"
397
- }, 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, {
398
126
  asChild: true
399
- }, /* @__PURE__ */ React3.createElement(Icon, {
127
+ }, /* @__PURE__ */ React2.createElement(Icon, {
400
128
  icon: "ph--warning--regular",
401
129
  size: 4,
402
130
  classNames: errorText
403
- })), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
131
+ })), /* @__PURE__ */ React2.createElement(Tooltip.Content, {
404
132
  side: "bottom"
405
- }, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
133
+ }, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
406
134
  };
407
135
 
408
136
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
409
137
  var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
410
138
  const { t } = useTranslation(translationKey);
411
139
  const { status, error } = getStatus();
412
- const geoPoint = useMemo3(() => getValue3() ?? [
140
+ const geoPoint = useMemo(() => getValue3() ?? [
413
141
  0,
414
142
  0
415
143
  ], [
416
144
  getValue3
417
145
  ]);
418
- const geoLocation = useMemo3(() => GeoLocation.fromGeoPoint(geoPoint), [
146
+ const geoLocation = useMemo(() => GeoLocation.fromGeoPoint(geoPoint), [
419
147
  geoPoint
420
148
  ]);
421
- const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
422
- const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
423
- useEffect3(() => {
149
+ const [longitudeText, setLongitudeText] = useState(geoLocation.longitude?.toString());
150
+ const [latitudeText, setLatitudeText] = useState(geoLocation.latitude?.toString());
151
+ useEffect(() => {
424
152
  const location = GeoLocation.fromGeoPoint(geoPoint);
425
153
  setLongitudeText(location.longitude.toString());
426
154
  setLatitudeText(location.latitude.toString());
427
155
  }, [
428
156
  geoPoint
429
157
  ]);
430
- const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
158
+ const handleCoordinateChange = useCallback((coordinateType, setText) => (event) => {
431
159
  const inputText = event.target.value;
432
160
  setText(inputText);
433
161
  if (inputText !== "" && inputText !== "-") {
@@ -450,14 +178,14 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
450
178
  onValueChange,
451
179
  type
452
180
  ]);
453
- return /* @__PURE__ */ React4.createElement(Input2.Root, {
181
+ return /* @__PURE__ */ React3.createElement(Input2.Root, {
454
182
  validationValence: status
455
- }, /* @__PURE__ */ React4.createElement(InputHeader, {
183
+ }, /* @__PURE__ */ React3.createElement(InputHeader, {
456
184
  error
457
- }, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
185
+ }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
458
186
  role: "none",
459
187
  className: "grid grid-cols-2 gap-2"
460
- }, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
188
+ }, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
461
189
  type: "text",
462
190
  pattern: "^-?[0-9]*\\.?[0-9]*$",
463
191
  disabled,
@@ -465,7 +193,7 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
465
193
  value: latitudeText,
466
194
  onChange: handleCoordinateChange("latitude", setLatitudeText),
467
195
  onBlur
468
- }), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
196
+ }), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
469
197
  type: "text",
470
198
  pattern: "^-?[0-9]*\\.?[0-9]*$",
471
199
  disabled,
@@ -477,20 +205,20 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
477
205
  };
478
206
 
479
207
  // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
480
- 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";
481
209
  import { PublicKey } from "@dxos/keys";
482
210
  import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
483
211
  import { List } from "@dxos/react-ui-list";
484
212
  import { HuePicker } from "@dxos/react-ui-pickers";
485
213
  import { hues, mx as mx2 } from "@dxos/react-ui-theme";
486
214
  var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
487
- const [selected, setSelectedId] = useState3(null);
488
- const [isNewOption, setIsNewOption] = useState3(false);
215
+ const [selected, setSelectedId] = useState2(null);
216
+ const [isNewOption, setIsNewOption] = useState2(false);
489
217
  const inputRef = useRef(null);
490
218
  const { t } = useTranslation2(translationKey);
491
219
  const { status, error } = getStatus();
492
220
  const options = getValue3();
493
- useEffect4(() => {
221
+ useEffect2(() => {
494
222
  if (options === void 0) {
495
223
  onValueChange(type, []);
496
224
  }
@@ -509,7 +237,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
509
237
  const availableHues = hues.filter((hue) => !lastThree.has(hue));
510
238
  return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
511
239
  };
512
- const handleAdd = useCallback3(() => {
240
+ const handleAdd = useCallback2(() => {
513
241
  const newId = PublicKey.random().truncate();
514
242
  const newOption = {
515
243
  id: newId,
@@ -527,7 +255,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
527
255
  type,
528
256
  onValueChange
529
257
  ]);
530
- const handleDelete = useCallback3((id) => {
258
+ const handleDelete = useCallback2((id) => {
531
259
  const newOptions = options?.filter((option) => option.id !== id) ?? [];
532
260
  onValueChange(type, newOptions);
533
261
  setSelectedId(null);
@@ -536,7 +264,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
536
264
  type,
537
265
  onValueChange
538
266
  ]);
539
- const handleMove = useCallback3((from, to) => {
267
+ const handleMove = useCallback2((from, to) => {
540
268
  if (!options) {
541
269
  return;
542
270
  }
@@ -551,10 +279,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
551
279
  type,
552
280
  onValueChange
553
281
  ]);
554
- const handleClick = useCallback3((id) => {
282
+ const handleClick = useCallback2((id) => {
555
283
  setSelectedId((current) => current === id ? null : id);
556
284
  }, []);
557
- const handleTitleChange = useCallback3((id) => (e) => {
285
+ const handleTitleChange = useCallback2((id) => (e) => {
558
286
  const newOptions = options?.map((o) => o.id === id ? {
559
287
  ...o,
560
288
  title: e.target.value
@@ -565,12 +293,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
565
293
  type,
566
294
  onValueChange
567
295
  ]);
568
- const handleKeyDown = useCallback3((e) => {
296
+ const handleKeyDown = useCallback2((e) => {
569
297
  if (e.key === "Enter") {
570
298
  setSelectedId(null);
571
299
  }
572
300
  }, []);
573
- const handleColorChange = useCallback3((id) => (hue) => {
301
+ const handleColorChange = useCallback2((id) => (hue) => {
574
302
  const newOptions = options?.map((o) => o.id === id ? {
575
303
  ...o,
576
304
  color: hue
@@ -581,7 +309,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
581
309
  type,
582
310
  onValueChange
583
311
  ]);
584
- useEffect4(() => {
312
+ useEffect2(() => {
585
313
  if (selected && isNewOption && inputRef.current) {
586
314
  inputRef.current.focus();
587
315
  setIsNewOption(false);
@@ -590,43 +318,43 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
590
318
  selected,
591
319
  isNewOption
592
320
  ]);
593
- return /* @__PURE__ */ React5.createElement(Input3.Root, {
321
+ return /* @__PURE__ */ React4.createElement(Input3.Root, {
594
322
  validationValence: status
595
- }, /* @__PURE__ */ React5.createElement(InputHeader, {
323
+ }, /* @__PURE__ */ React4.createElement(InputHeader, {
596
324
  error
597
- }, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
325
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
598
326
  role: "none"
599
- }, options && /* @__PURE__ */ React5.createElement(List.Root, {
327
+ }, options && /* @__PURE__ */ React4.createElement(List.Root, {
600
328
  items: options,
601
329
  isItem: (item) => true,
602
330
  onMove: handleMove
603
- }, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
331
+ }, ({ items }) => /* @__PURE__ */ React4.createElement("div", {
604
332
  role: "list",
605
333
  className: "w-full overflow-auto"
606
- }, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
334
+ }, items?.map((item) => /* @__PURE__ */ React4.createElement("div", {
607
335
  key: item.id
608
- }, /* @__PURE__ */ React5.createElement(List.Item, {
336
+ }, /* @__PURE__ */ React4.createElement(List.Item, {
609
337
  role: "button",
610
338
  item,
611
339
  classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
612
340
  "aria-expanded": selected === item.id
613
- }, /* @__PURE__ */ React5.createElement("div", {
341
+ }, /* @__PURE__ */ React4.createElement("div", {
614
342
  className: "flex items-center"
615
- }, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
343
+ }, /* @__PURE__ */ React4.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React4.createElement(List.ItemTitle, {
616
344
  onClick: () => handleClick(item.id),
617
345
  classNames: "flex-1"
618
- }, /* @__PURE__ */ React5.createElement(Tag, {
346
+ }, /* @__PURE__ */ React4.createElement(Tag, {
619
347
  palette: item.color
620
- }, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
348
+ }, item.title || "\u200B")), /* @__PURE__ */ React4.createElement(Icon2, {
621
349
  icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
622
350
  onClick: () => handleClick(item.id)
623
- })), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
351
+ })), selected === item.id && /* @__PURE__ */ React4.createElement("div", {
624
352
  className: "mis-4 mbs-2 flex flex-col gap-1"
625
- }, /* @__PURE__ */ React5.createElement(Input3.Label, {
353
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, {
626
354
  classNames: "text-xs"
627
- }, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
355
+ }, t("select option label")), /* @__PURE__ */ React4.createElement("div", {
628
356
  className: "flex flex-row items-center gap-1"
629
- }, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
357
+ }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
630
358
  placeholder: t("select option label placeholder"),
631
359
  ref: selected === item.id ? inputRef : void 0,
632
360
  value: item.title,
@@ -634,33 +362,33 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
634
362
  onKeyDown: handleKeyDown,
635
363
  classNames: "flex-1",
636
364
  "data-no-submit": true
637
- }), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
365
+ }), /* @__PURE__ */ React4.createElement(Toolbar.Root, {
638
366
  classNames: "p-0 m-0 !is-auto"
639
- }, /* @__PURE__ */ React5.createElement(HuePicker, {
367
+ }, /* @__PURE__ */ React4.createElement(HuePicker, {
640
368
  value: item.color,
641
369
  onChange: handleColorChange(item.id)
642
- })), /* @__PURE__ */ React5.createElement(IconButton, {
370
+ })), /* @__PURE__ */ React4.createElement(IconButton, {
643
371
  icon: "ph--trash--fill",
644
372
  iconOnly: true,
645
373
  label: t("select option delete"),
646
374
  onClick: () => handleDelete(item.id)
647
- })))))), /* @__PURE__ */ React5.createElement("div", {
375
+ })))))), /* @__PURE__ */ React4.createElement("div", {
648
376
  role: "listitem"
649
- }, /* @__PURE__ */ React5.createElement("div", {
377
+ }, /* @__PURE__ */ React4.createElement("div", {
650
378
  role: "button",
651
379
  className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
652
380
  onClick: handleAdd
653
- }, /* @__PURE__ */ React5.createElement("div", {
381
+ }, /* @__PURE__ */ React4.createElement("div", {
654
382
  className: "flex items-center gap-1"
655
- }, /* @__PURE__ */ React5.createElement(Icon2, {
383
+ }, /* @__PURE__ */ React4.createElement(Icon2, {
656
384
  icon: "ph--plus--regular"
657
- }), /* @__PURE__ */ React5.createElement("span", {
385
+ }), /* @__PURE__ */ React4.createElement("span", {
658
386
  className: "text-sm"
659
387
  }, t("select option add")))))))));
660
388
  };
661
389
 
662
390
  // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
663
- import React6 from "react";
391
+ import React5 from "react";
664
392
  import { Input as Input4 } from "@dxos/react-ui";
665
393
  import { mx as mx3 } from "@dxos/react-ui-theme";
666
394
  import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
@@ -673,13 +401,13 @@ var gridCols = [
673
401
  var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
674
402
  const { status, error } = getStatus();
675
403
  const values = getValue3() ?? {};
676
- return /* @__PURE__ */ React6.createElement(Input4.Root, {
404
+ return /* @__PURE__ */ React5.createElement(Input4.Root, {
677
405
  validationValence: status
678
- }, /* @__PURE__ */ React6.createElement(InputHeader, {
406
+ }, /* @__PURE__ */ React5.createElement(InputHeader, {
679
407
  error
680
- }, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
408
+ }, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
681
409
  className: mx3("grid gap-2", gridCols[binding.length - 1])
682
- }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
410
+ }, binding.map((prop) => /* @__PURE__ */ React5.createElement(Input4.TextInput, {
683
411
  key: prop,
684
412
  type: "number",
685
413
  disabled,
@@ -691,120 +419,402 @@ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValu
691
419
  onBlur
692
420
  }))));
693
421
  };
694
-
695
- // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
696
- import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
697
- import { Input as Input5, Select } from "@dxos/react-ui";
698
- var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
699
- const { status, error } = getStatus();
700
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
701
- validationValence: status
702
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
703
- error
704
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
705
- disabled,
706
- placeholder,
707
- value: getValue3() ?? "",
708
- onChange: (event) => onValueChange(type, event.target.value),
709
- onBlur
710
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
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
+ ]);
726
+ };
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
+ }), {});
711
741
  };
712
- var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
713
- const { status, error } = getStatus();
714
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
715
- validationValence: status
716
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
717
- error
718
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
719
- type: "number",
720
- disabled,
721
- placeholder,
722
- value: getValue3() ?? 0,
723
- onChange: (event) => onValueChange(type, event.target.value),
724
- onBlur
725
- }), 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
+ }, {});
726
750
  };
727
- var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
728
- const { status, error } = getStatus();
729
- const checked = Boolean(getValue3());
730
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
731
- validationValence: status
732
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
733
- error
734
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
735
- checked,
736
- onCheckedChange: (value) => onValueChange(type, value)
737
- }), 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"));
738
756
  };
739
- var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
740
- const { status, error } = getStatus();
741
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
742
- validationValence: status
743
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
744
- error
745
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
746
- value: getValue3(),
747
- onValueChange: (value) => onValueChange(type, value)
748
- }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
749
- classNames: "is-full",
750
- disabled,
751
- placeholder
752
- }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
753
- // NOTE: Numeric values are converted to and from strings.
754
- /* @__PURE__ */ React7.createElement(Select.Option, {
755
- key: String(value),
756
- value: String(value)
757
- }, label2 ?? String(value))
758
- )))))), 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);
759
761
  };
760
- var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
761
- const { status, error } = getStatus();
762
- const textareaRef = useRef2(null);
763
- const adjustHeight = useCallback4(() => {
764
- const textarea = textareaRef.current;
765
- if (textarea) {
766
- textarea.style.blockSize = "auto";
767
- textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
768
- }
769
- }, []);
770
- useEffect5(() => {
771
- adjustHeight();
772
- }, [
773
- 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
774
778
  ]);
779
+ };
780
+ var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
781
+ const form = useForm(formOptions);
775
782
  useEffect5(() => {
776
- 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);
777
805
  }, [
778
- getValue3(),
779
- adjustHeight
806
+ formRef,
807
+ form.canSave,
808
+ form.formIsValid,
809
+ form.handleSave,
810
+ autoSave
780
811
  ]);
781
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
782
- validationValence: status
783
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
784
- error
785
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
786
- ref: textareaRef,
787
- disabled,
788
- placeholder,
789
- value: getValue3() ?? "",
790
- classNames: "min-bs-auto max-h-40 overflow-auto",
791
- onChange: (event) => onValueChange(type, event.target.value),
792
- onBlur,
793
- style: {
794
- resize: "none"
795
- },
796
- spellCheck: false
797
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
812
+ return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
813
+ value: form
814
+ }, children);
798
815
  };
799
816
 
800
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
801
- import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
802
- import { mx as mx7 } from "@dxos/react-ui-theme";
803
-
804
817
  // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
805
- import React8 from "react";
806
- import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
807
- import { mx as mx4 } from "@dxos/react-ui-theme";
808
818
  var FormActions = ({ onCancel, readonly }) => {
809
819
  const { t } = useTranslation3(translationKey);
810
820
  const { canSave, handleSave } = useFormContext();
@@ -1308,10 +1318,12 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1308
1318
  // packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
1309
1319
  import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
1310
1320
  import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
1321
+ import { invariant as invariant3 } from "@dxos/invariant";
1311
1322
  import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
1312
1323
  import { List as List2 } from "@dxos/react-ui-list";
1313
1324
  import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
1314
1325
  import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
1326
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx";
1315
1327
  var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
1316
1328
  var ViewMetaSchema = S2.Struct({
1317
1329
  name: S2.String.annotations({
@@ -1338,39 +1350,77 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1338
1350
  }, [
1339
1351
  view
1340
1352
  ]);
1341
- const handleViewUpdate = useCallback7(({ name, typename }) => {
1353
+ const handleSelect = useCallback7((field2) => {
1354
+ setField((f) => f === field2 ? void 0 : field2);
1355
+ }, []);
1356
+ const handleAdd = useCallback7(() => {
1357
+ invariant3(!schema.readonly, void 0, {
1358
+ F: __dxlog_file4,
1359
+ L: 76,
1360
+ S: void 0,
1361
+ A: [
1362
+ "!schema.readonly",
1363
+ ""
1364
+ ]
1365
+ });
1366
+ const field2 = projection.createFieldProjection();
1367
+ setField(field2);
1368
+ }, [
1369
+ schema,
1370
+ view
1371
+ ]);
1372
+ const handleUpdate = useCallback7(({ name, typename }) => {
1373
+ invariant3(!schema.readonly, void 0, {
1374
+ F: __dxlog_file4,
1375
+ L: 83,
1376
+ S: void 0,
1377
+ A: [
1378
+ "!schema.readonly",
1379
+ ""
1380
+ ]
1381
+ });
1342
1382
  requestAnimationFrame(() => {
1343
1383
  if (view.name !== name) {
1344
1384
  view.name = name;
1345
1385
  }
1346
1386
  if (view.query.typename !== typename) {
1347
- onTypenameChanged(typename);
1387
+ onTypenameChanged?.(typename);
1348
1388
  }
1349
1389
  });
1350
1390
  }, [
1351
- view,
1352
1391
  schema,
1392
+ view,
1353
1393
  onTypenameChanged
1354
1394
  ]);
1355
- const handleSelect = useCallback7((field2) => {
1356
- setField((f) => f === field2 ? void 0 : field2);
1357
- }, []);
1358
- const handleAdd = useCallback7(() => {
1359
- const field2 = projection.createFieldProjection();
1360
- setField(field2);
1361
- }, [
1362
- view
1363
- ]);
1364
1395
  const handleDelete = useCallback7((fieldId) => {
1396
+ invariant3(!schema.readonly, void 0, {
1397
+ F: __dxlog_file4,
1398
+ L: 99,
1399
+ S: void 0,
1400
+ A: [
1401
+ "!schema.readonly",
1402
+ ""
1403
+ ]
1404
+ });
1365
1405
  if (fieldId === field?.id) {
1366
1406
  setField(void 0);
1367
1407
  }
1368
- onDelete(fieldId);
1408
+ onDelete?.(fieldId);
1369
1409
  }, [
1370
- onDelete,
1371
- field
1410
+ schema,
1411
+ field,
1412
+ onDelete
1372
1413
  ]);
1373
1414
  const handleMove = useCallback7((fromIndex, toIndex) => {
1415
+ invariant3(!schema.readonly, void 0, {
1416
+ F: __dxlog_file4,
1417
+ L: 111,
1418
+ S: void 0,
1419
+ A: [
1420
+ "!schema.readonly",
1421
+ ""
1422
+ ]
1423
+ });
1374
1424
  const fields = [
1375
1425
  ...view.fields
1376
1426
  ];
@@ -1378,6 +1428,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1378
1428
  fields.splice(toIndex, 0, moved);
1379
1429
  view.fields = fields;
1380
1430
  }, [
1431
+ schema,
1381
1432
  view.fields
1382
1433
  ]);
1383
1434
  const handleClose = useCallback7(() => setField(void 0), []);
@@ -1401,7 +1452,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1401
1452
  autoSave: true,
1402
1453
  schema: ViewMetaSchema,
1403
1454
  values: viewValues,
1404
- onSave: handleViewUpdate
1455
+ onSave: schema.readonly ? void 0 : handleUpdate
1405
1456
  }), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
1406
1457
  role: "none",
1407
1458
  className: "p-2"
@@ -1411,7 +1462,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1411
1462
  items: view.fields,
1412
1463
  isItem: S2.is(FieldSchema),
1413
1464
  getId: (field2) => field2.id,
1414
- onMove: handleMove
1465
+ onMove: schema.readonly ? void 0 : handleMove
1415
1466
  }, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
1416
1467
  role: "heading",
1417
1468
  className: grid
@@ -1432,7 +1483,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1432
1483
  icon: "ph--eye-slash--regular",
1433
1484
  disabled: view.fields.length <= 1,
1434
1485
  onClick: () => handleHide(field2.id)
1435
- }), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1486
+ }), !schema.readonly && /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1436
1487
  icon: "ph--trash--regular",
1437
1488
  disabled: view.fields.length <= 1,
1438
1489
  onClick: () => handleDelete(field2.id)
@@ -1467,7 +1518,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1467
1518
  }, /* @__PURE__ */ React13.createElement(IconButton4, {
1468
1519
  icon: "ph--plus--regular",
1469
1520
  label: t("button add property"),
1470
- onClick: handleAdd,
1521
+ onClick: schema.readonly ? void 0 : handleAdd,
1471
1522
  // TODO(burdon): Show field limit in ux (not tooltip).
1472
1523
  disabled: view.fields.length >= VIEW_FIELD_LIMIT
1473
1524
  })));
@@ -1590,6 +1641,7 @@ var cellClassesForFieldType = ({ type, format }) => {
1590
1641
  "text-right"
1591
1642
  ];
1592
1643
  case FormatEnum3.JSON:
1644
+ case FormatEnum3.DID:
1593
1645
  return [
1594
1646
  "font-mono"
1595
1647
  ];
@@ -1738,6 +1790,7 @@ export {
1738
1790
  DeprecatedFormInput,
1739
1791
  FieldEditor,
1740
1792
  Form,
1793
+ FormProvider,
1741
1794
  GeoPointInput,
1742
1795
  InputHeader,
1743
1796
  MarkdownInput,
@@ -1755,6 +1808,8 @@ export {
1755
1808
  pathNotUniqueError,
1756
1809
  translations_default as translations,
1757
1810
  useForm,
1758
- useFormValues
1811
+ useFormContext,
1812
+ useFormValues,
1813
+ useInputProps
1759
1814
  };
1760
1815
  //# sourceMappingURL=index.mjs.map