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