@dxos/react-ui-form 0.9.1-staging.ee54ba693a → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/lib/index.mjs +4064 -0
  2. package/dist/lib/index.mjs.map +1 -0
  3. package/dist/lib/translations.mjs +77 -0
  4. package/dist/lib/translations.mjs.map +1 -0
  5. package/dist/lib/types.mjs +0 -0
  6. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +1 -0
  7. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/Form/Card.stories.d.ts +88 -0
  9. package/dist/types/src/components/Form/Card.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/Form/Form.d.ts +7 -7
  11. package/dist/types/src/components/Form/Form.stories.d.ts +21 -0
  12. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.theme.d.ts +6 -0
  14. package/dist/types/src/components/Form/Form.theme.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/FormControls.d.ts +7 -7
  16. package/dist/types/src/components/Form/FormControls.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/FormField/FormField.d.ts +1 -1
  18. package/dist/types/src/components/Form/FormField/FormField.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormField/FormRow.d.ts +17 -3
  20. package/dist/types/src/components/Form/FormField/FormRow.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.d.ts +0 -6
  22. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts +1 -0
  24. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/FormField/fields/ArrayField/default-value.d.ts +7 -0
  26. package/dist/types/src/components/Form/FormField/fields/ArrayField/default-value.d.ts.map +1 -0
  27. package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts +1 -0
  28. package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.stories.d.ts +1 -0
  30. package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.stories.d.ts.map +1 -1
  31. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts +1 -0
  33. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts +1 -0
  35. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.d.ts.map +1 -1
  37. package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.d.ts +1 -0
  38. package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.d.ts.map +1 -1
  39. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts +0 -11
  40. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts.map +1 -1
  41. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts +1 -0
  42. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/Form/FormField/fields/NumberField/index.d.ts +1 -0
  44. package/dist/types/src/components/Form/FormField/fields/NumberField/index.d.ts.map +1 -1
  45. package/dist/types/src/components/Form/FormField/fields/NumberField/numeric-constraints.d.ts +12 -0
  46. package/dist/types/src/components/Form/FormField/fields/NumberField/numeric-constraints.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.d.ts.map +1 -1
  48. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts +1 -0
  49. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts.map +1 -1
  50. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts +1 -11
  51. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts.map +1 -1
  52. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts +1 -0
  53. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/Form/FormField/fields/RefField/find-ref-option.d.ts +13 -0
  55. package/dist/types/src/components/Form/FormField/fields/RefField/find-ref-option.d.ts.map +1 -0
  56. package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts +1 -0
  57. package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts.map +1 -1
  58. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts +1 -13
  59. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts.map +1 -1
  60. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts +1 -0
  61. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts.map +1 -1
  62. package/dist/types/src/components/Form/FormField/fields/SelectField/create-select-field.d.ts +15 -0
  63. package/dist/types/src/components/Form/FormField/fields/SelectField/create-select-field.d.ts.map +1 -0
  64. package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts +1 -0
  65. package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts.map +1 -1
  66. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.d.ts.map +1 -1
  67. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.stories.d.ts +1 -0
  68. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.stories.d.ts.map +1 -1
  69. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts +1 -0
  70. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts.map +1 -1
  71. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts +1 -0
  72. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts.map +1 -1
  73. package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts +1 -0
  74. package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts +1 -1
  76. package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts.map +1 -1
  77. package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts +0 -27
  78. package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts.map +1 -1
  79. package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts +1 -0
  80. package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts.map +1 -1
  81. package/dist/types/src/components/Form/FormLayout/index.d.ts +1 -0
  82. package/dist/types/src/components/Form/FormLayout/index.d.ts.map +1 -1
  83. package/dist/types/src/components/Form/FormLayout/resolve-layout-field.d.ts +29 -0
  84. package/dist/types/src/components/Form/FormLayout/resolve-layout-field.d.ts.map +1 -0
  85. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +10 -1
  86. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -1
  87. package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts +2 -0
  88. package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts.map +1 -1
  89. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +8 -23
  90. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  91. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +1 -0
  92. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +1 -1
  94. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -1
  95. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +1 -0
  96. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -1
  97. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts +1 -0
  98. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/RefEditor/RefEditor.d.ts.map +1 -1
  100. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts +2 -0
  101. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts.map +1 -1
  102. package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts +9 -8
  103. package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts.map +1 -1
  104. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +1 -0
  105. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  106. package/dist/types/src/hooks/useFormHandler.d.ts +6 -2
  107. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
  108. package/dist/types/src/hooks/useFormHandler.test.d.ts +2 -0
  109. package/dist/types/src/hooks/useFormHandler.test.d.ts.map +1 -0
  110. package/dist/types/src/testing/TestLayout.d.ts +1 -1
  111. package/dist/types/src/testing/TestLayout.d.ts.map +1 -1
  112. package/dist/types/src/translations.d.ts +1 -0
  113. package/dist/types/src/translations.d.ts.map +1 -1
  114. package/dist/types/src/types.d.ts +5 -0
  115. package/dist/types/src/types.d.ts.map +1 -1
  116. package/dist/types/src/util/index.d.ts +0 -1
  117. package/dist/types/src/util/index.d.ts.map +1 -1
  118. package/dist/types/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +39 -42
  120. package/src/components/Form/Card.stories.tsx +93 -0
  121. package/src/components/Form/Form.stories.tsx +95 -3
  122. package/src/components/Form/Form.theme.ts +14 -8
  123. package/src/components/Form/Form.tsx +2 -2
  124. package/src/components/Form/FormControls.tsx +32 -13
  125. package/src/components/Form/FormField/FormField.tsx +11 -3
  126. package/src/components/Form/FormField/FormRow.tsx +20 -1
  127. package/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx +3 -46
  128. package/src/components/Form/FormField/fields/ArrayField/default-value.ts +49 -0
  129. package/src/components/Form/FormField/fields/ArrayField/index.ts +2 -0
  130. package/src/components/Form/FormField/fields/ComboboxField/ComboboxField.tsx +12 -2
  131. package/src/components/Form/FormField/fields/DateField/DateField.tsx +10 -3
  132. package/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.tsx +1 -1
  133. package/src/components/Form/FormField/fields/MarkdownField/MarkdownField.tsx +2 -12
  134. package/src/components/Form/FormField/fields/NumberField/NumberField.test.ts +1 -1
  135. package/src/components/Form/FormField/fields/NumberField/NumberField.tsx +1 -36
  136. package/src/components/Form/FormField/fields/NumberField/index.ts +2 -0
  137. package/src/components/Form/FormField/fields/NumberField/numeric-constraints.ts +43 -0
  138. package/src/components/Form/FormField/fields/PasswordField/PasswordField.tsx +1 -0
  139. package/src/components/Form/FormField/fields/RefField/InlineRefField.tsx +2 -2
  140. package/src/components/Form/FormField/fields/RefField/RefField.test.ts +1 -1
  141. package/src/components/Form/FormField/fields/RefField/RefField.tsx +3 -45
  142. package/src/components/Form/FormField/fields/RefField/find-ref-option.ts +56 -0
  143. package/src/components/Form/FormField/fields/RefField/index.ts +2 -0
  144. package/src/components/Form/FormField/fields/SelectField/SelectField.tsx +2 -67
  145. package/src/components/Form/FormField/fields/SelectField/create-select-field.tsx +80 -0
  146. package/src/components/Form/FormField/fields/SelectField/index.ts +2 -0
  147. package/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.tsx +2 -1
  148. package/src/components/Form/FormFieldSet/FormFieldSetContainer.tsx +11 -9
  149. package/src/components/Form/FormLayout/FormLayout.test.ts +1 -1
  150. package/src/components/Form/FormLayout/FormLayout.tsx +1 -86
  151. package/src/components/Form/FormLayout/index.ts +2 -0
  152. package/src/components/Form/FormLayout/resolve-layout-field.ts +98 -0
  153. package/src/components/ObjectForm/ObjectForm.stories.tsx +93 -3
  154. package/src/components/ObjectForm/ObjectForm.tsx +22 -4
  155. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +1 -1
  156. package/src/components/ObjectPicker/ObjectPicker.tsx +3 -3
  157. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +1 -1
  158. package/src/components/ObjectProperties/ObjectProperties.tsx +7 -1
  159. package/src/components/ObjectTree/ObjectTree.stories.tsx +1 -1
  160. package/src/components/RefEditor/RefEditor.stories.tsx +9 -1
  161. package/src/components/RefEditor/RefEditor.tsx +42 -6
  162. package/src/components/RefEditor/ref-editor-extension.ts +168 -91
  163. package/src/components/ViewEditor/ViewEditor.stories.tsx +1 -1
  164. package/src/components/ViewEditor/ViewEditor.tsx +9 -9
  165. package/src/hooks/useFormHandler.test.ts +85 -0
  166. package/src/hooks/useFormHandler.ts +118 -31
  167. package/src/translations.ts +1 -0
  168. package/src/types.ts +5 -0
  169. package/src/util/index.ts +0 -1
  170. package/dist/lib/browser/index.mjs +0 -4675
  171. package/dist/lib/browser/index.mjs.map +0 -7
  172. package/dist/lib/browser/meta.json +0 -1
  173. package/dist/lib/browser/translations.mjs +0 -87
  174. package/dist/lib/browser/translations.mjs.map +0 -7
  175. package/dist/lib/node-esm/index.mjs +0 -4677
  176. package/dist/lib/node-esm/index.mjs.map +0 -7
  177. package/dist/lib/node-esm/meta.json +0 -1
  178. package/dist/lib/node-esm/translations.mjs +0 -89
  179. package/dist/lib/node-esm/translations.mjs.map +0 -7
  180. package/dist/types/src/util/formatting.d.ts +0 -15
  181. package/dist/types/src/util/formatting.d.ts.map +0 -1
  182. package/src/util/formatting.ts +0 -171
@@ -0,0 +1,4064 @@
1
+ import "./types.mjs";
2
+ import { ANY_OBJECT_TYPENAME, ReferenceAnnotationId, createAnnotationHelper } from "@dxos/echo/internal";
3
+ import { Children, Component, Fragment, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
4
+ import { Annotation, DXN, EID, Entity, Feed, Filter, Format, Obj, Query, QueryAST, Ref, Scope, Tag, Type, View } from "@dxos/echo";
5
+ import { Format as Format$1, FormatEnums, GeoLocation, TypeEnum, formatToType } from "@dxos/echo/Format";
6
+ import { log } from "@dxos/log";
7
+ import { Button, Column, Icon, IconButton, Input, Message, Popover, ScrollArea, Select, Tag as Tag$1, ToggleIconButton, Tooltip, composable, composableProps, useAsyncEffect, useDefaultValue, useInColumn, useMergeRefs, useThemeContext, useTranslation, withColumn } from "@dxos/react-ui";
8
+ import { FactoryAnnotation, ParentLabelAnnotation, ParentLabelAnnotationId, ProjectionModel, Text, VIEW_FIELD_LIMIT, createEchoChangeCallback, formatToAdditionalPropertyAttributes, getFormatSchema, getTypeURIFromQuery, validateSchema } from "@dxos/schema";
9
+ import { translationKey } from "#translations";
10
+ import * as Option from "effect/Option";
11
+ import * as Schema from "effect/Schema";
12
+ import * as SchemaAST from "effect/SchemaAST";
13
+ import { SchemaEx } from "@dxos/effect";
14
+ import { DEFAULT_LAYOUT_NAME, FormLayoutAnnotation, ReferenceAnnotationId as ReferenceAnnotationId$1 } from "@dxos/echo/Annotation";
15
+ import { MarkdownView } from "@dxos/react-ui-markdown";
16
+ import { getHashHue, getStyles, mx, osTranslations, tv } from "@dxos/ui-theme";
17
+ import * as Cause from "effect/Cause";
18
+ import * as Effect from "effect/Effect";
19
+ import * as Exit from "effect/Exit";
20
+ import { createContext } from "@radix-ui/react-context";
21
+ import * as Equal from "effect/Equal";
22
+ import * as Utils from "effect/Utils";
23
+ import { addEventListener } from "@dxos/async";
24
+ import { useObject, useQuery, useType } from "@dxos/echo-react";
25
+ import { DXN as DXN$1, EID as EID$1, PublicKey, URI } from "@dxos/keys";
26
+ import { Combobox, OrderedList } from "@dxos/react-ui-list";
27
+ import { arrayMove, safeParseFloat } from "@dxos/util";
28
+ import * as Match from "effect/Match";
29
+ import * as String$1 from "effect/String";
30
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
31
+ import { format } from "date-fns";
32
+ import { invariant } from "@dxos/invariant";
33
+ import { RegistryContext, useAtomValue } from "@effect-atom/atom-react";
34
+ import { Doc } from "@dxos/echo-doc";
35
+ import { Editor, useBasicMarkdownExtensions } from "@dxos/react-ui-editor";
36
+ import { createBasicExtensions, createDataExtensions, createThemeExtensions, insertAtCursor, keymap } from "@dxos/ui-editor";
37
+ import "@dxos/lit-ui/dx-tag-picker.pcss";
38
+ import { DxAnchor } from "@dxos/lit-ui/react";
39
+ import { useSearchListResults } from "@dxos/react-ui-search";
40
+ import { HuePicker } from "@dxos/react-ui-pickers";
41
+ import { hues } from "@dxos/ui-types";
42
+ import * as Function from "effect/Function";
43
+ import { EditorState, Prec, RangeSetBuilder, StateEffect, StateField } from "@codemirror/state";
44
+ import { Decoration, EditorView, WidgetType } from "@codemirror/view";
45
+ import { Domino } from "@dxos/ui";
46
+ import { QueryForm } from "@dxos/react-ui-components";
47
+ //#region src/annotations.ts
48
+ var OptionsLookupAnnotationId = Symbol.for("@dxos/schema/annotation/OptionsLookup");
49
+ var OptionsLookupAnnotation = createAnnotationHelper(OptionsLookupAnnotationId);
50
+ /**
51
+ * Builds an {@link OptionsLookup} typed against a schema's value type `Values`: `deps` is checked
52
+ * against its field names, and `load` receives only those fields, narrowed. Pass `{ combobox: true }` to
53
+ * render an editable combobox.
54
+ */
55
+ var optionsLookup = () => (deps, load, options) => ({
56
+ deps,
57
+ load,
58
+ combobox: options?.combobox
59
+ });
60
+ var AutofillAnnotationId = Symbol.for("@dxos/schema/annotation/Autofill");
61
+ var AutofillAnnotation = createAnnotationHelper(AutofillAnnotationId);
62
+ /** Builds an {@link Autofill} typed against a schema's value type `Values` (see {@link optionsLookup}). */
63
+ var autofill = () => (deps, derive) => ({
64
+ deps,
65
+ derive
66
+ });
67
+ //#endregion
68
+ //#region src/util/field.ts
69
+ var parseValue = ({ type, format, value }) => {
70
+ if (value === void 0 || value === null || value === "") return;
71
+ const parseScalar = (type) => {
72
+ switch (type) {
73
+ case TypeEnum.Boolean:
74
+ if (typeof value === "string") {
75
+ const lowercaseValue = value.toLowerCase();
76
+ if (lowercaseValue === "0" || lowercaseValue === "false") return false;
77
+ else if (lowercaseValue === "1" || lowercaseValue === "true") return true;
78
+ }
79
+ return Boolean(value);
80
+ case TypeEnum.Number: {
81
+ const cleaned = typeof value === "string" ? value.replace(/[^0-9.-]+/g, "") : value;
82
+ const num = Number(cleaned);
83
+ return Number.isNaN(num) ? null : num;
84
+ }
85
+ case TypeEnum.String:
86
+ case TypeEnum.Ref: return String(value);
87
+ default: return value;
88
+ }
89
+ };
90
+ if (!format) return parseScalar(type);
91
+ switch (format) {
92
+ case Format$1.TypeFormat.Boolean: return parseScalar(TypeEnum.Boolean);
93
+ case Format$1.TypeFormat.Number:
94
+ case Format$1.TypeFormat.Percent:
95
+ case Format$1.TypeFormat.Currency: return parseScalar(TypeEnum.Number);
96
+ case Format$1.TypeFormat.String:
97
+ case Format$1.TypeFormat.Markdown: return parseScalar(TypeEnum.String);
98
+ case Format$1.TypeFormat.Ref: throw new Error(`unexpected format: ${Format$1.TypeFormat.Ref}`);
99
+ case Format$1.TypeFormat.DateTime:
100
+ case Format$1.TypeFormat.Date:
101
+ case Format$1.TypeFormat.Time:
102
+ case Format$1.TypeFormat.Timestamp: {
103
+ const date = new Date(value);
104
+ return isNaN(date.getTime()) ? null : date;
105
+ }
106
+ case Format$1.TypeFormat.GeoPoint:
107
+ if (typeof value === "string") {
108
+ const parts = value.replace(/[^0-9.,-]+/g, "").split(",");
109
+ if (parts.length === 2) {
110
+ const lat = parseFloat(parts[0]);
111
+ const long = parseFloat(parts[1]);
112
+ if (!isNaN(lat) && !isNaN(long)) return GeoLocation.toGeoPoint({
113
+ latitude: lat,
114
+ longitude: long
115
+ });
116
+ }
117
+ }
118
+ return [0, 0];
119
+ default: return value;
120
+ }
121
+ };
122
+ var cellClassesForFieldType = ({ type, format }) => {
123
+ switch (format || type) {
124
+ case Format$1.TypeFormat.Markdown: return;
125
+ case Format$1.TypeFormat.Time:
126
+ case Format$1.TypeFormat.Timestamp:
127
+ case Format$1.TypeFormat.DateTime:
128
+ case Format$1.TypeFormat.Date:
129
+ case Format$1.TypeFormat.Duration: return ["font-mono", "text-right"];
130
+ case Format$1.TypeFormat.Currency: return ["text-right"];
131
+ case Format$1.TypeFormat.JSON:
132
+ case Format$1.TypeFormat.DID: return ["font-mono"];
133
+ case TypeEnum.Number:
134
+ case TypeEnum.Boolean: return ["text-right", "font-mono"];
135
+ case TypeEnum.String: return;
136
+ case TypeEnum.Ref: return;
137
+ default: return;
138
+ }
139
+ };
140
+ var pathNotUniqueError = (path) => ({
141
+ path: "path",
142
+ message: `'${path}' is already present`
143
+ });
144
+ //#endregion
145
+ //#region src/util/omit.ts
146
+ var omitId = (schemaOrType) => {
147
+ return omitIdFromSchema(Type.isType(schemaOrType) ? Type.getSchema(schemaOrType) : schemaOrType);
148
+ };
149
+ /**
150
+ * Removes the `id` field where present, preserving the schema's shape. Recurses into unions so a
151
+ * discriminated (create) schema keeps its discriminant — `Schema.omit('id')` is a struct operation and
152
+ * would otherwise flatten the union, breaking variant rendering. A no-op when there is no `id` (plain
153
+ * create structs that never carried one).
154
+ */
155
+ var omitIdFromSchema = (schema) => {
156
+ const ast = schema.ast;
157
+ if (SchemaAST.isUnion(ast)) return Schema.Union(...ast.types.map((type) => omitIdFromSchema(Schema.make(type))));
158
+ return SchemaAST.getPropertySignatures(ast).some((prop) => prop.name === "id") ? schema.pipe(Schema.omit("id")) : schema;
159
+ };
160
+ /**
161
+ * Drop fields annotated with `FormInputAnnotation.set(false)` from a schema so
162
+ * the form's validator doesn't trip on required-but-hidden fields. Used by
163
+ * the picker's inline create form, where a `FactoryAnnotation` typically
164
+ * supplies the hidden values (e.g. a backing-object Ref) outside the form.
165
+ */
166
+ var omitHiddenFormFields = (schema) => {
167
+ const hidden = SchemaAST.getPropertySignatures(schema.ast).filter((prop) => Option.getOrElse(Annotation.FormInputAnnotation.getFromAst(prop.type), () => true) === false).map((prop) => prop.name);
168
+ return hidden.length === 0 ? schema : schema.pipe(Schema.omit(...hidden));
169
+ };
170
+ //#endregion
171
+ //#region src/util/properties.ts
172
+ /** The property's type with an optional `T | undefined` union unwrapped to its inner `T`. */
173
+ var unwrapOptional = (prop) => {
174
+ if (!prop.isOptional || !SchemaAST.isUnion(prop.type)) return prop.type;
175
+ const defined = prop.type.types.filter((type) => type._tag !== "UndefinedKeyword");
176
+ if (defined.length === 0) return prop.type;
177
+ return defined.length === 1 ? defined[0] : SchemaAST.Union.make(defined, prop.type.annotations);
178
+ };
179
+ /**
180
+ * Resolve the properties to render for a field set's *root* schema, given the current form values.
181
+ *
182
+ * A top-level discriminated union is rendered flat: the fields of the member selected by the current
183
+ * discriminator value, with the discriminator field keeping the union-wide set of literals so it stays
184
+ * switchable. (`SchemaAST.getPropertySignatures` on a union returns only the common discriminator, so a
185
+ * union root would otherwise render just that one field.) Non-union roots are unchanged. Nested unions are
186
+ * unaffected — those are expanded per-field by `FormField`, which passes a single-member type literal here.
187
+ */
188
+ var getRootFormProperties = (ast, values) => {
189
+ if (!SchemaEx.isDiscriminatedUnion(ast)) return getFormProperties(ast);
190
+ const activeType = SchemaEx.getDiscriminatedType(ast, values ?? {});
191
+ if (!activeType) return getFormProperties(ast);
192
+ const activeProps = getFormProperties(activeType);
193
+ const discriminators = new Set((SchemaEx.getDiscriminatingProps(ast) ?? []).map((name) => name.toString()));
194
+ if (discriminators.size === 0) return activeProps;
195
+ const fullType = SchemaEx.getDiscriminatedType(ast, {});
196
+ const fullDiscriminatorProps = fullType ? getFormProperties(fullType) : activeProps;
197
+ return activeProps.map((prop) => discriminators.has(prop.name.toString()) ? fullDiscriminatorProps.find((candidate) => candidate.name === prop.name) ?? prop : prop);
198
+ };
199
+ /**
200
+ * Get the property types of an AST and filter out properties that are not form inputs.
201
+ *
202
+ * The `FormInputAnnotation` is read from the raw property signature's AST (before
203
+ * `encodedBoundAST` is applied by `SchemaEx.getProperties`), because `encodedBoundAST` strips
204
+ * annotations from non-keyword inner types (e.g., `Schema.Struct`, `Schema.Array`).
205
+ * Without this, fields like `Routine.input` (a JsonSchema struct) annotated
206
+ * `FormInputAnnotation.set(false)` would still render in the form.
207
+ *
208
+ * For optional fields, `prop.type` is the union `T | undefined` — the annotation
209
+ * lives on the inner `T`. Unwrap before reading so that the conventional pattern
210
+ * `Schema.String.pipe(FormInputAnnotation.set(false), Schema.optional)` works.
211
+ *
212
+ * Container properties (nested structs, unions, arrays) keep their *raw* AST rather than
213
+ * the `encodedBoundAST` produced by `SchemaEx.getProperties`. `encodedBoundAST` strips annotations
214
+ * from non-keyword inner types, so a `FormInputAnnotation` nested beneath an encoded container
215
+ * (e.g. `RouteLeg.geometry`, reached via `Segment → details → routes → Route → legs`) would
216
+ * otherwise be lost by the time recursion reaches it. Leaf/transformation fields stay encoded
217
+ * (e.g. a `DateTime` still renders as its encoded string input).
218
+ */
219
+ var getFormProperties = (ast) => {
220
+ const signatures = SchemaAST.getPropertySignatures(ast);
221
+ const rawByName = new Map(signatures.map((prop) => [prop.name, unwrapOptional(prop)]));
222
+ const hidden = new Set(signatures.filter((prop) => Annotation.FormInputAnnotation.getFromAst(unwrapOptional(prop)).pipe(Option.getOrElse(() => true)) === false).map((prop) => prop.name));
223
+ return SchemaEx.getProperties(ast).filter((prop) => !hidden.has(prop.name)).map((prop) => {
224
+ const raw = rawByName.get(prop.name);
225
+ return raw && (SchemaEx.isNestedType(raw) || SchemaEx.isArrayType(raw)) ? {
226
+ ...prop,
227
+ type: raw
228
+ } : prop;
229
+ });
230
+ };
231
+ //#endregion
232
+ //#region src/util/refs.ts
233
+ var getRefProps = (ast) => {
234
+ if (SchemaEx.isArrayType(ast)) {
235
+ const elementType = SchemaEx.getArrayElementType(ast);
236
+ if (elementType) {
237
+ if (Ref.isRefType(elementType)) return {
238
+ ast: elementType,
239
+ isArray: true,
240
+ typename: SchemaEx.findAnnotation(elementType, ReferenceAnnotationId$1)?.typename
241
+ };
242
+ }
243
+ }
244
+ if (Ref.isRefType(ast)) return {
245
+ ast,
246
+ isArray: false,
247
+ typename: SchemaEx.findAnnotation(ast, ReferenceAnnotationId$1)?.typename
248
+ };
249
+ };
250
+ //#endregion
251
+ //#region src/components/Form/meta-tags.ts
252
+ /**
253
+ * Form-field key for the synthetic meta-tags array spliced into property/create forms.
254
+ *
255
+ * An object's tags live in `Obj.getMeta(obj).tags` (off-schema), so the form surfaces them as an
256
+ * extra field rather than reading the schema. The key is underscore-prefixed so it never collides
257
+ * with a `tags` field an object schema declares itself — e.g. a `TagIndex` record on a feed host,
258
+ * whose `tags` is `Record<tagId, objectId[]>`, not an array. Extending such a schema with a `tags`
259
+ * array threw "overlapping types at path: [\"tags\"]"; the disjoint `_tags` key avoids it. The
260
+ * human-facing label is restored via the `title` annotation on {@link withMetaTags}.
261
+ */
262
+ var META_TAGS_KEY = "_tags";
263
+ /**
264
+ * Splices the synthetic meta-tags array (an object's `meta.tags`, surfaced as {@link Tag} refs) onto
265
+ * a schema for editing and drops `id`. Keyed by {@link META_TAGS_KEY} so it never collides with a
266
+ * `tags` field the schema declares itself.
267
+ */
268
+ var withMetaTags = (schema) => omitId(Schema.Struct({ [META_TAGS_KEY]: Schema.Array(Ref.Ref(Tag.Tag)).pipe(Schema.annotations({ title: "Tags" }), Schema.optional) }).pipe(Schema.extend(schema)));
269
+ //#endregion
270
+ //#region src/hooks/useAsyncFieldEffect.ts
271
+ /** Picks the declared dependency fields from the form values — the subset a dynamic field subscribes to. */
272
+ var pickValues = (values, deps) => {
273
+ const subset = {};
274
+ for (const dep of deps) subset[dep] = values?.[dep];
275
+ return subset;
276
+ };
277
+ /**
278
+ * Runs a self-contained Effect (`R = never`) whenever `key` changes, debounced and abortable. Used by
279
+ * dynamic form fields to load select options, derive values, or validate against an external source from
280
+ * the current form values. The latest `factory` is always used, but a run is only (re)triggered when
281
+ * `key` changes — derive `key` from the inputs the factory actually reads. Re-running interrupts the
282
+ * prior run (cancelling any in-flight request).
283
+ */
284
+ var useAsyncFieldEffect = (factory, key, { debounceMs = 400, enabled = true } = {}) => {
285
+ const factoryRef = useRef(factory);
286
+ factoryRef.current = factory;
287
+ const [state, setState] = useState({
288
+ loading: false,
289
+ error: void 0,
290
+ data: void 0
291
+ });
292
+ useEffect(() => {
293
+ if (!enabled) {
294
+ setState({
295
+ loading: false,
296
+ error: void 0,
297
+ data: void 0
298
+ });
299
+ return;
300
+ }
301
+ let cancel;
302
+ const timer = setTimeout(() => {
303
+ setState((prev) => ({
304
+ ...prev,
305
+ loading: true,
306
+ error: void 0
307
+ }));
308
+ cancel = Effect.runCallback(factoryRef.current(), { onExit: (exit) => {
309
+ Exit.match(exit, {
310
+ onSuccess: (data) => setState({
311
+ loading: false,
312
+ error: void 0,
313
+ data
314
+ }),
315
+ onFailure: (cause) => {
316
+ if (!Cause.isInterruptedOnly(cause)) setState((prev) => ({
317
+ loading: false,
318
+ error: Cause.squash(cause),
319
+ data: prev.data
320
+ }));
321
+ }
322
+ });
323
+ } });
324
+ }, debounceMs);
325
+ return () => {
326
+ clearTimeout(timer);
327
+ cancel?.();
328
+ };
329
+ }, [
330
+ key,
331
+ enabled,
332
+ debounceMs
333
+ ]);
334
+ return state;
335
+ };
336
+ //#endregion
337
+ //#region src/hooks/useFormContext.ts
338
+ var [FormContextProvider, useFormContext] = createContext("Form");
339
+ /**
340
+ * Get the current form values.
341
+ */
342
+ var useFormValues = (componentName, path, defaultValue) => {
343
+ const stablePath = useMemo(() => path ?? [], [path ? path.join(".") : void 0]);
344
+ const jsonPath = SchemaEx.createJsonPath(stablePath);
345
+ const { form: { values, onValueChange } } = useFormContext(componentName);
346
+ const value = SchemaEx.getValue(values, jsonPath);
347
+ const lastAppliedPathRef = useRef(null);
348
+ useEffect(() => {
349
+ if (value == null && defaultValue && lastAppliedPathRef.current !== jsonPath) {
350
+ lastAppliedPathRef.current = jsonPath;
351
+ onValueChange(stablePath, SchemaAST.stringKeyword, defaultValue());
352
+ }
353
+ }, [
354
+ value,
355
+ defaultValue,
356
+ onValueChange,
357
+ stablePath,
358
+ jsonPath
359
+ ]);
360
+ return value;
361
+ };
362
+ /**
363
+ * Get the state props for the given field.
364
+ */
365
+ var useFormFieldState = (componentName, path = []) => {
366
+ const stablePath = useMemo(() => path, [Array.isArray(path) ? path.join(".") : path]);
367
+ const { form: { getStatus, getValue, onBlur, onValueChange } } = useFormContext(componentName);
368
+ return useMemo(() => ({
369
+ getStatus: () => getStatus(stablePath),
370
+ getValue: () => getValue(stablePath),
371
+ onBlur: () => onBlur(stablePath),
372
+ onValueChange: (ast, value) => onValueChange(stablePath, ast, value)
373
+ }), [
374
+ getStatus,
375
+ getValue,
376
+ onBlur,
377
+ onValueChange,
378
+ stablePath
379
+ ]);
380
+ };
381
+ //#endregion
382
+ //#region src/hooks/useFormHandler.ts
383
+ var __dxlog_file$6 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/hooks/useFormHandler.ts";
384
+ /**
385
+ * Creates a hook for managing form state, including values, validation, and submission.
386
+ * Deeply integrated with `@dxos/schema` for schema-based validation.
387
+ */
388
+ var useFormHandler = ({ schema, autoSave, values: valuesProp, defaultValues: defaultValuesProp, onValuesChanged, onValidate, onSave, onCancel, ...props }) => {
389
+ const [changed, setChanged] = useState({});
390
+ const [touched, setTouched] = useState({});
391
+ const [errors, setErrors] = useState({});
392
+ const [saving, setSaving] = useState(false);
393
+ const defaultValues = useDefaultValue(defaultValuesProp, () => ({}));
394
+ const source = valuesProp ?? defaultValues;
395
+ const [overrides, setOverrides] = useState({});
396
+ const values = applyOverrides(source, overrides);
397
+ const validate = useCallback((values) => {
398
+ if (!schema) return false;
399
+ let errors = validateSchema(schema, values) ?? [];
400
+ log("validate", {
401
+ values,
402
+ errors
403
+ }, {
404
+ "~LogMeta": "~LogMeta",
405
+ F: __dxlog_file$6,
406
+ L: 160,
407
+ S: void 0
408
+ });
409
+ if (errors.length === 0 && onValidate) errors = onValidate(values) ?? [];
410
+ setErrors(flatMap(errors));
411
+ return errors.length === 0;
412
+ }, [schema, onValidate]);
413
+ const [schemaChanged, setSchemaChanged] = useState(false);
414
+ useEffect(() => setSchemaChanged(true), [schema]);
415
+ useEffect(() => {
416
+ if (schemaChanged) {
417
+ validate(values);
418
+ setSchemaChanged(false);
419
+ }
420
+ }, [
421
+ validate,
422
+ values,
423
+ schemaChanged
424
+ ]);
425
+ useEffect(() => {
426
+ const reconciled = Object.keys(overrides).filter((path) => valuesEqual(SchemaEx.getValue(source, path), overrides[path]));
427
+ if (reconciled.length === 0) return;
428
+ setOverrides((prev) => omitPaths(prev, reconciled));
429
+ setChanged((prev) => omitPaths(prev, reconciled));
430
+ setTouched((prev) => omitPaths(prev, reconciled));
431
+ });
432
+ const isValid = useMemo(() => {
433
+ return Object.keys(errors).length === 0;
434
+ }, [errors]);
435
+ /**
436
+ * NOTE: We can submit if there is no touched field that has an error.
437
+ * If there's a validation message visible in the form, submit should be disabled.
438
+ */
439
+ const canSave = useMemo(() => !saving && !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
440
+ touched,
441
+ errors,
442
+ saving
443
+ ]);
444
+ const handleSave = useCallback(async () => {
445
+ if (validate(values)) {
446
+ setSaving(true);
447
+ try {
448
+ await onSave?.(values, {
449
+ changed,
450
+ isValid
451
+ });
452
+ } finally {
453
+ setSaving(false);
454
+ }
455
+ }
456
+ }, [
457
+ values,
458
+ validate,
459
+ onSave,
460
+ changed,
461
+ isValid
462
+ ]);
463
+ const handleCancel = useCallback(() => {
464
+ onCancel?.();
465
+ }, [onCancel]);
466
+ const getStatus = useCallback((path) => {
467
+ const jsonPath = Array.isArray(path) ? SchemaEx.createJsonPath(path) : path;
468
+ const [_, error] = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`)) ?? [];
469
+ if (!touched[jsonPath]) return {
470
+ status: void 0,
471
+ error: void 0
472
+ };
473
+ return {
474
+ status: error ? "error" : void 0,
475
+ error: error ? error ?? void 0 : void 0
476
+ };
477
+ }, [errors, touched]);
478
+ const getValue = useCallback((path) => {
479
+ const jsonPath = SchemaEx.createJsonPath(path);
480
+ if (Object.prototype.hasOwnProperty.call(overrides, jsonPath)) return overrides[jsonPath];
481
+ return SchemaEx.getValue(source, jsonPath);
482
+ }, [source, overrides]);
483
+ const onValueChange = useCallback((path, type, value) => {
484
+ log("onValueChange", {
485
+ path,
486
+ value
487
+ }, {
488
+ "~LogMeta": "~LogMeta",
489
+ F: __dxlog_file$6,
490
+ L: 280,
491
+ S: void 0
492
+ });
493
+ const jsonPath = SchemaEx.createJsonPath(path);
494
+ let parsedValue = value;
495
+ try {
496
+ if (type._tag === "NumberKeyword") parsedValue = parseFloat(value) || 0;
497
+ } catch (err) {
498
+ log.catch(err, void 0, {
499
+ "~LogMeta": "~LogMeta",
500
+ F: __dxlog_file$6,
501
+ L: 290,
502
+ S: void 0
503
+ });
504
+ parsedValue = void 0;
505
+ }
506
+ const newOverrides = {
507
+ ...overrides,
508
+ [jsonPath]: parsedValue
509
+ };
510
+ setOverrides(newOverrides);
511
+ const newChanged = { [jsonPath]: true };
512
+ setChanged((prev) => ({
513
+ ...prev,
514
+ ...newChanged
515
+ }));
516
+ const newValues = applyOverrides(source, newOverrides);
517
+ const isValid = validate(newValues);
518
+ onValuesChanged?.(newValues, {
519
+ isValid,
520
+ changed: newChanged
521
+ });
522
+ }, [
523
+ source,
524
+ overrides,
525
+ validate,
526
+ onValuesChanged
527
+ ]);
528
+ const onBlur = useCallback(async (path) => {
529
+ const jsonPath = SchemaEx.createJsonPath(path);
530
+ setTouched((touched) => ({
531
+ ...touched,
532
+ [jsonPath]: true
533
+ }));
534
+ const isValid = validate(values);
535
+ if (Object.keys(changed).length > 0 && isValid && autoSave) await onSave?.(values, {
536
+ changed,
537
+ isValid
538
+ });
539
+ }, [
540
+ validate,
541
+ values,
542
+ changed,
543
+ autoSave,
544
+ onSave
545
+ ]);
546
+ return useMemo(() => ({
547
+ schema,
548
+ values,
549
+ errors,
550
+ touched,
551
+ changed,
552
+ isValid,
553
+ canSave,
554
+ autoSave,
555
+ onSave: handleSave,
556
+ onCancel: handleCancel,
557
+ getStatus,
558
+ getValue,
559
+ onBlur,
560
+ onValueChange,
561
+ ...props
562
+ }), [
563
+ schema,
564
+ autoSave,
565
+ values,
566
+ errors,
567
+ touched,
568
+ changed,
569
+ canSave,
570
+ isValid,
571
+ getStatus,
572
+ getValue,
573
+ onBlur,
574
+ onValueChange,
575
+ handleSave,
576
+ handleCancel
577
+ ]);
578
+ };
579
+ /**
580
+ * Creates a new object with value at path. Does not mutate source.
581
+ * Preserves arrays when path segments are numeric indices.
582
+ */
583
+ var mergeAtPath = (obj, path, value) => {
584
+ if (path.length === 0) return value;
585
+ const [head, ...rest] = path;
586
+ const child = rest.length === 0 ? value : mergeAtPath(obj?.[head], rest, value);
587
+ if (Array.isArray(obj)) {
588
+ const copy = [...obj];
589
+ copy[head] = child;
590
+ return copy;
591
+ }
592
+ return {
593
+ ...obj,
594
+ [head]: child
595
+ };
596
+ };
597
+ /** Overlays sparse json-path edits onto a base value without mutating it; un-edited paths keep the base reference. */
598
+ var applyOverrides = (base, overrides) => {
599
+ let result = base;
600
+ for (const path of Object.keys(overrides)) result = mergeAtPath(result, SchemaEx.splitJsonPath(path), overrides[path]);
601
+ return result;
602
+ };
603
+ var valuesEqual = (left, right) => {
604
+ if (left === right) return true;
605
+ if (left === null || right === null) return left === right;
606
+ if (typeof left !== "object" || typeof right !== "object") return Utils.structuralRegion(() => Equal.equals(left, right));
607
+ if (Ref.isRef(left) && Ref.isRef(right)) return left.uri === right.uri;
608
+ if (Ref.isRef(left) || Ref.isRef(right)) return false;
609
+ if (Array.isArray(left) && Array.isArray(right)) {
610
+ if (left.length !== right.length) return false;
611
+ return left.every((value, index) => valuesEqual(value, right[index]));
612
+ }
613
+ if (Array.isArray(left) || Array.isArray(right)) return false;
614
+ const leftRecord = left;
615
+ const rightRecord = right;
616
+ const keys = /* @__PURE__ */ new Set([...Object.keys(leftRecord).filter((key) => key !== "id"), ...Object.keys(rightRecord).filter((key) => key !== "id")]);
617
+ for (const key of keys) if (!valuesEqual(leftRecord[key], rightRecord[key])) return false;
618
+ return true;
619
+ };
620
+ /** Returns a copy of `record` without the given json-path keys. */
621
+ var omitPaths = (record, paths) => {
622
+ const omit = new Set(paths);
623
+ return Object.fromEntries(Object.entries(record).filter(([key]) => !omit.has(key)));
624
+ };
625
+ var flatMap = (errors) => {
626
+ return errors.reduce((result, { path, message }) => {
627
+ const jsonPath = SchemaEx.fromEffectValidationPath(path);
628
+ if (!(jsonPath in result)) result[jsonPath] = message;
629
+ return result;
630
+ }, {});
631
+ };
632
+ //#endregion
633
+ //#region src/hooks/useKeyHandler.tsx
634
+ /**
635
+ * Key handler.
636
+ */
637
+ var useKeyHandler = (elRef, form) => {
638
+ const handleKeyDown = useCallback((event) => {
639
+ switch (event.key) {
640
+ case "Enter":
641
+ if (event.metaKey && form.canSave) form.onSave();
642
+ break;
643
+ }
644
+ }, [
645
+ form.isValid,
646
+ form.canSave,
647
+ form.onSave,
648
+ form.autoSave
649
+ ]);
650
+ useLayoutEffect(() => {
651
+ const el = elRef.current;
652
+ if (!el) return;
653
+ return addEventListener(el, "keydown", handleKeyDown);
654
+ }, [elRef, handleKeyDown]);
655
+ };
656
+ /** {@link Form} theme template: a tailwind-variants recipe (`styles`) plus non-class `behavior`. */
657
+ var formTheme = {
658
+ styles: tv({
659
+ slots: {
660
+ viewport: "",
661
+ content: "pb-form-padding",
662
+ section: "flex flex-col py-form-section-gap first:pt-0",
663
+ group: "flex flex-col gap-trim-md p-trim-md border border-separator rounded-sm",
664
+ sectionHeader: "",
665
+ sectionTitle: "text-lg",
666
+ sectionDescription: "text-description",
667
+ fieldSet: "",
668
+ field: "",
669
+ fieldLabel: "min-h-(--dx-control) grid grid-cols-[1fr_auto_auto_auto] items-center select-none",
670
+ fieldLabelText: "",
671
+ fieldDescription: "text-description",
672
+ fieldControl: "",
673
+ fieldValidation: "",
674
+ actions: "grid grid-flow-col gap-form-gap auto-cols-fr py-form-padding",
675
+ submit: "flex w-full pt-form-padding",
676
+ fieldSetBody: "flex flex-col px-trim-sm pb-trim-sm",
677
+ fieldSetBox: "border border-subdued-separator rounded-sm",
678
+ fieldSetBoxOuter: ""
679
+ },
680
+ variants: { variant: {
681
+ default: {},
682
+ settings: {
683
+ content: "dx-document",
684
+ section: "py-form-section-gap! gap-trim-md",
685
+ sectionHeader: "pb-form-section-gap",
686
+ sectionTitle: "px-trim-md text-xl",
687
+ sectionDescription: "px-trim-md",
688
+ fieldSet: "flex flex-col gap-trim-md",
689
+ fieldSetBody: "flex flex-col gap-trim-md px-trim-sm pb-trim-sm",
690
+ field: mx("grid", "grid-cols-1 [grid-template-areas:\"header\"\"description\"\"control\"\"validation\"]", "md:grid-cols-2 md:[grid-template-areas:\"header_header\"\"description_control\"\"validation_validation\"]", "gap-x-trim-xl gap-y-0 p-trim-md border border-input-separator rounded-md"),
691
+ fieldLabel: "[grid-area:header]",
692
+ fieldLabelText: "text-base-fg text-lg",
693
+ fieldDescription: "[grid-area:description] pt-trim-xs text-description",
694
+ fieldControl: "[grid-area:control] flex justify-end items-start pt-trim-md md:pt-0",
695
+ fieldValidation: "[grid-area:validation]"
696
+ }
697
+ } },
698
+ defaultVariants: { variant: "default" }
699
+ }),
700
+ behavior: {
701
+ default: { showDescription: false },
702
+ settings: { showDescription: true }
703
+ }
704
+ };
705
+ Object.keys(formTheme.styles());
706
+ //#endregion
707
+ //#region src/components/Form/FormField/fields/ArrayField/default-value.ts
708
+ /**
709
+ * Returns the default empty value for a given AST.
710
+ * Used for initializing new array values etc.
711
+ */
712
+ var getDefaultValue = (ast) => {
713
+ switch (ast?._tag) {
714
+ case "StringKeyword": return "";
715
+ case "NumberKeyword": return 0;
716
+ case "BooleanKeyword": return false;
717
+ case "Suspend": return getDefaultValue(ast.f());
718
+ case "Refinement": {
719
+ const jsonSchema = Option.getOrUndefined(SchemaAST.getJSONSchemaAnnotation(ast));
720
+ if (jsonSchema != null && "minimum" in jsonSchema && typeof jsonSchema.minimum === "number") return jsonSchema.minimum;
721
+ return getDefaultValue(ast.from);
722
+ }
723
+ default: if (ast && SchemaEx.isNestedType(ast)) return {};
724
+ else throw new Error(`Unsupported type: ${ast?._tag}`);
725
+ }
726
+ };
727
+ //#endregion
728
+ //#region src/components/Form/FormField/FormField.tsx
729
+ var FormField = (props) => {
730
+ const { type, name, label: labelProp, path, required, projection, fieldMap, fieldProvider, readonly, hideEmpty = true, layout, createTypename, createOptionLabel, createOptionIcon, createInitialValuePath, createFieldMap, db, useType, getOptions, onCreate, resolveCreateEntry, refInline } = props;
731
+ const { t } = useTranslation(translationKey);
732
+ const title = SchemaEx.getAnnotation(SchemaAST.TitleAnnotationId)(type);
733
+ const description = SchemaEx.getAnnotation(SchemaAST.DescriptionAnnotationId)(type);
734
+ const examples = SchemaEx.getAnnotation(SchemaAST.ExamplesAnnotationId)(type);
735
+ const label = useMemo(() => labelProp ?? title ?? (name == null ? "" : String$1.capitalize(name)), [
736
+ labelProp,
737
+ title,
738
+ name
739
+ ]);
740
+ const placeholder = useMemo(() => examples?.length ? `${t("example.placeholder")}: ${examples[0]}` : description ?? label, [
741
+ examples,
742
+ description,
743
+ label,
744
+ t
745
+ ]);
746
+ const providerSchema = useMemo(() => fieldProvider ? Schema.make(type) : void 0, [fieldProvider, type]);
747
+ const fieldState = useFormFieldState(FormField.displayName, path);
748
+ const jsonPath = SchemaEx.createJsonPath(path ?? []);
749
+ const fieldProps = {
750
+ type,
751
+ format: Format.FormatAnnotation.getFromAst(type).pipe((annotation) => Option.getOrUndefined(annotation)),
752
+ readonly,
753
+ label,
754
+ description,
755
+ jsonPath,
756
+ placeholder,
757
+ presentation: layout,
758
+ required,
759
+ db,
760
+ ...fieldState
761
+ };
762
+ if (readonly && hideEmpty && fieldState.getValue() == null) return null;
763
+ const CustomField = fieldMap?.[jsonPath];
764
+ if (CustomField) return /* @__PURE__ */ jsx(CustomField, { ...fieldProps });
765
+ if (fieldProvider && providerSchema) {
766
+ const component = fieldProvider({
767
+ schema: providerSchema,
768
+ prop: name ?? "",
769
+ fieldProps
770
+ });
771
+ if (component) return component;
772
+ }
773
+ const optionsLookup = Option.getOrUndefined(OptionsLookupAnnotation.getFromAst(type));
774
+ if (optionsLookup) return optionsLookup.combobox ? /* @__PURE__ */ jsx(ComboboxField, {
775
+ ...fieldProps,
776
+ lookup: optionsLookup
777
+ }) : /* @__PURE__ */ jsx(AsyncSelectField, {
778
+ ...fieldProps,
779
+ lookup: optionsLookup
780
+ });
781
+ const autofill = Option.getOrUndefined(AutofillAnnotation.getFromAst(type));
782
+ if (autofill) return /* @__PURE__ */ jsx(AutofillField, {
783
+ ...fieldProps,
784
+ autofill
785
+ });
786
+ if (SchemaEx.isArrayType(type)) return /* @__PURE__ */ jsx(ArrayField, {
787
+ fieldProps: fieldState,
788
+ label,
789
+ ...props
790
+ });
791
+ const Field = getFormField(fieldProps);
792
+ if (Field) return /* @__PURE__ */ jsx(Field, { ...fieldProps });
793
+ const options = getSelectOptions(type);
794
+ if (options) {
795
+ const selectOptions = ((projection?.getFieldProjections())?.find((fp) => fp.field.path === name))?.props.options;
796
+ return /* @__PURE__ */ jsx(SelectField, {
797
+ ...fieldProps,
798
+ options: options.map((option) => {
799
+ return {
800
+ value: option,
801
+ label: (selectOptions?.find((so) => so.id === globalThis.String(option)))?.title ?? option.toString()
802
+ };
803
+ })
804
+ });
805
+ }
806
+ const refProps = getRefProps(type);
807
+ if (refProps) {
808
+ if ((refInline || Annotation.FormInlineAnnotation.getFromAst(refProps.ast).pipe(Option.getOrElse(() => false))) && !refProps.isArray) return /* @__PURE__ */ jsx(InlineRefField, {
809
+ ...fieldProps,
810
+ ...refProps,
811
+ db,
812
+ useType,
813
+ onCreate,
814
+ resolveCreateEntry
815
+ });
816
+ const isCreateTarget = !createTypename || refProps.typename === createTypename;
817
+ return /* @__PURE__ */ jsx(RefField, {
818
+ ...fieldProps,
819
+ ...refProps,
820
+ createOptionLabel: isCreateTarget ? createOptionLabel : void 0,
821
+ createOptionIcon: isCreateTarget ? createOptionIcon : void 0,
822
+ createInitialValuePath: isCreateTarget ? createInitialValuePath : void 0,
823
+ createFieldMap: isCreateTarget ? createFieldMap : void 0,
824
+ db,
825
+ useType,
826
+ getOptions,
827
+ onCreate,
828
+ resolveCreateEntry
829
+ });
830
+ }
831
+ if (SchemaEx.isNestedType(type)) {
832
+ const baseNode = SchemaEx.findNode(type, SchemaEx.isDiscriminatedUnion);
833
+ const typeLiteral = baseNode ? SchemaEx.getDiscriminatedType(baseNode, fieldState.getValue()) : SchemaEx.findNode(type, SchemaAST.isTypeLiteral);
834
+ if (typeLiteral) return /* @__PURE__ */ jsx(FormFieldSet, {
835
+ schema: Schema.make(typeLiteral),
836
+ path,
837
+ readonly,
838
+ layout,
839
+ label,
840
+ collapsible: true,
841
+ projection,
842
+ fieldMap,
843
+ fieldProvider,
844
+ createOptionLabel,
845
+ createOptionIcon,
846
+ createInitialValuePath,
847
+ db,
848
+ useType,
849
+ getOptions,
850
+ onCreate,
851
+ resolveCreateEntry
852
+ });
853
+ }
854
+ return null;
855
+ };
856
+ FormField.displayName = "Form.FormField";
857
+ var CompactIconButton = ({ classNames, ...props }) => {
858
+ return /* @__PURE__ */ jsx("span", {
859
+ className: "grid size-8 shrink-0 place-items-center",
860
+ children: /* @__PURE__ */ jsx(IconButton, {
861
+ variant: "ghost",
862
+ iconOnly: true,
863
+ ...props,
864
+ classNames: mx("min-h-0 h-7 w-7 p-0", classNames)
865
+ })
866
+ });
867
+ };
868
+ /**
869
+ * Get property input component.
870
+ */
871
+ var getFormField = ({ type, format }) => {
872
+ if (SchemaAST.isRefinement(type)) return getFormField({
873
+ type: type.from,
874
+ format
875
+ });
876
+ const formatField = Match.value(format).pipe(Match.withReturnType(), Match.when(Format.TypeFormat.Date, () => DateField), Match.when(Format.TypeFormat.DateTime, () => DateField), Match.when(Format.TypeFormat.GeoPoint, () => GeoPointField), Match.when(Format.TypeFormat.Markdown, () => MarkdownField), Match.when(Format.TypeFormat.Password, () => PasswordField), Match.when(Format.TypeFormat.Text, () => TextAreaField), Match.when(Format.TypeFormat.Time, () => DateField), Match.orElse(() => void 0));
877
+ if (formatField) return formatField;
878
+ switch (type._tag) {
879
+ case "AnyKeyword":
880
+ case "StringKeyword": return TextField;
881
+ case "NumberKeyword": return NumberField;
882
+ case "BooleanKeyword": return BooleanField;
883
+ }
884
+ };
885
+ var getSelectOptions = (ast) => {
886
+ if (SchemaEx.isLiteralUnion(ast)) return ast.types.map((type) => type.literal).filter((v) => v !== null);
887
+ return Format.OptionsAnnotation.getFromAst(ast).pipe((annotation) => Option.getOrUndefined(annotation));
888
+ };
889
+ //#endregion
890
+ //#region src/components/Form/FormField/presentation.tsx
891
+ /**
892
+ * Derive the {@link FormFieldPresentation} for a layout.
893
+ * - `static` renders read-only plain DOM.
894
+ * - `inline` drops the label (the control stands alone, e.g. an array row).
895
+ * - `full` adds the inline error block.
896
+ */
897
+ var presentationFor = (layout) => ({
898
+ layout,
899
+ isStatic: layout === "static",
900
+ showLabel: layout !== "inline",
901
+ showError: layout === "full"
902
+ });
903
+ //#endregion
904
+ //#region src/components/Form/FormField/FormRow.tsx
905
+ var FormFieldLabel = ({ classNames, labelClassName, variant = "default", label, error, readonly, required, standalone, button, labelEnd, onClick }) => {
906
+ const styles = formTheme.styles({ variant });
907
+ const labelClassNames = mx("text-sm text-description", required && "after:content-['*'] after:ms-0.5 after:text-warning-text", styles.fieldLabelText({ class: labelClassName }));
908
+ const labelNode = readonly || standalone ? /* @__PURE__ */ jsx("span", {
909
+ className: labelClassNames,
910
+ children: label
911
+ }) : /* @__PURE__ */ jsx(Input.Label, {
912
+ classNames: labelClassNames,
913
+ children: label
914
+ });
915
+ return /* @__PURE__ */ jsxs("div", {
916
+ className: styles.fieldLabel({ class: mx(onClick && "cursor-pointer", classNames) }),
917
+ onClick,
918
+ children: [
919
+ labelNode,
920
+ labelEnd,
921
+ error ? /* @__PURE__ */ jsx(Tooltip.Trigger, {
922
+ asChild: true,
923
+ content: error,
924
+ side: "bottom",
925
+ children: /* @__PURE__ */ jsx(Icon, {
926
+ icon: "ph--warning--regular",
927
+ size: 4,
928
+ classNames: "text-error-text"
929
+ })
930
+ }) : /* @__PURE__ */ jsx("span", {}),
931
+ button
932
+ ]
933
+ });
934
+ };
935
+ FormFieldLabel.displayName = "Form.FieldLabel";
936
+ /**
937
+ * Formats a value for `static` (read-only, plain-DOM) presentation based on its
938
+ * type `format`. Dates/times are rendered human-readable; everything else falls
939
+ * back to `String(value)`.
940
+ */
941
+ var formatStaticValue = (value, format$1) => {
942
+ if (value == null) return "";
943
+ switch (format$1) {
944
+ case Format.TypeFormat.DateTime:
945
+ case Format.TypeFormat.Date:
946
+ case Format.TypeFormat.Time: {
947
+ const date = new Date(value);
948
+ if (Number.isNaN(date.getTime())) return String(value);
949
+ return format(date, format$1 === Format.TypeFormat.DateTime ? "PPp" : format$1 === Format.TypeFormat.Date ? "PP" : "p");
950
+ }
951
+ default: return String(value);
952
+ }
953
+ };
954
+ var FORM_ROW_NAME = "Form.Row";
955
+ /**
956
+ * A labeled card row — the single shell behind both schema fields and free-form "action" rows. Field
957
+ * renderers pass a render-prop `children` (field mode: bound to the form value, with validation and
958
+ * static rendering); consumers pass plain `children` for an arbitrary control (action mode), the
959
+ * affordance for an arbitrary labeled control (action rows, settings cards).
960
+ */
961
+ var FormRow = ({ classNames, children, readonly, presentation, label, description, jsonPath, format, required, standalone, labelEnd, renderStatic, validation, getStatus, getValue, rootRef }) => {
962
+ const { variant = "default" } = useFormContext(FORM_ROW_NAME);
963
+ const styles = formTheme.styles({ variant });
964
+ const { showDescription } = formTheme.behavior[variant];
965
+ const resolved = presentationFor(presentation);
966
+ if (typeof children === "function" && getStatus && getValue) {
967
+ const { status, error } = getStatus();
968
+ const value = getValue();
969
+ if (resolved.isStatic && value == null) return null;
970
+ const control = resolved.isStatic ? renderStatic?.(value) ?? /* @__PURE__ */ jsx("p", {
971
+ className: "truncate min-w-0",
972
+ children: formatStaticValue(value, format)
973
+ }) : children({
974
+ value,
975
+ presentation: resolved
976
+ });
977
+ return /* @__PURE__ */ jsx(Input.Root, {
978
+ validationValence: status,
979
+ children: /* @__PURE__ */ jsxs("div", {
980
+ className: styles.field({ class: classNames }),
981
+ ref: rootRef,
982
+ children: [
983
+ resolved.showLabel && /* @__PURE__ */ jsx(FormFieldLabel, {
984
+ variant,
985
+ error,
986
+ readonly,
987
+ required,
988
+ standalone,
989
+ labelEnd,
990
+ label,
991
+ path: jsonPath
992
+ }),
993
+ showDescription && description && /* @__PURE__ */ jsx(Input.Description, {
994
+ classNames: styles.fieldDescription(),
995
+ children: description
996
+ }),
997
+ /* @__PURE__ */ jsx("div", {
998
+ className: styles.fieldControl(),
999
+ children: control
1000
+ }),
1001
+ resolved.showError && error && /* @__PURE__ */ jsx("div", {
1002
+ className: styles.fieldValidation(),
1003
+ children: /* @__PURE__ */ jsx(Input.DescriptionAndValidation, { children: /* @__PURE__ */ jsx(Input.Validation, { children: error }) })
1004
+ })
1005
+ ]
1006
+ })
1007
+ });
1008
+ }
1009
+ return /* @__PURE__ */ jsxs("div", {
1010
+ ref: rootRef,
1011
+ className: styles.field({ class: classNames }),
1012
+ children: [
1013
+ resolved.showLabel && label && /* @__PURE__ */ jsx(FormFieldLabel, {
1014
+ variant,
1015
+ readonly,
1016
+ required,
1017
+ standalone: true,
1018
+ labelEnd,
1019
+ label,
1020
+ path: jsonPath
1021
+ }),
1022
+ showDescription && description && /* @__PURE__ */ jsx("p", {
1023
+ className: styles.fieldDescription(),
1024
+ children: description
1025
+ }),
1026
+ /* @__PURE__ */ jsx("div", {
1027
+ className: styles.fieldControl(),
1028
+ children: typeof children === "function" ? null : children
1029
+ }),
1030
+ validation && /* @__PURE__ */ jsx("div", {
1031
+ className: styles.fieldValidation(),
1032
+ children: validation
1033
+ })
1034
+ ]
1035
+ });
1036
+ };
1037
+ FormRow.displayName = FORM_ROW_NAME;
1038
+ var FormFieldErrorBoundary = class extends Component {
1039
+ static getDerivedStateFromError(error) {
1040
+ return { error };
1041
+ }
1042
+ state = { error: void 0 };
1043
+ componentDidUpdate(prevProps) {
1044
+ if (prevProps.path !== this.props.path) this.resetError();
1045
+ }
1046
+ render() {
1047
+ if (this.state.error) return /* @__PURE__ */ jsxs("div", {
1048
+ className: "flex gap-2 border border-error-border font-mono text-sm",
1049
+ children: [/* @__PURE__ */ jsx("span", {
1050
+ className: "bg-error-bg text-base-fg px-1 font-thin",
1051
+ children: "ERROR"
1052
+ }), String(this.props.path?.join("."))]
1053
+ });
1054
+ return this.props.children;
1055
+ }
1056
+ resetError() {
1057
+ this.setState({ error: void 0 });
1058
+ }
1059
+ };
1060
+ //#endregion
1061
+ //#region src/components/Form/FormField/FormFieldHeader.tsx
1062
+ /**
1063
+ * Header row for a labelled group or list: a label with optional trailing controls (an inline add
1064
+ * affordance and/or arbitrary `actions`). Shared by array/list fields ({@link ArrayField}, the view
1065
+ * editor) and {@link FormFieldSetContainer}'s collapse header, so all group/list headers render identically.
1066
+ */
1067
+ var FormFieldHeader = ({ label, path, required, readonly, classNames, add, actions, onClick }) => /* @__PURE__ */ jsx(FormFieldLabel, {
1068
+ standalone: true,
1069
+ classNames,
1070
+ label,
1071
+ required,
1072
+ readonly,
1073
+ path,
1074
+ button: !readonly && add || actions ? /* @__PURE__ */ jsxs(Fragment$1, { children: [!readonly && add && /* @__PURE__ */ jsx(CompactIconButton, {
1075
+ disabled: add.disabled,
1076
+ icon: add.icon ?? "ph--plus--regular",
1077
+ label: add.label,
1078
+ onClick: (event) => {
1079
+ event.stopPropagation();
1080
+ add.onClick();
1081
+ }
1082
+ }), actions] }) : void 0,
1083
+ onClick
1084
+ });
1085
+ //#endregion
1086
+ //#region src/components/Form/FormField/fields/ArrayField/ArrayField.tsx
1087
+ var __dxlog_file$5 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx";
1088
+ var DND_ID = "__dndId";
1089
+ var isOrderedItem = (item) => !!item && typeof item === "object" && typeof item[DND_ID] === "string";
1090
+ var getOrderedId = (item) => item[DND_ID];
1091
+ var ArrayField = ({ type, path, label, readonly, layout, fieldProps: inputProps, ...props }) => {
1092
+ const { t } = useTranslation(translationKey);
1093
+ const elementType = SchemaEx.getArrayElementType(type);
1094
+ const { onValueChange } = inputProps;
1095
+ const values = useFormValues(ArrayField.displayName, path, () => []);
1096
+ const ordered = Annotation.FormOrderedAnnotation.getFromAst(type).pipe(Option.getOrElse(() => false));
1097
+ const { db, onCreate, getCreateDefaults, useType: useType$4 = useType } = props;
1098
+ const createTypename = Annotation.FormCreateAnnotation.getFromAst(type).pipe(Option.getOrUndefined);
1099
+ const createSchema = useType$4(db, createTypename ? DXN$1.make(createTypename) : void 0);
1100
+ const createInline = !!createTypename && !!elementType && Ref.isRefType(elementType) && !readonly && layout !== "static";
1101
+ const idsRef = useRef([]);
1102
+ const counterRef = useRef(0);
1103
+ const nextId = useCallback(() => `${DND_ID}-${counterRef.current++}`, []);
1104
+ const handleAddCreate = useCallback(async () => {
1105
+ if (!createSchema || !onCreate) return;
1106
+ const defaults = getCreateDefaults?.({
1107
+ jsonPath: SchemaEx.createJsonPath(path ?? []),
1108
+ schema: createSchema
1109
+ }) ?? {};
1110
+ const created = await onCreate(createSchema, defaults);
1111
+ if (!created) return;
1112
+ idsRef.current.push(nextId());
1113
+ onValueChange(type, [...values ?? [], Ref.make(created)]);
1114
+ }, [
1115
+ createSchema,
1116
+ onCreate,
1117
+ getCreateDefaults,
1118
+ onValueChange,
1119
+ type,
1120
+ path,
1121
+ values,
1122
+ nextId
1123
+ ]);
1124
+ const getDefaultObjectValue = (typeNode) => {
1125
+ const baseNode = SchemaEx.findNode(typeNode, SchemaEx.isDiscriminatedUnion);
1126
+ const typeLiteral = baseNode ? SchemaEx.getDiscriminatedType(baseNode, {}) : SchemaEx.findNode(typeNode, SchemaAST.isTypeLiteral);
1127
+ if (!typeLiteral) return {};
1128
+ return Object.fromEntries(getFormProperties(typeLiteral).map((prop) => {
1129
+ const defaultValue = SchemaAST.getDefaultAnnotation(prop.type).pipe((annotation) => Option.getOrUndefined(annotation));
1130
+ return [prop.name, defaultValue];
1131
+ }));
1132
+ };
1133
+ const handleAdd = useCallback(() => {
1134
+ let defaultValue;
1135
+ try {
1136
+ defaultValue = elementType && SchemaEx.isNestedType(elementType) ? getDefaultObjectValue(elementType) : getDefaultValue(elementType);
1137
+ } catch (err) {
1138
+ log.catch(err, void 0, {
1139
+ "~LogMeta": "~LogMeta",
1140
+ F: __dxlog_file$5,
1141
+ L: 126,
1142
+ S: void 0
1143
+ });
1144
+ return;
1145
+ }
1146
+ idsRef.current.push(nextId());
1147
+ onValueChange(type, [...values ?? [], defaultValue]);
1148
+ }, [
1149
+ onValueChange,
1150
+ type,
1151
+ elementType,
1152
+ values,
1153
+ nextId
1154
+ ]);
1155
+ const handleDelete = useCallback((idx) => {
1156
+ idsRef.current.splice(idx, 1);
1157
+ onValueChange(type, values?.filter((_, i) => i !== idx));
1158
+ }, [
1159
+ onValueChange,
1160
+ type,
1161
+ values
1162
+ ]);
1163
+ const handleMove = useCallback((fromIndex, toIndex) => {
1164
+ const next = [...values ?? []];
1165
+ arrayMove(next, fromIndex, toIndex);
1166
+ arrayMove(idsRef.current, fromIndex, toIndex);
1167
+ onValueChange(type, next);
1168
+ }, [
1169
+ onValueChange,
1170
+ type,
1171
+ values
1172
+ ]);
1173
+ if (!elementType || (readonly || layout === "static") && values && values.length < 1) return null;
1174
+ const renderItemAsObject = elementType && SchemaEx.isNestedType(elementType) && !Ref.isRefType(elementType);
1175
+ const renderField = (index, isLast) => {
1176
+ const field = /* @__PURE__ */ jsx(FormField, {
1177
+ ...props,
1178
+ autoFocus: isLast,
1179
+ type: elementType,
1180
+ ...renderItemAsObject || createInline ? { name: null } : { label },
1181
+ path: [...path ?? [], index],
1182
+ readonly: readonly || layout === "static",
1183
+ layout: renderItemAsObject ? layout === "static" ? "static" : void 0 : "inline",
1184
+ refInline: createInline || void 0
1185
+ });
1186
+ return createInline ? /* @__PURE__ */ jsx("div", {
1187
+ className: "w-full min-w-0 [--dx-col:auto]",
1188
+ children: field
1189
+ }) : field;
1190
+ };
1191
+ const header = (layout !== "static" || values && values.length > 0) && /* @__PURE__ */ jsx(FormFieldHeader, {
1192
+ label,
1193
+ path: SchemaEx.createJsonPath(path ?? []),
1194
+ readonly,
1195
+ add: layout !== "static" ? {
1196
+ label: t("add-item.button"),
1197
+ onClick: createInline ? () => void handleAddCreate() : handleAdd
1198
+ } : void 0
1199
+ });
1200
+ if (ordered && !readonly && layout !== "static") {
1201
+ const ids = idsRef.current;
1202
+ const count = values?.length ?? 0;
1203
+ while (ids.length < count) ids.push(nextId());
1204
+ if (ids.length > count) ids.length = count;
1205
+ const items = (values ?? []).map((value, index) => ({
1206
+ value,
1207
+ index,
1208
+ [DND_ID]: ids[index]
1209
+ }));
1210
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [header, /* @__PURE__ */ jsx(OrderedList.Root, {
1211
+ items,
1212
+ isItem: isOrderedItem,
1213
+ getId: getOrderedId,
1214
+ onMove: handleMove,
1215
+ children: ({ items }) => /* @__PURE__ */ jsx(OrderedList.Content, { children: items.map((item) => /* @__PURE__ */ jsxs(OrderedList.Item, {
1216
+ id: getOrderedId(item),
1217
+ item,
1218
+ classNames: "grid grid-cols-[min-content_1fr_min-content] items-start gap-1 p-0 pb-1",
1219
+ children: [
1220
+ /* @__PURE__ */ jsx(OrderedList.DragHandle, {}),
1221
+ renderField(item.index, item.index === items.length - 1),
1222
+ /* @__PURE__ */ jsx(CompactIconButton, {
1223
+ icon: "ph--x--regular",
1224
+ label: t("remove-item.button"),
1225
+ onClick: () => handleDelete(item.index)
1226
+ })
1227
+ ]
1228
+ }, getOrderedId(item))) })
1229
+ })] });
1230
+ }
1231
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [header, /* @__PURE__ */ jsx("div", {
1232
+ className: "flex flex-col gap-2 pb-2",
1233
+ children: values?.map((_, index) => {
1234
+ const isLast = index === values.length - 1;
1235
+ return /* @__PURE__ */ jsxs("div", {
1236
+ className: mx("grid grid-cols-[minmax(0,1fr)_min-content] gap-1", renderItemAsObject || createInline ? "items-start" : "items-center"),
1237
+ children: [renderField(index, isLast), !readonly && layout !== "static" && /* @__PURE__ */ jsx(CompactIconButton, {
1238
+ icon: "ph--x--regular",
1239
+ label: t("remove-item.button"),
1240
+ onClick: () => handleDelete(index)
1241
+ })]
1242
+ }, index);
1243
+ })
1244
+ })] });
1245
+ };
1246
+ ArrayField.displayName = "Form.ArrayField";
1247
+ //#endregion
1248
+ //#region src/components/Form/FormField/fields/SelectField/create-select-field.tsx
1249
+ var __dxlog_file$4 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/Form/FormField/fields/SelectField/create-select-field.tsx";
1250
+ /**
1251
+ * Factory for a `fieldMap` entry (keyed by JSON path) that renders a Select over a fixed list of
1252
+ * options, with an optional sentinel mapping to `undefined`. Use for fields whose choices come from a
1253
+ * runtime list rather than the schema (e.g. available model ids).
1254
+ */
1255
+ var createSelectField = ({ options, defaultLabel = "Default" }) => {
1256
+ const normalized = options.map((option) => typeof option === "string" ? {
1257
+ value: option,
1258
+ label: option
1259
+ } : option);
1260
+ const hasDefault = defaultLabel !== null;
1261
+ const sentinel = "__default__";
1262
+ invariant(!normalized.some((option) => option.value === sentinel), `createSelectField: option value '${sentinel}' is reserved.`, {
1263
+ "~LogMeta": "~LogMeta",
1264
+ F: __dxlog_file$4,
1265
+ L: 39,
1266
+ S: void 0,
1267
+ A: ["!normalized.some((option) => option.value === sentinel)", "`createSelectField: option value '${sentinel}' is reserved.`"]
1268
+ });
1269
+ return ({ type, readonly, onValueChange, ...props }) => /* @__PURE__ */ jsx(FormRow, {
1270
+ readonly,
1271
+ renderStatic: (value) => /* @__PURE__ */ jsx("p", {
1272
+ className: "truncate min-w-0",
1273
+ children: normalized.find((option) => option.value === value)?.label ?? String(value ?? "")
1274
+ }),
1275
+ ...props,
1276
+ children: ({ value }) => /* @__PURE__ */ jsxs(Select.Root, {
1277
+ disabled: !!readonly,
1278
+ value: value ?? sentinel,
1279
+ onValueChange: (next) => onValueChange(type, hasDefault && next === sentinel ? void 0 : next),
1280
+ children: [/* @__PURE__ */ jsx(Select.TriggerButton, {
1281
+ classNames: "w-full",
1282
+ disabled: !!readonly
1283
+ }), normalized.length > 0 && /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsxs(Select.Content, { children: [/* @__PURE__ */ jsxs(Select.Viewport, { children: [hasDefault && /* @__PURE__ */ jsx(Select.Option, {
1284
+ value: sentinel,
1285
+ children: defaultLabel
1286
+ }), normalized.map((option) => /* @__PURE__ */ jsx(Select.Option, {
1287
+ value: option.value,
1288
+ children: option.label ?? option.value
1289
+ }, option.value))] }), /* @__PURE__ */ jsx(Select.Arrow, {})] }) })]
1290
+ })
1291
+ });
1292
+ };
1293
+ //#endregion
1294
+ //#region src/components/Form/FormField/fields/SelectField/SelectField.tsx
1295
+ var SelectField = ({ type, readonly, placeholder, options, onValueChange, ...props }) => {
1296
+ const handleValueChange = useCallback((value) => onValueChange(type, value), [type, onValueChange]);
1297
+ return /* @__PURE__ */ jsx(FormRow, {
1298
+ readonly,
1299
+ renderStatic: (value) => /* @__PURE__ */ jsx("p", {
1300
+ className: "truncate min-w-0",
1301
+ children: options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)
1302
+ }),
1303
+ ...props,
1304
+ children: ({ value }) => /* @__PURE__ */ jsxs(Select.Root, {
1305
+ value,
1306
+ onValueChange: handleValueChange,
1307
+ disabled: !!readonly,
1308
+ children: [/* @__PURE__ */ jsx(Select.TriggerButton, {
1309
+ classNames: "w-full",
1310
+ disabled: !!readonly,
1311
+ placeholder
1312
+ }), options?.length !== 0 && /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsxs(Select.Content, { children: [/* @__PURE__ */ jsx(Select.Viewport, { children: options?.map(({ value, label, secondaryLabel, icon, iconHue }) => /* @__PURE__ */ jsx(Select.Option, {
1313
+ value: String(value),
1314
+ children: /* @__PURE__ */ jsxs("span", {
1315
+ className: "flex items-center flex-row gap-2",
1316
+ children: [
1317
+ icon && /* @__PURE__ */ jsx(Icon, {
1318
+ icon,
1319
+ classNames: getIconHueStyles(iconHue)
1320
+ }),
1321
+ label ?? String(value),
1322
+ secondaryLabel && /* @__PURE__ */ jsx("span", {
1323
+ className: "text-subdued text-xs",
1324
+ children: secondaryLabel
1325
+ })
1326
+ ]
1327
+ })
1328
+ }, String(value))) }), /* @__PURE__ */ jsx(Select.Arrow, {})] }) })]
1329
+ })
1330
+ });
1331
+ };
1332
+ var getIconHueStyles = (iconHue) => {
1333
+ return (iconHue ? getStyles(iconHue) : void 0)?.fg;
1334
+ };
1335
+ //#endregion
1336
+ //#region src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.tsx
1337
+ /**
1338
+ * A select whose options are loaded dynamically (debounced) from the fields the lookup declares in its
1339
+ * `deps`, via an {@link Annotation.OptionsLookup}. Re-runs only when one of those values changes.
1340
+ * Auto-selects the sole option when exactly one resolves and nothing is chosen yet.
1341
+ */
1342
+ var AsyncSelectField = ({ lookup, ...fieldProps }) => {
1343
+ const values = useFormValues(AsyncSelectField.displayName);
1344
+ const subset = useMemo(() => pickValues(values, lookup.deps), [values, lookup.deps]);
1345
+ const { loading, data } = useAsyncFieldEffect(() => lookup.load(subset), useMemo(() => JSON.stringify(subset), [subset]));
1346
+ const options = data ?? [];
1347
+ const { type, placeholder } = fieldProps;
1348
+ const current = fieldProps.getValue();
1349
+ const onValueChangeRef = useRef(fieldProps.onValueChange);
1350
+ onValueChangeRef.current = fieldProps.onValueChange;
1351
+ const autoSelectedDataRef = useRef(void 0);
1352
+ useEffect(() => {
1353
+ if ((current == null || current === "") && data?.length === 1 && data !== autoSelectedDataRef.current) {
1354
+ autoSelectedDataRef.current = data;
1355
+ onValueChangeRef.current(type, data[0].value);
1356
+ }
1357
+ }, [
1358
+ current,
1359
+ data,
1360
+ type
1361
+ ]);
1362
+ const resolvedPlaceholder = loading ? "Loading…" : options.length === 0 ? "No options available" : placeholder;
1363
+ return /* @__PURE__ */ jsx(SelectField, {
1364
+ ...fieldProps,
1365
+ placeholder: resolvedPlaceholder,
1366
+ options: options.map(({ value, label, secondaryLabel, icon }) => ({
1367
+ value,
1368
+ label,
1369
+ secondaryLabel,
1370
+ icon
1371
+ }))
1372
+ });
1373
+ };
1374
+ AsyncSelectField.displayName = "Form.AsyncSelectField";
1375
+ //#endregion
1376
+ //#region src/components/Form/FormField/fields/TextField/TextField.tsx
1377
+ var TextField = ({ type, readonly, placeholder, onBlur, onValueChange, ...props }) => {
1378
+ const handleChange = useCallback((event) => onValueChange(type, event.target.value), [type, onValueChange]);
1379
+ return /* @__PURE__ */ jsx(FormRow, {
1380
+ readonly,
1381
+ ...props,
1382
+ children: ({ value = "" }) => /* @__PURE__ */ jsx(Input.TextInput, {
1383
+ noAutoFill: true,
1384
+ disabled: !!readonly,
1385
+ placeholder,
1386
+ value,
1387
+ onBlur,
1388
+ onChange: handleChange
1389
+ })
1390
+ });
1391
+ };
1392
+ //#endregion
1393
+ //#region src/components/Form/FormField/fields/AutofillField/AutofillField.tsx
1394
+ /**
1395
+ * An editable text field that pre-fills its value (debounced) from the fields the autofill declares in
1396
+ * its `deps`, via an {@link Autofill}. Re-runs only when one of those values changes. The
1397
+ * derived value is written only while the user has not typed their own (the field is empty or still
1398
+ * holds the last auto-filled value), so a manual edit is never clobbered.
1399
+ */
1400
+ var AutofillField = ({ autofill, ...fieldProps }) => {
1401
+ const values = useFormValues(AutofillField.displayName);
1402
+ const subset = useMemo(() => pickValues(values, autofill.deps), [values, autofill.deps]);
1403
+ const { data } = useAsyncFieldEffect(() => autofill.derive(subset), useMemo(() => JSON.stringify(subset), [subset]));
1404
+ const { type } = fieldProps;
1405
+ const getValueRef = useRef(fieldProps.getValue);
1406
+ getValueRef.current = fieldProps.getValue;
1407
+ const onValueChangeRef = useRef(fieldProps.onValueChange);
1408
+ onValueChangeRef.current = fieldProps.onValueChange;
1409
+ const lastFilledRef = useRef(void 0);
1410
+ useEffect(() => {
1411
+ const current = getValueRef.current();
1412
+ if (typeof data !== "string" || data.length === 0) {
1413
+ if (lastFilledRef.current != null && current === lastFilledRef.current) {
1414
+ lastFilledRef.current = void 0;
1415
+ onValueChangeRef.current(type, "");
1416
+ }
1417
+ return;
1418
+ }
1419
+ if (current === data) {
1420
+ lastFilledRef.current = data;
1421
+ return;
1422
+ }
1423
+ if (current == null || current === "" || current === lastFilledRef.current) {
1424
+ lastFilledRef.current = data;
1425
+ onValueChangeRef.current(type, data);
1426
+ }
1427
+ }, [data, type]);
1428
+ return /* @__PURE__ */ jsx(TextField, { ...fieldProps });
1429
+ };
1430
+ AutofillField.displayName = "Form.AutofillField";
1431
+ //#endregion
1432
+ //#region src/components/Form/FormField/fields/BooleanField/BooleanField.tsx
1433
+ var BooleanField = ({ type, readonly, onValueChange, ...props }) => {
1434
+ const handleChange = useCallback((value) => onValueChange?.(type, value), [type, onValueChange]);
1435
+ return /* @__PURE__ */ jsx(FormRow, {
1436
+ readonly,
1437
+ ...props,
1438
+ children: ({ value }) => /* @__PURE__ */ jsx(Input.Block, { children: /* @__PURE__ */ jsx(Input.Switch, {
1439
+ disabled: !!readonly,
1440
+ checked: value,
1441
+ onCheckedChange: handleChange
1442
+ }) })
1443
+ });
1444
+ };
1445
+ //#endregion
1446
+ //#region src/components/Form/FormField/fields/ComboboxField/ComboboxField.tsx
1447
+ /**
1448
+ * An editable combobox built on the shared {@link Combobox} primitive (trigger + popover search list,
1449
+ * the same family as `ObjectPicker`/`SearchList`). The trigger shows the current value; opening it reveals
1450
+ * a search input that loads suggestions (debounced) via an {@link OptionsLookup} and filters
1451
+ * them as you type. The literal typed text is offered as a fallback option at the bottom (deduped), so a
1452
+ * value not among the suggestions can still be selected. No options are shown until something is typed.
1453
+ */
1454
+ var ComboboxField = ({ lookup, type, readonly, placeholder, onValueChange, ...props }) => {
1455
+ const values = useFormValues(ComboboxField.displayName);
1456
+ const ownKey = props.jsonPath;
1457
+ const [query, setQuery] = useState("");
1458
+ const [selectedLabel, setSelectedLabel] = useState();
1459
+ const lookupValues = useMemo(() => {
1460
+ const subset = pickValues(values, lookup.deps);
1461
+ if (ownKey && lookup.deps.includes(ownKey)) subset[ownKey] = query;
1462
+ return subset;
1463
+ }, [
1464
+ values,
1465
+ lookup.deps,
1466
+ ownKey,
1467
+ query
1468
+ ]);
1469
+ const { data } = useAsyncFieldEffect(() => lookup.load(lookupValues), useMemo(() => JSON.stringify(lookupValues), [lookupValues]));
1470
+ const trimmed = query.trim();
1471
+ const normalized = trimmed.toLowerCase();
1472
+ const results = useMemo(() => normalized.length === 0 ? [] : (data ?? []).filter((option) => (option.label ?? option.value).toLowerCase().includes(normalized)), [data, normalized]);
1473
+ const hasExact = results.some((option) => option.value === trimmed);
1474
+ const handleValueChange = useCallback((next) => {
1475
+ setSelectedLabel((data ?? []).find((option) => option.value === next)?.label);
1476
+ onValueChange(type, next);
1477
+ }, [
1478
+ onValueChange,
1479
+ type,
1480
+ data
1481
+ ]);
1482
+ const handleOpenChange = useCallback((open) => {
1483
+ if (!open) setQuery("");
1484
+ }, []);
1485
+ return /* @__PURE__ */ jsx(FormRow, {
1486
+ readonly,
1487
+ renderStatic: (value) => /* @__PURE__ */ jsx("p", {
1488
+ className: "truncate min-w-0",
1489
+ children: selectedLabel ?? value ?? ""
1490
+ }),
1491
+ ...props,
1492
+ children: ({ value = "" }) => /* @__PURE__ */ jsxs(Combobox.Root, {
1493
+ value,
1494
+ displayValue: selectedLabel,
1495
+ onValueChange: handleValueChange,
1496
+ onOpenChange: handleOpenChange,
1497
+ placeholder,
1498
+ children: [/* @__PURE__ */ jsx(Combobox.Trigger, {
1499
+ disabled: !!readonly,
1500
+ classNames: "w-full"
1501
+ }), /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsxs(Combobox.Content, {
1502
+ resetSelectionOnChange: true,
1503
+ children: [
1504
+ /* @__PURE__ */ jsx(Combobox.Input, {
1505
+ autoFocus: true,
1506
+ value: query,
1507
+ onValueChange: setQuery,
1508
+ placeholder
1509
+ }),
1510
+ /* @__PURE__ */ jsxs(Combobox.List, { children: [results.map((option) => /* @__PURE__ */ jsx(Combobox.Item, {
1511
+ value: option.value,
1512
+ label: option.label ?? option.value,
1513
+ suffix: option.secondaryLabel
1514
+ }, option.value)), normalized.length > 0 && !hasExact && /* @__PURE__ */ jsx(Combobox.Item, {
1515
+ value: trimmed,
1516
+ label: trimmed
1517
+ })] }),
1518
+ /* @__PURE__ */ jsx(Combobox.Arrow, {})
1519
+ ]
1520
+ }) })]
1521
+ })
1522
+ });
1523
+ };
1524
+ ComboboxField.displayName = "Form.ComboboxField";
1525
+ //#endregion
1526
+ //#region src/components/Form/FormField/fields/DateField/DateField.tsx
1527
+ /**
1528
+ * Stored value shapes:
1529
+ * - `Format.DateTime` -> ISO 8601 (`2018-11-13T20:20:39.000Z`).
1530
+ * - `Format.Date` -> `YYYY-MM-DD`.
1531
+ * - `Format.Time` -> `HH:mm:ss`.
1532
+ *
1533
+ * Segmented input value shapes (react-aria-components-backed):
1534
+ * - `Input.DateTime` -> `YYYY-MM-DDTHH:mm` in local time.
1535
+ * - `Input.Date` -> `YYYY-MM-DD`.
1536
+ * - `Input.Time` -> `HH:mm`.
1537
+ */
1538
+ /** ISO 8601 → `YYYY-MM-DDTHH:mm` in the user's local timezone. */
1539
+ var isoToLocalDateTime = (value) => {
1540
+ if (!value) return "";
1541
+ const date = new Date(value);
1542
+ if (Number.isNaN(date.getTime())) return "";
1543
+ return format(date, "yyyy-MM-dd'T'HH:mm");
1544
+ };
1545
+ /** `YYYY-MM-DDTHH:mm` (local) → ISO 8601 with timezone. */
1546
+ var localDateTimeToIso = (value) => {
1547
+ if (!value) return;
1548
+ const date = new Date(value);
1549
+ return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
1550
+ };
1551
+ var DateField = ({ type, format, readonly, placeholder: _placeholder, onValueChange, onBlur: _onBlur, ...props }) => {
1552
+ const handleSimpleChange = useCallback((next) => onValueChange(type, next), [type, onValueChange]);
1553
+ const handleDateTimeChange = useCallback((next) => {
1554
+ onValueChange(type, localDateTimeToIso(next));
1555
+ }, [type, onValueChange]);
1556
+ return /* @__PURE__ */ jsx(FormRow, {
1557
+ ...props,
1558
+ readonly,
1559
+ format,
1560
+ children: ({ value }) => {
1561
+ switch (format) {
1562
+ case Format.TypeFormat.Date: return /* @__PURE__ */ jsxs("div", {
1563
+ className: "grid grid-cols-[minmax(0,1fr)_min-content] gap-1 items-stretch tabular-nums",
1564
+ children: [/* @__PURE__ */ jsx(Input.Date, {
1565
+ classNames: "min-w-0 overflow-hidden",
1566
+ disabled: readonly,
1567
+ value: value ?? "",
1568
+ onValueChange: handleSimpleChange
1569
+ }), /* @__PURE__ */ jsx(Input.TriggerIcon, {})]
1570
+ });
1571
+ case Format.TypeFormat.Time: return /* @__PURE__ */ jsx(Input.Time, {
1572
+ classNames: "tabular-nums",
1573
+ disabled: !!readonly,
1574
+ value: value ?? "",
1575
+ onValueChange: handleSimpleChange
1576
+ });
1577
+ case Format.TypeFormat.DateTime:
1578
+ default: return /* @__PURE__ */ jsxs("div", {
1579
+ className: "grid grid-cols-[minmax(0,1fr)_min-content] gap-1 items-stretch tabular-nums",
1580
+ children: [/* @__PURE__ */ jsx(Input.DateTime, {
1581
+ classNames: "min-w-0 overflow-hidden",
1582
+ disabled: readonly,
1583
+ value: isoToLocalDateTime(value),
1584
+ onValueChange: handleDateTimeChange
1585
+ }), /* @__PURE__ */ jsx(Input.TriggerIcon, {})]
1586
+ });
1587
+ }
1588
+ }
1589
+ });
1590
+ };
1591
+ //#endregion
1592
+ //#region src/components/Form/FormField/fields/GeoPointField/GeoPointField.tsx
1593
+ var GeoPointField = ({ type, readonly, getValue, onValueChange, onBlur, ...props }) => {
1594
+ const { t } = useTranslation(translationKey);
1595
+ const geoPoint = useMemo(() => getValue() ?? [0, 0], [getValue]);
1596
+ const value = useMemo(() => GeoLocation.fromGeoPoint(geoPoint), [geoPoint]);
1597
+ const [longitudeText, setLongitudeText] = useState(value.longitude?.toString());
1598
+ const [latitudeText, setLatitudeText] = useState(value.latitude?.toString());
1599
+ useEffect(() => {
1600
+ const location = GeoLocation.fromGeoPoint(geoPoint);
1601
+ setLongitudeText(location.longitude?.toString());
1602
+ setLatitudeText(location.latitude?.toString());
1603
+ }, [geoPoint]);
1604
+ const handleChange = useCallback((coordinateType, setText) => (event) => {
1605
+ const inputText = event.target.value;
1606
+ setText(inputText);
1607
+ if (inputText !== "" && inputText !== "-") {
1608
+ const coord = safeParseFloat(inputText);
1609
+ if (coord !== void 0 && !isNaN(coord)) {
1610
+ const newLocation = {
1611
+ ...GeoLocation.fromGeoPoint(getValue() ?? [0, 0]),
1612
+ [coordinateType]: coord
1613
+ };
1614
+ onValueChange(type, GeoLocation.toGeoPoint(newLocation));
1615
+ }
1616
+ }
1617
+ }, [
1618
+ type,
1619
+ getValue,
1620
+ onValueChange
1621
+ ]);
1622
+ return /* @__PURE__ */ jsx(FormRow, {
1623
+ readonly,
1624
+ getValue,
1625
+ standalone: true,
1626
+ renderStatic: (point) => {
1627
+ const location = GeoLocation.fromGeoPoint(point ?? [0, 0]);
1628
+ return !location.latitude && !location.longitude ? null : /* @__PURE__ */ jsx(LatLng, { ...location });
1629
+ },
1630
+ ...props,
1631
+ children: ({ presentation }) => /* @__PURE__ */ jsxs("div", {
1632
+ className: "grid grid-cols-2 gap-form-gap",
1633
+ children: [/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Input.Root, { children: [presentation.showLabel && /* @__PURE__ */ jsx(Input.Label, { children: t("latitude.label") }), /* @__PURE__ */ jsx(Input.TextInput, {
1634
+ type: "number",
1635
+ step: "0.00001",
1636
+ min: "-90",
1637
+ max: "90",
1638
+ disabled: !!readonly,
1639
+ placeholder: t("latitude.placeholder"),
1640
+ value: latitudeText ?? "",
1641
+ onChange: handleChange("latitude", setLatitudeText),
1642
+ onBlur
1643
+ })] }) }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Input.Root, { children: [presentation.showLabel && /* @__PURE__ */ jsx(Input.Label, { children: t("longitude.label") }), /* @__PURE__ */ jsx(Input.TextInput, {
1644
+ type: "number",
1645
+ step: "0.00001",
1646
+ min: "-180",
1647
+ max: "180",
1648
+ disabled: !!readonly,
1649
+ placeholder: t("longitude.placeholder"),
1650
+ value: longitudeText ?? "",
1651
+ onChange: handleChange("longitude", setLongitudeText),
1652
+ onBlur
1653
+ })] }) })]
1654
+ })
1655
+ });
1656
+ };
1657
+ var LatLng = ({ latitude = 0, longitude = 0 }) => {
1658
+ const latHem = latitude >= 0 ? "N" : "S";
1659
+ const lngHem = longitude >= 0 ? "E" : "W";
1660
+ return /* @__PURE__ */ jsxs("span", {
1661
+ className: "inline-flex items-center gap-form-gap",
1662
+ children: [/* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", { children: Math.abs(latitude).toFixed(5) }), /* @__PURE__ */ jsxs("span", {
1663
+ className: "text-subdued",
1664
+ children: ["°", latHem]
1665
+ })] }), /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", { children: Math.abs(longitude).toFixed(5) }), /* @__PURE__ */ jsxs("span", {
1666
+ className: "text-subdued",
1667
+ children: ["°", lngHem]
1668
+ })] })]
1669
+ });
1670
+ };
1671
+ //#endregion
1672
+ //#region src/components/Form/FormField/fields/MarkdownField/MarkdownField.tsx
1673
+ /**
1674
+ * Form field that edits a markdown value in a CodeMirror editor.
1675
+ * Supports two backings:
1676
+ * - `Schema.String` annotated with `Format.TypeFormat.Markdown`: edited as plain text via `getValue`/`onValueChange`.
1677
+ * - `Ref<Text>` annotated with `Format.TypeFormat.Markdown`: edited in-place via an Automerge doc accessor.
1678
+ * When the ref is empty and a database is available, a create button is shown to allocate a new Text.
1679
+ */
1680
+ var MarkdownField = ({ type, readonly, placeholder, db, getValue, onValueChange, ...props }) => {
1681
+ const isRef = Ref.isRefType(type);
1682
+ const renderStatic = (value) => {
1683
+ if (isRef) {
1684
+ const reference = value;
1685
+ return reference ? /* @__PURE__ */ jsx(RefStaticText, { reference }) : null;
1686
+ }
1687
+ return /* @__PURE__ */ jsx("p", {
1688
+ className: "whitespace-pre-wrap",
1689
+ children: value ?? ""
1690
+ });
1691
+ };
1692
+ const renderEditor = (value) => {
1693
+ if (isRef) {
1694
+ const reference = value;
1695
+ if (reference) return /* @__PURE__ */ jsx(RefMarkdownEditor, {
1696
+ reference,
1697
+ placeholder,
1698
+ readonly: !!readonly
1699
+ });
1700
+ if (readonly) return null;
1701
+ return /* @__PURE__ */ jsx(CreateTextButton, {
1702
+ db,
1703
+ disabled: !db,
1704
+ onCreate: (ref) => onValueChange(type, ref)
1705
+ });
1706
+ }
1707
+ return /* @__PURE__ */ jsx(StringMarkdownEditor, {
1708
+ value: value ?? "",
1709
+ placeholder,
1710
+ readonly: !!readonly,
1711
+ onChange: (next) => onValueChange(type, next)
1712
+ });
1713
+ };
1714
+ return /* @__PURE__ */ jsx(FormRow, {
1715
+ readonly,
1716
+ getValue,
1717
+ renderStatic,
1718
+ ...props,
1719
+ children: ({ value }) => renderEditor(value)
1720
+ });
1721
+ };
1722
+ /** Read-only static rendering for a `Ref<Text>` value: resolve the ref and print its content. */
1723
+ var RefStaticText = ({ reference }) => {
1724
+ const text = useAtomValue(useMemo(() => reference.atom, [reference]));
1725
+ if (!text?.content) return null;
1726
+ return /* @__PURE__ */ jsx("p", {
1727
+ className: "whitespace-pre-wrap",
1728
+ children: text.content
1729
+ });
1730
+ };
1731
+ var RefMarkdownEditor = ({ reference, placeholder, readonly }) => {
1732
+ const text = useAtomValue(useMemo(() => reference.atom, [reference]));
1733
+ const extensions = useBasicMarkdownExtensions({
1734
+ placeholder,
1735
+ readonly,
1736
+ extensions: useMemo(() => text ? [createDataExtensions({
1737
+ id: reference.uri,
1738
+ text: Doc.createAccessor(text, ["content"])
1739
+ })] : [], [text, reference])
1740
+ });
1741
+ if (!text) return null;
1742
+ return /* @__PURE__ */ jsx(Editor.Root, { children: /* @__PURE__ */ jsx(Editor.View, {
1743
+ classNames: "dx-input",
1744
+ extensions
1745
+ }) });
1746
+ };
1747
+ var StringMarkdownEditor = ({ value, placeholder, readonly, onChange }) => {
1748
+ const extensions = useBasicMarkdownExtensions({
1749
+ placeholder,
1750
+ readonly
1751
+ });
1752
+ const handleChange = useCallback((next) => onChange(next), [onChange]);
1753
+ return /* @__PURE__ */ jsx(Editor.Root, { children: /* @__PURE__ */ jsx(Editor.View, {
1754
+ classNames: "dx-input min-h-[6lh]",
1755
+ extensions,
1756
+ value,
1757
+ onChange: readonly ? void 0 : handleChange
1758
+ }) });
1759
+ };
1760
+ var CreateTextButton = ({ db, disabled, onCreate }) => {
1761
+ const { t } = useTranslation(translationKey);
1762
+ const handleClick = useCallback(() => {
1763
+ if (!db) return;
1764
+ onCreate(Ref.make(db.add(Text.make())));
1765
+ }, [db, onCreate]);
1766
+ return /* @__PURE__ */ jsx(IconButton, {
1767
+ icon: "ph--plus--regular",
1768
+ label: t("create-text.label"),
1769
+ onClick: handleClick,
1770
+ disabled
1771
+ });
1772
+ };
1773
+ //#endregion
1774
+ //#region src/components/Form/FormField/fields/NumberField/numeric-constraints.ts
1775
+ /**
1776
+ * Extracts numeric constraints (`minimum`/`maximum` bounds and whether the value must be an integer)
1777
+ * by walking the chain of Refinement ASTs and reading each one's JSON schema annotation (e.g. produced
1778
+ * by `Schema.between` / `Schema.int`). Returns empty constraints when none are declared.
1779
+ */
1780
+ var getNumericConstraints = (ast) => {
1781
+ let node = ast;
1782
+ let min;
1783
+ let max;
1784
+ let integer = false;
1785
+ while (node && SchemaAST.isRefinement(node)) {
1786
+ const jsonSchema = Option.getOrUndefined(SchemaAST.getJSONSchemaAnnotation(node));
1787
+ if (jsonSchema != null) {
1788
+ if ("minimum" in jsonSchema && typeof jsonSchema.minimum === "number") min = min === void 0 ? jsonSchema.minimum : Math.max(min, jsonSchema.minimum);
1789
+ if ("maximum" in jsonSchema && typeof jsonSchema.maximum === "number") max = max === void 0 ? jsonSchema.maximum : Math.min(max, jsonSchema.maximum);
1790
+ if ("type" in jsonSchema && jsonSchema.type === "integer" || "multipleOf" in jsonSchema && jsonSchema.multipleOf === 1) integer = true;
1791
+ }
1792
+ node = node.from;
1793
+ }
1794
+ return {
1795
+ min,
1796
+ max,
1797
+ integer
1798
+ };
1799
+ };
1800
+ //#endregion
1801
+ //#region src/components/Form/FormField/fields/NumberField/NumberField.tsx
1802
+ var NumberField = ({ type, readonly, placeholder, getValue, onValueChange, onBlur, ...props }) => {
1803
+ const { min, max, integer } = getNumericConstraints(type);
1804
+ const clamp = useCallback((n) => {
1805
+ let value = integer ? Math.round(n) : n;
1806
+ if (min !== void 0) value = Math.max(min, value);
1807
+ if (max !== void 0) value = Math.min(max, value);
1808
+ return value;
1809
+ }, [
1810
+ min,
1811
+ max,
1812
+ integer
1813
+ ]);
1814
+ const [raw, setRaw] = useState(() => {
1815
+ const v = getValue();
1816
+ return v !== void 0 ? String(v) : "";
1817
+ });
1818
+ const externalValue = getValue();
1819
+ useEffect(() => {
1820
+ if (externalValue !== safeParseFloat(raw)) setRaw(externalValue !== void 0 ? String(externalValue) : "");
1821
+ }, [externalValue]);
1822
+ const handleChange = useCallback((event) => {
1823
+ const value = event.target.value;
1824
+ setRaw(value);
1825
+ const parsed = safeParseFloat(value);
1826
+ if (parsed !== void 0) onValueChange(type, clamp(parsed));
1827
+ }, [
1828
+ type,
1829
+ onValueChange,
1830
+ clamp
1831
+ ]);
1832
+ const handleBlur = useCallback((event) => {
1833
+ const parsed = safeParseFloat(raw);
1834
+ if (parsed === void 0) {
1835
+ const committed = getValue();
1836
+ setRaw(committed !== void 0 ? String(committed) : "");
1837
+ } else setRaw(String(clamp(parsed)));
1838
+ onBlur(event);
1839
+ }, [
1840
+ raw,
1841
+ getValue,
1842
+ onBlur,
1843
+ clamp
1844
+ ]);
1845
+ return /* @__PURE__ */ jsx(FormRow, {
1846
+ readonly,
1847
+ getValue,
1848
+ ...props,
1849
+ children: () => /* @__PURE__ */ jsx(Input.TextInput, {
1850
+ type: "number",
1851
+ disabled: !!readonly,
1852
+ placeholder,
1853
+ value: raw,
1854
+ min,
1855
+ max,
1856
+ step: integer ? 1 : void 0,
1857
+ onChange: handleChange,
1858
+ onBlur: handleBlur
1859
+ })
1860
+ });
1861
+ };
1862
+ //#endregion
1863
+ //#region src/components/Form/FormField/fields/PasswordField/PasswordField.tsx
1864
+ var PasswordField = ({ type, readonly, placeholder, onBlur, onValueChange, ...props }) => {
1865
+ const handleChange = useCallback((event) => onValueChange(type, event.target.value), [type, onValueChange]);
1866
+ return /* @__PURE__ */ jsx(FormRow, {
1867
+ readonly,
1868
+ ...props,
1869
+ children: ({ value = "" }) => /* @__PURE__ */ jsx(Input.TextInput, {
1870
+ type: "password",
1871
+ noAutoFill: true,
1872
+ spellCheck: false,
1873
+ disabled: !!readonly,
1874
+ placeholder,
1875
+ value,
1876
+ onBlur,
1877
+ onChange: handleChange
1878
+ })
1879
+ });
1880
+ };
1881
+ //#endregion
1882
+ //#region src/components/Form/FormField/fields/RefField/find-ref-option.ts
1883
+ var isRefSnapshot = (val) => {
1884
+ return typeof val === "object" && typeof val?.["/"] === "string";
1885
+ };
1886
+ /**
1887
+ * Find the option a ref-like form value points at. Matches on the local (entity-id) form so a bare local EID
1888
+ * (`echo:/<id>`, produced by `Ref.make`) still resolves against an option keyed by the entity's qualified
1889
+ * self URI (`echo://<space>/<id>`). Returns `undefined` when the value is not a ref or no option matches.
1890
+ *
1891
+ * Comparing by entity id is only sound within one space (ids are unique there, not globally). Two EIDs that
1892
+ * both carry a space authority must therefore agree on it: a qualified value and a qualified option from
1893
+ * different spaces never match, even when their entity ids coincide.
1894
+ */
1895
+ var findRefOption = (value, options) => {
1896
+ const isRef = Ref.isRef(value);
1897
+ if (!isRef && !isRefSnapshot(value)) return;
1898
+ const valueUri = isRef ? value.uri : value["/"];
1899
+ const directMatch = options.find((option) => option.id === valueUri);
1900
+ if (directMatch) return directMatch;
1901
+ const valueEid = EID$1.tryParse(valueUri);
1902
+ if (!valueEid) return;
1903
+ const valueSpaceId = EID$1.getSpaceId(valueEid);
1904
+ const valueLocal = EID$1.toLocal(valueEid);
1905
+ return options.find((option) => {
1906
+ const optionEid = EID$1.tryParse(option.id);
1907
+ if (!optionEid) return false;
1908
+ const optionSpaceId = EID$1.getSpaceId(optionEid);
1909
+ if (valueSpaceId != null && optionSpaceId != null && valueSpaceId !== optionSpaceId) return false;
1910
+ return EID$1.equals(EID$1.toLocal(optionEid), valueLocal);
1911
+ });
1912
+ };
1913
+ //#endregion
1914
+ //#region src/components/ObjectPicker/ObjectPicker.tsx
1915
+ var ObjectPickerContent = composable(({ options, selectedIds, createSchema, createOptionLabel, createOptionIcon, createInitialValuePath, createFieldMap, onSelect, onCreate, ...props }, forwardedRef) => {
1916
+ const { t } = useTranslation(translationKey);
1917
+ const [showForm, setShowForm] = useState(false);
1918
+ const [formInitialValue, setFormInitialValue] = useState("");
1919
+ const { results, query, handleSearch } = useSearchListResults({ items: options });
1920
+ const handleFormSave = useCallback(async (values) => {
1921
+ await onCreate?.(values);
1922
+ setShowForm(false);
1923
+ setFormInitialValue("");
1924
+ }, [onCreate]);
1925
+ const handleFormCancel = useCallback(() => {
1926
+ setShowForm(false);
1927
+ setFormInitialValue("");
1928
+ }, []);
1929
+ const handleKeyDown = useCallback((event) => {
1930
+ if (event.key === "Enter" && createSchema && event.currentTarget.querySelector("[role=\"option\"][aria-selected=\"true\"][data-value=\"__create__\"]")) {
1931
+ event.stopPropagation();
1932
+ event.preventDefault();
1933
+ const currentQuery = event.currentTarget.querySelector("input[type=\"text\"]")?.value || "";
1934
+ setFormInitialValue(currentQuery);
1935
+ setShowForm(true);
1936
+ }
1937
+ }, [createSchema]);
1938
+ if (showForm && createSchema) return /* @__PURE__ */ jsxs(Combobox.Content, {
1939
+ ...props,
1940
+ onKeyDownCapture: handleKeyDown,
1941
+ ref: forwardedRef,
1942
+ children: [/* @__PURE__ */ jsx(Popover.Viewport, { children: /* @__PURE__ */ jsx(Form.Root, {
1943
+ testId: "create-referenced-object-form",
1944
+ schema: createSchema,
1945
+ defaultValues: createInitialValuePath ? { [createInitialValuePath]: formInitialValue } : {},
1946
+ fieldMap: createFieldMap,
1947
+ onSave: handleFormSave,
1948
+ onCancel: handleFormCancel,
1949
+ children: /* @__PURE__ */ jsx(Form.Viewport, { children: /* @__PURE__ */ jsxs(Form.Content, { children: [/* @__PURE__ */ jsx(Form.FieldSet, {}), /* @__PURE__ */ jsx(Form.Actions, {})] }) })
1950
+ }) }), /* @__PURE__ */ jsx(Combobox.Arrow, {})]
1951
+ });
1952
+ return /* @__PURE__ */ jsxs(Combobox.Content, {
1953
+ ...props,
1954
+ onKeyDownCapture: handleKeyDown,
1955
+ ref: forwardedRef,
1956
+ children: [
1957
+ /* @__PURE__ */ jsx(Combobox.Input, {
1958
+ placeholder: t("ref-field-combobox-input.placeholder"),
1959
+ autoFocus: true,
1960
+ value: query,
1961
+ onValueChange: handleSearch
1962
+ }),
1963
+ /* @__PURE__ */ jsxs(Combobox.List, { children: [results.map((option) => /* @__PURE__ */ jsx(Combobox.Item, {
1964
+ value: option.id,
1965
+ label: option.label,
1966
+ description: option.description,
1967
+ checked: selectedIds?.includes(option.id),
1968
+ onSelect: () => onSelect?.(option.id),
1969
+ classNames: "flex items-center gap-2"
1970
+ }, option.id)), createSchema && onCreate && /* @__PURE__ */ jsx(CreateItem, {
1971
+ query,
1972
+ createOptionLabel,
1973
+ createOptionIcon: createOptionIcon ?? "ph--plus--regular",
1974
+ onCreateItemSelect: (currentQuery) => {
1975
+ setFormInitialValue(currentQuery);
1976
+ setShowForm(true);
1977
+ }
1978
+ })] }),
1979
+ /* @__PURE__ */ jsx(Combobox.Arrow, {})
1980
+ ]
1981
+ });
1982
+ });
1983
+ var CreateItem = ({ query, createOptionLabel, createOptionIcon, onCreateItemSelect }) => {
1984
+ const { t } = useTranslation(translationKey);
1985
+ const label = createOptionLabel ? t(createOptionLabel[0], {
1986
+ ns: createOptionLabel[1].ns,
1987
+ text: query
1988
+ }) : t("create-option.label");
1989
+ return /* @__PURE__ */ jsx(Combobox.Item, {
1990
+ value: "__create__",
1991
+ label,
1992
+ icon: createOptionIcon,
1993
+ classNames: "flex items-center gap-2",
1994
+ closeOnSelect: false,
1995
+ onSelect: () => {
1996
+ onCreateItemSelect(query);
1997
+ }
1998
+ });
1999
+ };
2000
+ ObjectPickerContent.displayName = "ObjectPicker.Content";
2001
+ var ObjectPicker = {
2002
+ Root: Combobox.Root,
2003
+ Portal: Combobox.Portal,
2004
+ Trigger: Combobox.Trigger,
2005
+ VirtualTrigger: Combobox.VirtualTrigger,
2006
+ Content: ObjectPickerContent
2007
+ };
2008
+ //#endregion
2009
+ //#region src/components/Form/FormField/fields/RefField/RefField.tsx
2010
+ var defaultGetOptions = (results, { parentLabel, getTypePlaceholder } = {}) => results.filter((result) => !parentLabel || Obj.getParent(result) != null).map((result) => {
2011
+ const id = Entity.getURI(result, { prefer: "named" });
2012
+ const labelEntity = (parentLabel ? Obj.getParent(result) : void 0) ?? result;
2013
+ const typename = Entity.getTypename(labelEntity);
2014
+ return {
2015
+ id,
2016
+ label: Entity.getLabel(labelEntity) ?? (typename ? getTypePlaceholder?.(typename) : void 0) ?? id
2017
+ };
2018
+ });
2019
+ var defaultUseResults = (db, typename) => useQuery(db, !typename ? Query.select(Filter.nothing()) : typename === ANY_OBJECT_TYPENAME ? Query.select(Filter.everything()) : Query.select(Filter.type(DXN$1.make(typename))).from(Scope.space(), Scope.registry()));
2020
+ var RefField = (props) => {
2021
+ const { type, readonly, label, jsonPath, placeholder, presentation, required, getStatus, getValue, createOptionLabel, createOptionIcon, createInitialValuePath, createFieldMap, db, useType: useType$3 = useType, useResults = defaultUseResults, getOptions = defaultGetOptions, onCreate, resolveCreateEntry, onValueChange } = props;
2022
+ const { t } = useTranslation(translationKey);
2023
+ const { status, error } = getStatus();
2024
+ const resolved = presentationFor(presentation);
2025
+ const typename = useMemo(() => type ? SchemaEx.findAnnotation(type, ReferenceAnnotationId)?.typename : void 0, [type]);
2026
+ const getTypePlaceholder = useCallback((placeholderTypename) => {
2027
+ const segment = placeholderTypename.split(/[.:/]/).filter(Boolean).pop() ?? placeholderTypename;
2028
+ const humanized = segment.charAt(0).toUpperCase() + segment.slice(1);
2029
+ return t("object-name.placeholder", {
2030
+ ns: placeholderTypename,
2031
+ defaultValue: humanized
2032
+ });
2033
+ }, [t]);
2034
+ const results = useResults(db, typename);
2035
+ const options = useMemo(() => {
2036
+ const parentLabel = type ? SchemaEx.findAnnotation(type, ParentLabelAnnotationId) === true : false;
2037
+ return getOptions(results, {
2038
+ parentLabel,
2039
+ getTypePlaceholder
2040
+ });
2041
+ }, [
2042
+ results,
2043
+ getOptions,
2044
+ type,
2045
+ getTypePlaceholder
2046
+ ]);
2047
+ const item = useCallback(() => findRefOption(getValue(), options), [options, getValue])();
2048
+ const selectedIds = useMemo(() => item ? [item.id] : [], [item]);
2049
+ const createSchema = useType$3(db, typename && typename !== ANY_OBJECT_TYPENAME ? DXN$1.make(typename) : void 0);
2050
+ const createEntry = typename ? resolveCreateEntry?.(typename) : void 0;
2051
+ const [open, setOpen] = useState(false);
2052
+ const handleCreate = useCallback(async (values) => {
2053
+ if (createEntry?.createObject && db) {
2054
+ const newObject = await createEntry.createObject(values, db);
2055
+ if (newObject) onValueChange(type, Ref.make(newObject));
2056
+ } else if (createSchema && onCreate) {
2057
+ const newObject = await onCreate(createSchema, values);
2058
+ if (newObject) onValueChange(type, Ref.make(newObject));
2059
+ }
2060
+ setOpen(false);
2061
+ }, [
2062
+ createEntry,
2063
+ db,
2064
+ createSchema,
2065
+ onCreate,
2066
+ type,
2067
+ onValueChange
2068
+ ]);
2069
+ const handleUpdate = useCallback((id) => {
2070
+ const item = options.find((option) => option.id === id);
2071
+ const ref = item ? Ref.fromURI(URI.make(item.id)) : void 0;
2072
+ onValueChange(type, ref);
2073
+ }, [
2074
+ options,
2075
+ type,
2076
+ onValueChange
2077
+ ]);
2078
+ const handleSelect = useCallback((id) => {
2079
+ if (item?.id === id) handleUpdate(void 0);
2080
+ else handleUpdate(id);
2081
+ }, [item, handleUpdate]);
2082
+ if (!typename || (readonly || resolved.isStatic) && !item) return null;
2083
+ return /* @__PURE__ */ jsxs(Input.Root, {
2084
+ validationValence: status,
2085
+ children: [
2086
+ resolved.showLabel && /* @__PURE__ */ jsx(FormFieldLabel, {
2087
+ error,
2088
+ readonly,
2089
+ required,
2090
+ label,
2091
+ path: jsonPath
2092
+ }),
2093
+ /* @__PURE__ */ jsx("div", { children: readonly ? !item ? /* @__PURE__ */ jsx("p", {
2094
+ className: "text-description mb-2",
2095
+ children: t("empty-readonly-ref-field.label")
2096
+ }) : /* @__PURE__ */ jsx(DxAnchor, {
2097
+ dxn: item.id,
2098
+ rootclassname: "me-1",
2099
+ children: item.label
2100
+ }, item.id) : /* @__PURE__ */ jsxs(ObjectPicker.Root, {
2101
+ open,
2102
+ onOpenChange: setOpen,
2103
+ children: [/* @__PURE__ */ jsx(ObjectPicker.Trigger, {
2104
+ asChild: true,
2105
+ classNames: "p-0",
2106
+ children: item ? /* @__PURE__ */ jsx("div", {
2107
+ className: "flex gap-form-gap w-full",
2108
+ children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
2109
+ value: item.label,
2110
+ readOnly: true,
2111
+ classNames: "w-full"
2112
+ }) }, item.id)
2113
+ }) : /* @__PURE__ */ jsxs(Button, {
2114
+ classNames: "w-full text-start gap-form-gap",
2115
+ children: [/* @__PURE__ */ jsx("div", {
2116
+ className: "grow overflow-hidden",
2117
+ children: /* @__PURE__ */ jsx("span", {
2118
+ className: "truncate text-description",
2119
+ children: placeholder || label || t("ref-field.placeholder")
2120
+ })
2121
+ }), /* @__PURE__ */ jsx(Icon, {
2122
+ size: 3,
2123
+ icon: "ph--caret-down--bold"
2124
+ })]
2125
+ })
2126
+ }), /* @__PURE__ */ jsx(ObjectPicker.Portal, { children: /* @__PURE__ */ jsx(ObjectPicker.Content, {
2127
+ classNames: "dx-card-popover-width",
2128
+ options,
2129
+ selectedIds,
2130
+ createSchema: createEntry?.inputSchema ?? (createSchema && omitHiddenFormFields(omitId(Type.getSchema(createSchema)))),
2131
+ createOptionLabel,
2132
+ createOptionIcon,
2133
+ createInitialValuePath,
2134
+ createFieldMap,
2135
+ onCreate: onCreate || createEntry?.createObject ? handleCreate : void 0,
2136
+ onSelect: handleSelect
2137
+ }) })]
2138
+ }) }),
2139
+ resolved.showError && /* @__PURE__ */ jsx(Input.DescriptionAndValidation, { children: error })
2140
+ ]
2141
+ });
2142
+ };
2143
+ //#endregion
2144
+ //#region src/components/Form/FormField/fields/RefField/InlineRefField.tsx
2145
+ /**
2146
+ * Renders a referenced object's own fields inline (a nested form bound to the
2147
+ * target) rather than the {@link RefField} picker. Activated by
2148
+ * `FormInlineAnnotation.set(true)` on the `Ref` property.
2149
+ *
2150
+ * The nested form edits the target object directly: each changed path is
2151
+ * written back via `Obj.setValue` within an `Obj.update`, mirroring
2152
+ * `ObjectProperties` (without the synthetic meta-tags row).
2153
+ */
2154
+ var InlineRefField = (props) => {
2155
+ const { type, readonly, label, jsonPath, presentation, required, db, getValue, onValueChange, onCreate, useType: useType$2 = useType } = props;
2156
+ const { t } = useTranslation(translationKey);
2157
+ const resolved = presentationFor(presentation);
2158
+ const reference = getValue();
2159
+ const typename = useMemo(() => type ? SchemaEx.findAnnotation(type, ReferenceAnnotationId$1)?.typename : void 0, [type]);
2160
+ const createType = useType$2(db, typename ? DXN$1.make(typename) : void 0);
2161
+ const handleCreate = useCallback(async () => {
2162
+ if (!createType || !onCreate) return;
2163
+ const newObject = await onCreate(createType, {});
2164
+ if (newObject) onValueChange(type, Ref.make(newObject));
2165
+ }, [
2166
+ createType,
2167
+ onCreate,
2168
+ type,
2169
+ onValueChange
2170
+ ]);
2171
+ if (readonly && !reference) return null;
2172
+ return /* @__PURE__ */ jsxs(Input.Root, { children: [resolved.showLabel && /* @__PURE__ */ jsx(FormFieldLabel, {
2173
+ readonly,
2174
+ required,
2175
+ label,
2176
+ path: jsonPath
2177
+ }), reference ? /* @__PURE__ */ jsx(InlineForm, {
2178
+ reference,
2179
+ db,
2180
+ readonly,
2181
+ useType: useType$2
2182
+ }) : !readonly && onCreate && /* @__PURE__ */ jsx(IconButton, {
2183
+ classNames: "w-full gap-form-gap",
2184
+ disabled: !createType || !db,
2185
+ icon: "ph--plus--regular",
2186
+ label: label || t("ref-field.placeholder"),
2187
+ onClick: () => void handleCreate()
2188
+ })] });
2189
+ };
2190
+ var InlineForm = ({ reference, db, readonly, useType: useType$1 = useType }) => {
2191
+ const target = useAtomValue(useMemo(() => reference.atom, [reference]));
2192
+ const typeFromRegistry = useType$1(db, target ? Obj.getTypeURI(target) : void 0);
2193
+ const targetType = target && Obj.getType(target) || typeFromRegistry;
2194
+ const formSchema = useMemo(() => targetType ? omitId(Type.getSchema(targetType)) : void 0, [targetType]);
2195
+ const defaultValues = useMemo(() => target ? { ...target } : {}, [target]);
2196
+ const handleChange = useCallback((values, { isValid, changed }) => {
2197
+ if (!isValid || !target) return;
2198
+ const changedPaths = Object.keys(changed).filter((path) => changed[path]);
2199
+ if (changedPaths.length === 0) return;
2200
+ Obj.update(target, () => {
2201
+ for (const path of changedPaths) {
2202
+ const parts = SchemaEx.splitJsonPath(path);
2203
+ Obj.setValue(target, parts, Obj.getValue(values, parts));
2204
+ }
2205
+ });
2206
+ }, [target]);
2207
+ if (!target || !formSchema) return null;
2208
+ return /* @__PURE__ */ jsx(FormRoot, {
2209
+ db,
2210
+ schema: formSchema,
2211
+ defaultValues,
2212
+ onValuesChanged: handleChange,
2213
+ children: /* @__PURE__ */ jsx(FormContent, { children: /* @__PURE__ */ jsx(FormFieldSetContainer$1, {
2214
+ collapsible: true,
2215
+ readonly
2216
+ }) })
2217
+ });
2218
+ };
2219
+ //#endregion
2220
+ //#region src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.tsx
2221
+ var SelectOptionField = ({ type, readonly, label, jsonPath, getStatus, getValue, onValueChange }) => {
2222
+ const { t } = useTranslation(translationKey);
2223
+ const { status, error } = getStatus();
2224
+ const [selected, setSelectedId] = useState(null);
2225
+ const [isNewOption, setIsNewOption] = useState(false);
2226
+ const inputRef = useRef(null);
2227
+ const options = getValue();
2228
+ useEffect(() => {
2229
+ if (options === void 0) onValueChange(type, []);
2230
+ }, [
2231
+ options,
2232
+ type,
2233
+ onValueChange
2234
+ ]);
2235
+ const randomHue = () => {
2236
+ const usedHues = new Set(options?.map((option) => option.color) ?? []);
2237
+ const unusedHues = hues.filter((hue) => !usedHues.has(hue));
2238
+ if (unusedHues.length > 0) return unusedHues[Math.floor(Math.random() * unusedHues.length)];
2239
+ const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
2240
+ const availableHues = hues.filter((hue) => !lastThree.has(hue));
2241
+ return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
2242
+ };
2243
+ const handleAdd = useCallback(() => {
2244
+ const newId = PublicKey.random().truncate();
2245
+ const newOption = {
2246
+ id: newId,
2247
+ title: "",
2248
+ color: randomHue()
2249
+ };
2250
+ onValueChange(type, [...options ?? [], newOption]);
2251
+ setSelectedId(newId);
2252
+ setIsNewOption(true);
2253
+ }, [
2254
+ options,
2255
+ type,
2256
+ onValueChange
2257
+ ]);
2258
+ const handleDelete = useCallback((id) => {
2259
+ onValueChange(type, options?.filter((option) => option.id !== id) ?? []);
2260
+ setSelectedId(null);
2261
+ }, [
2262
+ options,
2263
+ type,
2264
+ onValueChange
2265
+ ]);
2266
+ const handleMove = useCallback((from, to) => {
2267
+ if (!options) return;
2268
+ const newOptions = [...options];
2269
+ const [removed] = newOptions.splice(from, 1);
2270
+ newOptions.splice(to, 0, removed);
2271
+ onValueChange(type, newOptions);
2272
+ }, [
2273
+ options,
2274
+ type,
2275
+ onValueChange
2276
+ ]);
2277
+ const handleClick = useCallback((id) => {
2278
+ setSelectedId((current) => current === id ? null : id);
2279
+ }, []);
2280
+ const handleTitleChange = useCallback((id) => (e) => {
2281
+ const newOptions = options?.map((o) => o.id === id ? {
2282
+ ...o,
2283
+ title: e.target.value
2284
+ } : o);
2285
+ onValueChange(type, newOptions ?? []);
2286
+ }, [
2287
+ options,
2288
+ type,
2289
+ onValueChange
2290
+ ]);
2291
+ const handleKeyDown = useCallback((ev) => {
2292
+ if (ev.key === "Enter") setSelectedId(null);
2293
+ }, []);
2294
+ const handleColorChange = useCallback((id) => (hue) => {
2295
+ const newOptions = options?.map((o) => o.id === id ? {
2296
+ ...o,
2297
+ color: hue
2298
+ } : o);
2299
+ onValueChange(type, newOptions ?? []);
2300
+ }, [
2301
+ options,
2302
+ type,
2303
+ onValueChange
2304
+ ]);
2305
+ useEffect(() => {
2306
+ if (selected && isNewOption && inputRef.current) {
2307
+ inputRef.current.focus();
2308
+ setIsNewOption(false);
2309
+ }
2310
+ }, [selected, isNewOption]);
2311
+ return /* @__PURE__ */ jsxs(Input.Root, {
2312
+ validationValence: status,
2313
+ children: [/* @__PURE__ */ jsx(FormFieldLabel, {
2314
+ error,
2315
+ readonly,
2316
+ label,
2317
+ path: jsonPath
2318
+ }), /* @__PURE__ */ jsx("div", { children: options && /* @__PURE__ */ jsx(OrderedList.Root, {
2319
+ items: options,
2320
+ isItem: (_item) => true,
2321
+ getId: (option) => option.id,
2322
+ onMove: readonly ? void 0 : handleMove,
2323
+ readonly: !!readonly,
2324
+ children: ({ items }) => /* @__PURE__ */ jsxs(OrderedList.Content, {
2325
+ classNames: "w-full overflow-auto",
2326
+ children: [items?.map((item) => /* @__PURE__ */ jsxs(OrderedList.Item, {
2327
+ id: item.id,
2328
+ item,
2329
+ canDrag: !readonly,
2330
+ classNames: "flex flex-col cursor-pointer rounded-xs dx-hover",
2331
+ children: [/* @__PURE__ */ jsxs("div", {
2332
+ className: "flex items-center",
2333
+ children: [
2334
+ /* @__PURE__ */ jsx(OrderedList.DragHandle, {}),
2335
+ /* @__PURE__ */ jsx("div", {
2336
+ className: "flex grow items-center truncate px-2",
2337
+ onClick: () => handleClick(item.id),
2338
+ children: /* @__PURE__ */ jsx(Tag$1, {
2339
+ hue: item.color,
2340
+ children: item.title || "​"
2341
+ })
2342
+ }),
2343
+ /* @__PURE__ */ jsx(ToggleIconButton, {
2344
+ iconOnly: true,
2345
+ variant: "ghost",
2346
+ active: selected === item.id,
2347
+ icon: "ph--caret-right--regular",
2348
+ label: t(selected === item.id ? "collapse.label" : "expand.label", { ns: osTranslations }),
2349
+ onClick: () => handleClick(item.id)
2350
+ })
2351
+ ]
2352
+ }), selected === item.id && /* @__PURE__ */ jsxs("div", {
2353
+ className: "flex flex-col p-form-padding gap-form-gap dx-density-md",
2354
+ children: [/* @__PURE__ */ jsx(Input.Label, {
2355
+ classNames: "text-sm",
2356
+ children: t("select-option.label")
2357
+ }), /* @__PURE__ */ jsxs("div", {
2358
+ className: "grid grid-cols-[1fr_min-content_min-content] gap-form-gap",
2359
+ children: [
2360
+ /* @__PURE__ */ jsx(Input.TextInput, {
2361
+ disabled: !!readonly,
2362
+ placeholder: t("select-option-label.placeholder"),
2363
+ ref: selected === item.id ? inputRef : void 0,
2364
+ value: item.title,
2365
+ onChange: handleTitleChange(item.id),
2366
+ onKeyDown: handleKeyDown,
2367
+ classNames: "flex-1"
2368
+ }),
2369
+ /* @__PURE__ */ jsx(HuePicker, {
2370
+ disabled: !!readonly,
2371
+ value: item.color,
2372
+ onChange: handleColorChange(item.id)
2373
+ }),
2374
+ /* @__PURE__ */ jsx(IconButton, {
2375
+ disabled: !!readonly,
2376
+ icon: "ph--trash--fill",
2377
+ iconOnly: true,
2378
+ label: t("select-option-delete.button"),
2379
+ onClick: () => handleDelete(item.id)
2380
+ })
2381
+ ]
2382
+ })]
2383
+ })]
2384
+ }, item.id)), /* @__PURE__ */ jsx(IconButton, {
2385
+ classNames: "w-full",
2386
+ variant: "ghost",
2387
+ icon: "ph--plus--regular",
2388
+ label: t("select-option-add.button"),
2389
+ onClick: handleAdd,
2390
+ disabled: !!readonly
2391
+ })]
2392
+ })
2393
+ }) })]
2394
+ });
2395
+ };
2396
+ //#endregion
2397
+ //#region src/components/Form/FormField/fields/TextAreaField/TextAreaField.tsx
2398
+ var TextAreaField = ({ type, readonly, placeholder, onValueChange, onBlur, ...props }) => {
2399
+ const handleChange = useCallback((event) => onValueChange(type, event.target.value), [type, onValueChange]);
2400
+ return /* @__PURE__ */ jsx(FormRow, {
2401
+ readonly,
2402
+ ...props,
2403
+ children: ({ value = "" }) => /* @__PURE__ */ jsx(Input.TextArea, {
2404
+ rows: 5,
2405
+ disabled: !!readonly,
2406
+ placeholder,
2407
+ value,
2408
+ onChange: handleChange,
2409
+ onBlur
2410
+ })
2411
+ });
2412
+ };
2413
+ //#endregion
2414
+ //#region src/components/Form/FormField/fields/TupleField/TupleField.tsx
2415
+ var gridCols = [
2416
+ "grid-cols-1",
2417
+ "grid-cols-2",
2418
+ "grid-cols-3",
2419
+ "grid-cols-4"
2420
+ ];
2421
+ var TupleField = ({ binding, type, readonly, label, jsonPath, getStatus, getValue, onValueChange, onBlur }) => {
2422
+ const { status, error } = getStatus();
2423
+ const values = getValue() ?? {};
2424
+ return /* @__PURE__ */ jsxs(Input.Root, {
2425
+ validationValence: status,
2426
+ children: [/* @__PURE__ */ jsx(FormFieldLabel, {
2427
+ error,
2428
+ readonly,
2429
+ label,
2430
+ path: jsonPath
2431
+ }), /* @__PURE__ */ jsx("div", {
2432
+ className: mx("grid gap-form-gap", gridCols[binding.length - 1]),
2433
+ children: binding.map((prop) => /* @__PURE__ */ jsx(Input.TextInput, {
2434
+ type: "number",
2435
+ disabled: !!readonly,
2436
+ value: values[prop],
2437
+ onChange: (event) => {
2438
+ onValueChange(type, {
2439
+ ...values,
2440
+ [prop]: safeParseFloat(event.target.value, 0)
2441
+ });
2442
+ },
2443
+ onBlur
2444
+ }, prop))
2445
+ })]
2446
+ });
2447
+ };
2448
+ //#endregion
2449
+ //#region src/components/Form/FormLayout/resolve-layout-field.ts
2450
+ /**
2451
+ * Walks a dotted path of property names through nested struct ASTs, returning the
2452
+ * leaf `PropertySignature` (or `undefined` if any segment is missing).
2453
+ */
2454
+ var resolvePropertySignature = (ast, segments) => {
2455
+ let node = ast;
2456
+ for (let index = 0; index < segments.length; index++) {
2457
+ const typeLiteral = SchemaEx.findNode(node, SchemaAST.isTypeLiteral);
2458
+ if (!typeLiteral) return;
2459
+ const prop = SchemaAST.getPropertySignatures(typeLiteral).find((p) => globalThis.String(p.name) === segments[index]);
2460
+ if (!prop) return;
2461
+ if (index === segments.length - 1) return prop;
2462
+ node = prop.type;
2463
+ }
2464
+ };
2465
+ /**
2466
+ * Resolves a (possibly dotted) layout field name against a schema.
2467
+ * - `origin.code` drills through nested structs to the leaf field.
2468
+ * - A name resolving to a nested struct with a `LabelAnnotation` is flagged via
2469
+ * `labelType` so the layout can auto-convert it to a single read-only label.
2470
+ * Returns `undefined` when the name does not resolve to a property.
2471
+ */
2472
+ var resolveLayoutField = (schema, name) => {
2473
+ const segments = name.split(".");
2474
+ const prop = resolvePropertySignature(schema.ast, segments);
2475
+ if (!prop) return;
2476
+ const { type: baseType } = SchemaEx.getBaseType(prop);
2477
+ const type = prop.annotations && Reflect.ownKeys(prop.annotations).length > 0 ? {
2478
+ ...baseType,
2479
+ annotations: {
2480
+ ...baseType.annotations,
2481
+ ...prop.annotations
2482
+ }
2483
+ } : baseType;
2484
+ const title = SchemaEx.getAnnotation(SchemaAST.TitleAnnotationId)(type);
2485
+ const labelType = SchemaEx.findNode(prop.type, SchemaAST.isTypeLiteral);
2486
+ const labelled = labelType != null && Option.isSome(Annotation.LabelAnnotation.getFromAst(labelType));
2487
+ return {
2488
+ type,
2489
+ segments,
2490
+ leafName: segments[segments.length - 1],
2491
+ title,
2492
+ labelType: labelled ? labelType : void 0,
2493
+ required: !prop.isOptional
2494
+ };
2495
+ };
2496
+ //#endregion
2497
+ //#region src/components/Form/FormLayout/parser.ts
2498
+ var LayoutParseError = class extends Error {
2499
+ position;
2500
+ _tag = "LayoutParseError";
2501
+ constructor(message, position) {
2502
+ super(position !== void 0 ? `${message} (at position ${position})` : message);
2503
+ this.position = position;
2504
+ }
2505
+ };
2506
+ var TAG_RE = /<\s*(\/?)\s*(\w+)([^>]*?)(\/?)\s*>/g;
2507
+ var ATTR_RE = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\d+))/g;
2508
+ var parseAttrs = (raw) => {
2509
+ const attrs = {};
2510
+ ATTR_RE.lastIndex = 0;
2511
+ let match;
2512
+ while ((match = ATTR_RE.exec(raw)) !== null) {
2513
+ const [, key, doubleQuoted, singleQuoted, bare] = match;
2514
+ attrs[key] = doubleQuoted ?? singleQuoted ?? bare;
2515
+ }
2516
+ return attrs;
2517
+ };
2518
+ var tokenize = (input) => {
2519
+ const tokens = [];
2520
+ TAG_RE.lastIndex = 0;
2521
+ let match;
2522
+ let cursor = 0;
2523
+ while ((match = TAG_RE.exec(input)) !== null) {
2524
+ const between = input.slice(cursor, match.index);
2525
+ if (between.trim().length > 0) throw new LayoutParseError(`unexpected text content: ${JSON.stringify(between.trim())}`, cursor);
2526
+ const [, slash, name, attrsRaw, selfSlash] = match;
2527
+ if (slash) tokens.push({
2528
+ type: "close",
2529
+ name,
2530
+ position: match.index
2531
+ });
2532
+ else tokens.push({
2533
+ type: "open",
2534
+ name,
2535
+ attrs: parseAttrs(attrsRaw),
2536
+ selfClosing: selfSlash === "/",
2537
+ position: match.index
2538
+ });
2539
+ cursor = match.index + match[0].length;
2540
+ }
2541
+ const trailing = input.slice(cursor);
2542
+ if (trailing.trim().length > 0) throw new LayoutParseError(`unexpected text content: ${JSON.stringify(trailing.trim())}`, cursor);
2543
+ return tokens;
2544
+ };
2545
+ var requireAttr = (attrs, key, tag, position) => {
2546
+ const value = attrs[key];
2547
+ if (value === void 0) throw new LayoutParseError(`<${tag}> missing required attribute "${key}"`, position);
2548
+ return value;
2549
+ };
2550
+ var parseInteger = (value, attr, tag, position) => {
2551
+ if (!/^\d+$/.test(value)) throw new LayoutParseError(`<${tag}> attribute "${attr}" expects an integer, got ${JSON.stringify(value)}`, position);
2552
+ return Number.parseInt(value, 10);
2553
+ };
2554
+ /** Parses a layout template string into a single root node. The root must be `<grid>`. */
2555
+ var parseLayout = (template) => {
2556
+ const tokens = tokenize(template);
2557
+ if (tokens.length === 0) throw new LayoutParseError("empty template");
2558
+ let index = 0;
2559
+ const parseNode = () => {
2560
+ const token = tokens[index];
2561
+ if (!token || token.type !== "open") throw new LayoutParseError(`expected an opening tag, got ${JSON.stringify(token)}`, token?.position);
2562
+ if (token.name === "field") {
2563
+ if (!token.selfClosing) throw new LayoutParseError("<field> must be self-closing (e.g. <field name=\"x\"/>)", token.position);
2564
+ const name = requireAttr(token.attrs, "name", "field", token.position);
2565
+ const spanRaw = token.attrs.span;
2566
+ const span = spanRaw !== void 0 ? parseInteger(spanRaw, "span", "field", token.position) : void 0;
2567
+ index += 1;
2568
+ return {
2569
+ kind: "field",
2570
+ name,
2571
+ span
2572
+ };
2573
+ }
2574
+ if (token.name === "grid") {
2575
+ if (token.selfClosing) throw new LayoutParseError("<grid> must wrap children (cannot self-close)", token.position);
2576
+ const cols = parseInteger(requireAttr(token.attrs, "cols", "grid", token.position), "cols", "grid", token.position);
2577
+ index += 1;
2578
+ const children = [];
2579
+ while (index < tokens.length && !(tokens[index].type === "close" && tokens[index].name === "grid")) children.push(parseNode());
2580
+ const closing = tokens[index];
2581
+ if (!closing || closing.type !== "close" || closing.name !== "grid") throw new LayoutParseError("<grid> not closed", token.position);
2582
+ index += 1;
2583
+ return {
2584
+ kind: "grid",
2585
+ cols,
2586
+ children
2587
+ };
2588
+ }
2589
+ throw new LayoutParseError(`unknown element <${token.name}>`, token.position);
2590
+ };
2591
+ const root = parseNode();
2592
+ if (index !== tokens.length) {
2593
+ const trailing = tokens[index];
2594
+ throw new LayoutParseError(`unexpected trailing tag <${trailing.type === "open" ? "" : "/"}${trailing.name}>`, trailing.position);
2595
+ }
2596
+ if (root.kind !== "grid") throw new LayoutParseError("root element must be <grid>");
2597
+ return root;
2598
+ };
2599
+ //#endregion
2600
+ //#region src/components/Form/FormLayout/FormLayout.tsx
2601
+ var FORM_LAYOUT_NAME$1 = "Form.Layout";
2602
+ /**
2603
+ * Lays out schema fields according to a `FormLayout` DSL template. The template
2604
+ * is either passed via the `template` prop or read from the schema's
2605
+ * `FormLayoutAnnotation`. Fields not referenced in the template are hidden —
2606
+ * the template controls exactly what renders.
2607
+ *
2608
+ * A `<field name=…/>` resolves against the schema:
2609
+ * - A dotted name (`origin.code`) drills into a nested struct and renders the leaf field.
2610
+ * - A name that resolves to a nested struct carrying a `LabelAnnotation` auto-converts
2611
+ * to the struct's computed label, rendered as a single read-only text (rather than
2612
+ * expanding the struct's sub-fields).
2613
+ */
2614
+ var FormLayout = ({ schema, template, name = Annotation.DEFAULT_LAYOUT_NAME, path, readonly, layout, projection, ...props }) => {
2615
+ const annotated = Option.getOrUndefined(Annotation.FormLayoutAnnotation.get(schema));
2616
+ const source = template ?? annotated?.[name];
2617
+ if (source === void 0) throw new LayoutParseError(annotated ? `no layout named "${name}" on schema (available: ${Object.keys(annotated).join(", ") || "∅"})` : `no template provided and schema has no FormLayoutAnnotation`);
2618
+ return /* @__PURE__ */ jsx(RenderNode, {
2619
+ node: useMemo(() => parseLayout(source), [source]),
2620
+ schema,
2621
+ basePath: path ?? [],
2622
+ readonly,
2623
+ layout,
2624
+ projection,
2625
+ ...props
2626
+ });
2627
+ };
2628
+ FormLayout.displayName = FORM_LAYOUT_NAME$1;
2629
+ var RenderNode = ({ node, schema, basePath, ...props }) => {
2630
+ if (node.kind === "grid") return /* @__PURE__ */ jsx("div", {
2631
+ className: "grid gap-x-form-gap",
2632
+ style: { gridTemplateColumns: `repeat(${node.cols}, minmax(0, 1fr))` },
2633
+ children: node.children.map((child, index) => /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(RenderNode, {
2634
+ node: child,
2635
+ schema,
2636
+ basePath,
2637
+ ...props
2638
+ }) }, index))
2639
+ });
2640
+ const resolved = resolveLayoutField(schema, node.name);
2641
+ if (!resolved) throw new LayoutParseError(`field "${node.name}" not found on schema`);
2642
+ const { type, segments, leafName, title, labelType, required } = resolved;
2643
+ const path = [...basePath, ...segments];
2644
+ return /* @__PURE__ */ jsx("div", {
2645
+ className: "min-w-0",
2646
+ style: node.span ? { gridColumn: `span ${node.span} / span ${node.span}` } : void 0,
2647
+ children: /* @__PURE__ */ jsx(FormFieldErrorBoundary, {
2648
+ path,
2649
+ children: labelType ? /* @__PURE__ */ jsx(LabelField, {
2650
+ schema: Schema.make(labelType),
2651
+ label: title ?? String$1.capitalize(leafName),
2652
+ path,
2653
+ layout: props.layout
2654
+ }) : /* @__PURE__ */ jsx(FormField, {
2655
+ type,
2656
+ name: leafName,
2657
+ path,
2658
+ required,
2659
+ ...props
2660
+ })
2661
+ })
2662
+ });
2663
+ };
2664
+ /**
2665
+ * Renders a nested struct value as its computed label (via `LabelAnnotation`),
2666
+ * as a single read-only text. Empty values are omitted, mirroring the static
2667
+ * presentation of regular fields.
2668
+ */
2669
+ var LabelField = ({ schema, label, path, layout }) => {
2670
+ const { variant = "default" } = useFormContext(FORM_LAYOUT_NAME$1);
2671
+ const styles = formTheme.styles({ variant });
2672
+ const { getValue } = useFormFieldState(FORM_LAYOUT_NAME$1, path);
2673
+ const value = getValue();
2674
+ const text = value == null ? void 0 : Annotation.getLabelWithSchema(schema, value);
2675
+ if (text == null || text.trim().length === 0) return null;
2676
+ const presentation = presentationFor(layout);
2677
+ return /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsxs("div", {
2678
+ className: styles.field(),
2679
+ children: [presentation.showLabel && /* @__PURE__ */ jsx(FormFieldLabel, {
2680
+ variant,
2681
+ readonly: true,
2682
+ label,
2683
+ path: SchemaEx.createJsonPath(path)
2684
+ }), /* @__PURE__ */ jsx("div", {
2685
+ className: styles.fieldControl(),
2686
+ children: /* @__PURE__ */ jsx("p", {
2687
+ className: "truncate min-w-0",
2688
+ title: text,
2689
+ children: text
2690
+ })
2691
+ })]
2692
+ }) });
2693
+ };
2694
+ //#endregion
2695
+ //#region src/components/Form/FormFieldSet/FormFieldSetContainer.tsx
2696
+ var styles$1 = formTheme.styles();
2697
+ /**
2698
+ * Shared chrome for a labelled group of sub-fields: an optional header (label + collapse toggle) and,
2699
+ * when collapsible, an indented bordered container around the body. Nested structs, object-array items,
2700
+ * and inline refs all reach this via `FormFieldSet`, so their visual containment is identical.
2701
+ */
2702
+ var FormFieldSetContainer = ({ classNames, label, path, readonly, presentation, collapsible, children }) => {
2703
+ const { t } = useTranslation(translationKey);
2704
+ const [collapsed, setCollapsed] = useState(false);
2705
+ const canCollapse = collapsible && Children.toArray(children).length > 0;
2706
+ const showBody = !(canCollapse && collapsed);
2707
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [presentation.showLabel && label && /* @__PURE__ */ jsx(FormFieldHeader, {
2708
+ label,
2709
+ path,
2710
+ readonly,
2711
+ classNames: "pl-2",
2712
+ actions: canCollapse ? /* @__PURE__ */ jsx(ToggleIconButton, {
2713
+ active: !collapsed,
2714
+ classNames: "px-1 mr-0.5",
2715
+ variant: "ghost",
2716
+ density: "sm",
2717
+ iconOnly: true,
2718
+ icon: "ph--caret-right--regular",
2719
+ label: t(collapsed ? "expand-fields.label" : "collapse-fields.label")
2720
+ }) : void 0,
2721
+ onClick: canCollapse ? () => setCollapsed((value) => !value) : void 0
2722
+ }), showBody && (canCollapse ? /* @__PURE__ */ jsx("div", {
2723
+ className: styles$1.fieldSetBody(),
2724
+ children
2725
+ }) : children)] });
2726
+ if (canCollapse) return /* @__PURE__ */ jsx("div", {
2727
+ className: styles$1.fieldSetBoxOuter(),
2728
+ children: /* @__PURE__ */ jsx("div", {
2729
+ className: styles$1.fieldSetBox({ class: mx(classNames) }),
2730
+ children: content
2731
+ })
2732
+ });
2733
+ return classNames ? /* @__PURE__ */ jsx("div", {
2734
+ className: mx(classNames),
2735
+ children: content
2736
+ }) : /* @__PURE__ */ jsx(Fragment$1, { children: content });
2737
+ };
2738
+ //#endregion
2739
+ //#region src/components/Form/FormFieldSet/FormFieldSet.tsx
2740
+ var FORM_FIELDSET_NAME$1 = "Form.FieldSet";
2741
+ /**
2742
+ * Renders a set of form fields derived from a schema object.
2743
+ */
2744
+ var FormFieldSet = ({ classNames, label, schema, readonly, path, sort, collapsible, filter, projection, layout, layoutName = DEFAULT_LAYOUT_NAME, ...props }) => {
2745
+ const values = useFormValues(FORM_FIELDSET_NAME$1, path);
2746
+ const properties = useFormFieldSetProperties({
2747
+ schema,
2748
+ values,
2749
+ filter,
2750
+ sort,
2751
+ projection
2752
+ });
2753
+ if ((readonly || layout === "static") && values == null) return null;
2754
+ const body = (schema ? Option.getOrUndefined(FormLayoutAnnotation.get(schema)) : void 0)?.[layoutName] !== void 0 && schema ? /* @__PURE__ */ jsx(FormLayout, {
2755
+ schema,
2756
+ name: layoutName,
2757
+ path,
2758
+ readonly,
2759
+ layout,
2760
+ projection,
2761
+ ...props
2762
+ }) : properties.map((property) => {
2763
+ const name = property.name.toString();
2764
+ return /* @__PURE__ */ jsx(FormFieldErrorBoundary, {
2765
+ path: [...path ?? [], name],
2766
+ children: /* @__PURE__ */ jsx(FormField, {
2767
+ type: property.type,
2768
+ name,
2769
+ path: [...path ?? [], name],
2770
+ required: !property.isOptional,
2771
+ readonly,
2772
+ layout,
2773
+ projection,
2774
+ ...props
2775
+ })
2776
+ }, name);
2777
+ });
2778
+ return /* @__PURE__ */ jsx(FormFieldSetContainer, {
2779
+ classNames,
2780
+ label,
2781
+ path: SchemaEx.createJsonPath(path ?? []),
2782
+ readonly,
2783
+ presentation: presentationFor(layout),
2784
+ collapsible,
2785
+ children: body
2786
+ });
2787
+ };
2788
+ FormFieldSet.displayName = FORM_FIELDSET_NAME$1;
2789
+ /**
2790
+ * Resolves ordered schema properties for a field set (projection order, filter, or sort).
2791
+ */
2792
+ var useFormFieldSetProperties = ({ schema, filter, projection, values, sort }) => {
2793
+ return useMemo(() => {
2794
+ if (!schema) return [];
2795
+ const schemaProps = getRootFormProperties(schema.ast, values);
2796
+ const filteredProps = filter ? filter(schemaProps) : schemaProps;
2797
+ if (projection) {
2798
+ const fieldProjections = projection.getFieldProjections();
2799
+ const hiddenProperties = new Set(projection.getHiddenProperties());
2800
+ const visibleProps = filteredProps.filter((prop) => !hiddenProperties.has(prop.name.toString()));
2801
+ const orderedProps = [];
2802
+ for (const fieldProjection of fieldProjections) {
2803
+ const fieldPath = String(fieldProjection.field.path);
2804
+ const prop = visibleProps.find((prop) => prop.name === fieldPath);
2805
+ if (prop) orderedProps.push(prop);
2806
+ }
2807
+ const projectionPaths = new Set(fieldProjections.map((projection) => String(projection.field.path)));
2808
+ const remainingProps = visibleProps.filter((prop) => !projectionPaths.has(prop.name.toString()));
2809
+ orderedProps.push(...remainingProps);
2810
+ return orderedProps;
2811
+ }
2812
+ return sort ? [...filteredProps].sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString())) : filteredProps;
2813
+ }, [
2814
+ schema,
2815
+ values,
2816
+ filter,
2817
+ sort,
2818
+ projection
2819
+ ]);
2820
+ };
2821
+ //#endregion
2822
+ //#region src/components/Form/FormControls.tsx
2823
+ var FormRoot = ({ children, schema, values, onSave, onCancel, ...props }) => {
2824
+ return /* @__PURE__ */ jsx(FormContextProvider, {
2825
+ form: useFormHandler({
2826
+ schema,
2827
+ values,
2828
+ onSave,
2829
+ onCancel,
2830
+ ...props
2831
+ }),
2832
+ ...props,
2833
+ children
2834
+ });
2835
+ };
2836
+ FormRoot.displayName = "Form.Root";
2837
+ var FORM_VIEWPORT_NAME = "Form.Viewport";
2838
+ var FormViewport = composable(({ children, scroll, gutter = "sm", ...props }, forwardedRef) => {
2839
+ const { variant = "default" } = useFormContext(FORM_VIEWPORT_NAME);
2840
+ const styles = formTheme.styles({ variant });
2841
+ const inColumn = useInColumn();
2842
+ const span = "[.dx-column-root_&]:col-span-full";
2843
+ if (inColumn && !scroll) return /* @__PURE__ */ jsx(Column.Center, {
2844
+ ...composableProps(props, { classNames: ["w-full min-w-0", styles.viewport()] }),
2845
+ ref: forwardedRef,
2846
+ children
2847
+ });
2848
+ if (scroll) return /* @__PURE__ */ jsx(Column.Root, {
2849
+ ...composableProps(props, { classNames: [
2850
+ "dx-expander",
2851
+ span,
2852
+ styles.viewport()
2853
+ ] }),
2854
+ gutter,
2855
+ ref: forwardedRef,
2856
+ children: /* @__PURE__ */ jsx(ScrollArea.Root, {
2857
+ orientation: "vertical",
2858
+ centered: true,
2859
+ padding: true,
2860
+ thin: true,
2861
+ children: /* @__PURE__ */ jsx(ScrollArea.Viewport, { children })
2862
+ })
2863
+ });
2864
+ return /* @__PURE__ */ jsx(Column.Root, {
2865
+ ...composableProps(props, { classNames: [
2866
+ "w-full min-w-0",
2867
+ span,
2868
+ styles.viewport()
2869
+ ] }),
2870
+ gutter,
2871
+ ref: forwardedRef,
2872
+ children
2873
+ });
2874
+ });
2875
+ FormViewport.displayName = FORM_VIEWPORT_NAME;
2876
+ var FORM_CONTENT_NAME = "Form.Content";
2877
+ var FormContent = composable(({ children, ...props }, forwardedRef) => {
2878
+ const { form, testId, variant = "default" } = useFormContext(FORM_CONTENT_NAME);
2879
+ const styles = formTheme.styles({ variant });
2880
+ const localRef = useRef(null);
2881
+ const mergedRef = useMergeRefs([forwardedRef, localRef]);
2882
+ useKeyHandler(localRef, form);
2883
+ return /* @__PURE__ */ jsx("div", {
2884
+ ...composableProps(props, {
2885
+ role: "form",
2886
+ classNames: mx(withColumn.center(), "flex flex-col w-full dx-document", styles.content())
2887
+ }),
2888
+ "data-testid": testId,
2889
+ ref: mergedRef,
2890
+ children
2891
+ });
2892
+ });
2893
+ FormContent.displayName = FORM_CONTENT_NAME;
2894
+ var FORM_FIELDSET_NAME = "Form.FieldSet";
2895
+ /** Context-reading binding for `Form.FieldSet`: pulls the schema + field context off the form and delegates to {@link FormFieldSet}. */
2896
+ var FormFieldSetContainer$1 = ({ classNames, ...props }) => {
2897
+ const { form, variant = "default", ...contextProps } = useFormContext(FORM_FIELDSET_NAME);
2898
+ const styles = formTheme.styles({ variant });
2899
+ return /* @__PURE__ */ jsx(FormFieldSet, {
2900
+ schema: form.schema,
2901
+ classNames: styles.fieldSet({ class: classNames }),
2902
+ ...contextProps,
2903
+ ...props
2904
+ });
2905
+ };
2906
+ FormFieldSetContainer$1.displayName = FORM_FIELDSET_NAME;
2907
+ var FORM_LAYOUT_NAME = "Form.Layout";
2908
+ /** Context-reading binding for `Form.Layout`: resolves the schema (prop or form) and delegates to {@link FormLayout}. */
2909
+ var FormLayoutController = ({ schema, ...props }) => {
2910
+ const { form, ...contextProps } = useFormContext(FORM_LAYOUT_NAME);
2911
+ const resolvedSchema = schema ?? form.schema;
2912
+ if (!resolvedSchema) return null;
2913
+ return /* @__PURE__ */ jsx(FormLayout, {
2914
+ schema: resolvedSchema,
2915
+ ...contextProps,
2916
+ ...props
2917
+ });
2918
+ };
2919
+ FormLayoutController.displayName = FORM_LAYOUT_NAME;
2920
+ var FORM_ACTIONS_NAME = "Form.Actions";
2921
+ var FormActions = ({ classNames }) => {
2922
+ const { t } = useTranslation(translationKey);
2923
+ const { form: { canSave, onSave, onCancel }, readonly, layout } = useFormContext(FORM_ACTIONS_NAME);
2924
+ if (readonly || layout === "static") return null;
2925
+ return /* @__PURE__ */ jsxs("div", {
2926
+ className: mx(withColumn.center(), formTheme.styles().actions(), classNames),
2927
+ children: [onCancel && /* @__PURE__ */ jsx(IconButton, {
2928
+ icon: "ph--x--regular",
2929
+ iconEnd: true,
2930
+ label: t("cancel-button.label"),
2931
+ onClick: onCancel,
2932
+ "data-testid": "cancel-button"
2933
+ }), onSave && /* @__PURE__ */ jsx(IconButton, {
2934
+ type: "submit",
2935
+ variant: "primary",
2936
+ disabled: !canSave,
2937
+ icon: "ph--check--regular",
2938
+ iconEnd: true,
2939
+ label: t("save-button.label"),
2940
+ onClick: onSave,
2941
+ "data-testid": "save-button"
2942
+ })]
2943
+ });
2944
+ };
2945
+ FormActions.displayName = FORM_ACTIONS_NAME;
2946
+ var FORM_SECTION_NAME = "Form.Section";
2947
+ var FormSection = composable(({ children, title, description, ...props }, forwardedRef) => {
2948
+ const { variant = "default" } = useFormContext(FORM_SECTION_NAME);
2949
+ const styles = formTheme.styles({ variant });
2950
+ return /* @__PURE__ */ jsxs("div", {
2951
+ ...composableProps(props, { classNames: styles.section() }),
2952
+ ref: forwardedRef,
2953
+ children: [(title || description) && /* @__PURE__ */ jsxs("div", {
2954
+ className: styles.sectionHeader(),
2955
+ children: [title && /* @__PURE__ */ jsx("h2", {
2956
+ className: styles.sectionTitle(),
2957
+ children: title
2958
+ }), description && /* @__PURE__ */ jsx(MarkdownView, {
2959
+ classNames: styles.sectionDescription(),
2960
+ content: description
2961
+ })]
2962
+ }), children]
2963
+ });
2964
+ });
2965
+ FormSection.displayName = FORM_SECTION_NAME;
2966
+ var FORM_GROUP_NAME = "Form.Group";
2967
+ /**
2968
+ * A bordered card grouping related rows/controls (e.g. an entity card in a list).
2969
+ * Layout-only and context-free — unlike `Form.FieldSet` it is not schema-driven.
2970
+ */
2971
+ var FormGroup = composable(({ children, ...props }, forwardedRef) => {
2972
+ const { variant = "default" } = useFormContext(FORM_SECTION_NAME);
2973
+ return /* @__PURE__ */ jsx("div", {
2974
+ ...composableProps(props, { classNames: formTheme.styles({ variant }).group() }),
2975
+ ref: forwardedRef,
2976
+ children
2977
+ });
2978
+ });
2979
+ FormGroup.displayName = FORM_GROUP_NAME;
2980
+ var FORM_SUBMIT_NAME = "Form.Submit";
2981
+ var FormSubmit = ({ classNames, label, icon, disabled }) => {
2982
+ const { t } = useTranslation(translationKey);
2983
+ const { form: { canSave, onSave }, readonly, layout } = useFormContext(FORM_SUBMIT_NAME);
2984
+ if (readonly || layout === "static") return null;
2985
+ return /* @__PURE__ */ jsx("div", {
2986
+ className: mx(formTheme.styles().submit(), classNames),
2987
+ children: /* @__PURE__ */ jsx(IconButton, {
2988
+ classNames: "w-full",
2989
+ type: "submit",
2990
+ variant: "primary",
2991
+ disabled: disabled ?? !canSave,
2992
+ icon: icon ?? "ph--check--regular",
2993
+ label: label ?? t("save-button.label"),
2994
+ onClick: onSave,
2995
+ "data-testid": "save-button"
2996
+ })
2997
+ });
2998
+ };
2999
+ FormSubmit.displayName = FORM_SUBMIT_NAME;
3000
+ var FORM_ERROR_NAME = "Form.Error";
3001
+ /** Form-level error/validation message (e.g. a failed submit), styled via the error valence. */
3002
+ var FormError = ({ children, classNames }) => {
3003
+ if (!children) return null;
3004
+ return /* @__PURE__ */ jsx(Input.Root, {
3005
+ validationValence: "error",
3006
+ children: /* @__PURE__ */ jsx(Input.Validation, {
3007
+ classNames,
3008
+ children
3009
+ })
3010
+ });
3011
+ };
3012
+ FormError.displayName = FORM_ERROR_NAME;
3013
+ //#endregion
3014
+ //#region src/components/Form/Form.tsx
3015
+ var Form = {
3016
+ Root: FormRoot,
3017
+ Viewport: FormViewport,
3018
+ Content: FormContent,
3019
+ Section: FormSection,
3020
+ Group: FormGroup,
3021
+ FieldSet: FormFieldSetContainer$1,
3022
+ Layout: FormLayoutController,
3023
+ Label: FormFieldLabel,
3024
+ Row: FormRow,
3025
+ Actions: FormActions,
3026
+ Submit: FormSubmit,
3027
+ Error: FormError
3028
+ };
3029
+ //#endregion
3030
+ //#region src/components/FieldEditor/FieldEditor.tsx
3031
+ var __dxlog_file$3 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
3032
+ /**
3033
+ * Displays a Form representing the metadata for a `Field` within a given `View`.
3034
+ */
3035
+ var FieldEditor = ({ readonly, projection, field, registry, view, onSave, onCancel }) => {
3036
+ const { t } = useTranslation(translationKey);
3037
+ const [props, setProps] = useState(projection.getFieldProjection(field.id).props);
3038
+ useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
3039
+ const [schemas, setSchemas] = useState([]);
3040
+ useAsyncEffect(async () => {
3041
+ if (!registry) return;
3042
+ const subscription = registry.query(Filter.type(Type.Type)).subscribe((query) => setSchemas(query.results), { fire: true });
3043
+ const schemas = await registry.query(Filter.type(Type.Type)).run();
3044
+ setSchemas(schemas);
3045
+ return () => subscription?.();
3046
+ }, [registry]);
3047
+ const [referenceSchema, setReferenceSchema] = useState();
3048
+ useEffect(() => {
3049
+ setReferenceSchema(() => schemas.find((schema) => Type.getTypename(schema) === props?.referenceSchema));
3050
+ }, [schemas, props?.referenceSchema]);
3051
+ const [{ fieldSchema }, setFieldSchema] = useState({ fieldSchema: getFormatSchema(props?.format) });
3052
+ const fieldMap = useMemo(() => ({
3053
+ ["format"]: (props) => /* @__PURE__ */ jsx(SelectField, {
3054
+ ...props,
3055
+ options: FormatEnums.filter((value) => value !== Format$1.TypeFormat.None).map((value) => ({
3056
+ value,
3057
+ label: t(`format.${value}.label`)
3058
+ }))
3059
+ }),
3060
+ ["referenceSchema"]: (props) => /* @__PURE__ */ jsx(SelectField, {
3061
+ ...props,
3062
+ options: schemas.map((schema) => Type.getTypename(schema)).filter((typename) => typename != null).map((typename) => ({ value: typename }))
3063
+ }),
3064
+ ["referencePath"]: (props) => /* @__PURE__ */ jsx(SelectField, {
3065
+ ...props,
3066
+ options: referenceSchema ? getFormProperties(Type.getSchema(referenceSchema).ast).sort((a, b) => a.name.toString().localeCompare(b.name.toString())).map((p) => ({ value: p.name.toString() })) : []
3067
+ }),
3068
+ ["options"]: (props) => /* @__PURE__ */ jsx(SelectOptionField, { ...props })
3069
+ }), [
3070
+ t,
3071
+ schemas,
3072
+ referenceSchema
3073
+ ]);
3074
+ const propIsNotType = useCallback((props) => props.filter((prop) => prop.name !== "type"), []);
3075
+ const handleValuesChanged = useCallback((_props) => {
3076
+ setFieldSchema((prev) => {
3077
+ const fieldSchema = getFormatSchema(_props.format);
3078
+ if (prev.fieldSchema === fieldSchema) return prev;
3079
+ return { fieldSchema };
3080
+ });
3081
+ setReferenceSchema((prev) => {
3082
+ if (_props.referenceSchema !== (prev ? Type.getTypename(prev) : void 0)) {
3083
+ const newSchema = schemas.find((schema) => Type.getTypename(schema) === _props.referenceSchema);
3084
+ if (newSchema) return newSchema;
3085
+ }
3086
+ return prev;
3087
+ });
3088
+ setProps((props) => {
3089
+ const type = formatToType[_props.format];
3090
+ const additionalProps = formatToAdditionalPropertyAttributes[_props.format];
3091
+ if (props.type !== type) return {
3092
+ ...props,
3093
+ ..._props,
3094
+ ...additionalProps,
3095
+ type
3096
+ };
3097
+ return _props;
3098
+ });
3099
+ }, [
3100
+ schemas,
3101
+ props.format,
3102
+ props.referenceSchema
3103
+ ]);
3104
+ const handleValidate = useCallback(({ property }) => {
3105
+ if (property && projection.getFields().find((f) => f.path === property && f.path !== field.path)) return [{
3106
+ path: "property",
3107
+ message: `property is not unique: '${property}'`
3108
+ }];
3109
+ }, [projection.getFields(), field]);
3110
+ const handleSave = useCallback((props) => {
3111
+ if (view) Obj.update(view, () => {
3112
+ projection.setFieldProjection({
3113
+ field,
3114
+ props
3115
+ });
3116
+ });
3117
+ else projection.setFieldProjection({
3118
+ field,
3119
+ props
3120
+ });
3121
+ onSave();
3122
+ }, [
3123
+ projection,
3124
+ field,
3125
+ view,
3126
+ onSave
3127
+ ]);
3128
+ const handleCancel = useCallback(() => {
3129
+ onSave();
3130
+ requestAnimationFrame(() => onCancel?.());
3131
+ }, [onSave]);
3132
+ if (!fieldSchema) {
3133
+ log.warn("invalid format", { props }, {
3134
+ "~LogMeta": "~LogMeta",
3135
+ F: __dxlog_file$3,
3136
+ L: 185,
3137
+ S: void 0
3138
+ });
3139
+ return null;
3140
+ }
3141
+ return /* @__PURE__ */ jsx(Form.Root, {
3142
+ fieldMap,
3143
+ autoFocus: true,
3144
+ readonly,
3145
+ schema: fieldSchema,
3146
+ values: props,
3147
+ filter: propIsNotType,
3148
+ sort: ["property", "format"],
3149
+ onValuesChanged: handleValuesChanged,
3150
+ onValidate: handleValidate,
3151
+ onSave: handleSave,
3152
+ onCancel: handleCancel,
3153
+ children: /* @__PURE__ */ jsxs(Form.Content, { children: [/* @__PURE__ */ jsx(Form.FieldSet, {}), /* @__PURE__ */ jsx(Form.Actions, {})] })
3154
+ }, field.id);
3155
+ };
3156
+ //#endregion
3157
+ //#region src/components/ObjectForm/ObjectForm.tsx
3158
+ var __dxlog_file$2 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ObjectForm/ObjectForm.tsx";
3159
+ var ObjectForm = ({ object, type, schema, showTags = true }) => {
3160
+ const db = Obj.getDatabase(object);
3161
+ const [snapshot] = useObject(object);
3162
+ const tags = [...Obj.getMeta(object).tags];
3163
+ const values = useMemo(() => ({
3164
+ [META_TAGS_KEY]: tags,
3165
+ ...snapshot
3166
+ }), [snapshot, tags]);
3167
+ const formSchema = useMemo(() => {
3168
+ const base = schema ?? Type.getSchema(type);
3169
+ return showTags ? withMetaTags(base) : omitId(base);
3170
+ }, [
3171
+ schema,
3172
+ type,
3173
+ showTags
3174
+ ]);
3175
+ const handleCreate = useCallback((type, values) => {
3176
+ invariant(db, void 0, {
3177
+ "~LogMeta": "~LogMeta",
3178
+ F: __dxlog_file$2,
3179
+ L: 49,
3180
+ S: void 0,
3181
+ A: ["db", ""]
3182
+ });
3183
+ invariant(Type.isObject(type), void 0, {
3184
+ "~LogMeta": "~LogMeta",
3185
+ F: __dxlog_file$2,
3186
+ L: 50,
3187
+ S: void 0,
3188
+ A: ["Type.isObject(type)", ""]
3189
+ });
3190
+ const newObject = db.add(Obj.make(type, values));
3191
+ if (Obj.instanceOf(Tag.Tag, newObject)) Obj.update(object, (object) => {
3192
+ Obj.getMeta(object).tags = [...Obj.getMeta(object).tags, Ref.make(newObject)];
3193
+ });
3194
+ }, []);
3195
+ const handleChange = useCallback(({ [META_TAGS_KEY]: metaTags, ...values }, { isValid, changed }) => {
3196
+ if (!isValid) return;
3197
+ const changedPaths = Object.keys(changed).filter((path) => changed[path]);
3198
+ if (changedPaths.some((path) => SchemaEx.splitJsonPath(path)[0] === "_tags")) Obj.update(object, (object) => {
3199
+ Obj.getMeta(object).tags = Array.isArray(metaTags) ? [...metaTags] : [];
3200
+ });
3201
+ const nonTagPaths = changedPaths.filter((path) => SchemaEx.splitJsonPath(path)[0] !== META_TAGS_KEY);
3202
+ if (nonTagPaths.length > 0) Obj.update(object, () => {
3203
+ for (const path of nonTagPaths) {
3204
+ const parts = SchemaEx.splitJsonPath(path);
3205
+ const value = Obj.getValue(values, parts);
3206
+ Obj.setValue(object, parts, value);
3207
+ }
3208
+ });
3209
+ }, [object]);
3210
+ return /* @__PURE__ */ jsx(Form.Root, {
3211
+ schema: formSchema,
3212
+ values,
3213
+ createTypename: Type.getTypename(Tag.Tag),
3214
+ createOptionIcon: "ph--plus--regular",
3215
+ createOptionLabel: ["add-tag.label", { ns: translationKey }],
3216
+ createInitialValuePath: "label",
3217
+ createFieldMap: createFieldMap$1,
3218
+ db,
3219
+ onValuesChanged: handleChange,
3220
+ onCreate: handleCreate,
3221
+ children: /* @__PURE__ */ jsx(Form.Viewport, { children: /* @__PURE__ */ jsx(Form.Content, { children: /* @__PURE__ */ jsx(Form.FieldSet, {}) }) })
3222
+ });
3223
+ };
3224
+ var createFieldMap$1 = { hue: ({ type, label, presentation, getValue, onValueChange }) => {
3225
+ const handleChange = useCallback((nextHue) => onValueChange(type, nextHue), [onValueChange, type]);
3226
+ const handleReset = useCallback(() => onValueChange(type, void 0), [onValueChange, type]);
3227
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [presentation !== "inline" && /* @__PURE__ */ jsx(Form.Label, { label }), /* @__PURE__ */ jsx(HuePicker, {
3228
+ value: getValue(),
3229
+ onChange: handleChange,
3230
+ onReset: handleReset
3231
+ })] });
3232
+ } };
3233
+ //#endregion
3234
+ //#region src/components/ObjectProperties/ObjectProperties.tsx
3235
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ObjectProperties/ObjectProperties.tsx";
3236
+ var ObjectProperties = composable(({ children, object, getCreateDefaults, resolveCreateEntry, ...props }, forwardedRef) => {
3237
+ const db = Obj.getDatabase(object);
3238
+ const tags = [...Obj.getMeta(object).tags];
3239
+ const values = useMemo(() => ({
3240
+ [META_TAGS_KEY]: tags,
3241
+ ...object
3242
+ }), [object, tags]);
3243
+ const typeFromRegistry = useType(db, Obj.getTypeURI(object));
3244
+ const type = Obj.getType(object) ?? typeFromRegistry;
3245
+ const formSchema = useMemo(() => {
3246
+ return Function.pipe(type, Option.fromNullable, Option.map((type) => Type.getSchema(type)), Option.map((schema) => withMetaTags(schema)), Option.getOrUndefined);
3247
+ }, [type]);
3248
+ const handleCreate = useCallback((type, values) => {
3249
+ invariant(db, void 0, {
3250
+ "~LogMeta": "~LogMeta",
3251
+ F: __dxlog_file$1,
3252
+ L: 69,
3253
+ S: void 0,
3254
+ A: ["db", ""]
3255
+ });
3256
+ invariant(Type.isObject(type), void 0, {
3257
+ "~LogMeta": "~LogMeta",
3258
+ F: __dxlog_file$1,
3259
+ L: 70,
3260
+ S: void 0,
3261
+ A: ["Type.isObject(type)", ""]
3262
+ });
3263
+ const factory = Option.getOrUndefined(FactoryAnnotation.get(Type.getSchema(type)));
3264
+ const newObject = factory ? factory(values) : Obj.make(type, values);
3265
+ return db.add(newObject);
3266
+ }, [db]);
3267
+ const handleChange = useCallback(({ [META_TAGS_KEY]: metaTags, ...values }, { isValid, changed }) => {
3268
+ if (!isValid) return;
3269
+ const changedPaths = Object.keys(changed).filter((path) => changed[path]);
3270
+ if (changedPaths.some((path) => SchemaEx.splitJsonPath(path)[0] === "_tags")) Obj.update(object, (object) => {
3271
+ Obj.getMeta(object).tags = Array.isArray(metaTags) ? [...metaTags] : [];
3272
+ });
3273
+ const nonTagPaths = changedPaths.filter((path) => SchemaEx.splitJsonPath(path)[0] !== META_TAGS_KEY);
3274
+ if (nonTagPaths.length > 0) Obj.update(object, () => {
3275
+ for (const path of nonTagPaths) {
3276
+ const parts = SchemaEx.splitJsonPath(path);
3277
+ const value = Obj.getValue(values, parts);
3278
+ Obj.setValue(object, parts, value);
3279
+ }
3280
+ });
3281
+ }, [object]);
3282
+ if (!formSchema) return null;
3283
+ return /* @__PURE__ */ jsx(Form.Root, {
3284
+ db,
3285
+ schema: formSchema,
3286
+ defaultValues: values,
3287
+ createTypename: Type.getTypename(Tag.Tag),
3288
+ createOptionIcon: "ph--plus--regular",
3289
+ createOptionLabel: ["add-tag.label", { ns: translationKey }],
3290
+ createInitialValuePath: "label",
3291
+ createFieldMap,
3292
+ onValuesChanged: handleChange,
3293
+ onCreate: handleCreate,
3294
+ getCreateDefaults,
3295
+ resolveCreateEntry,
3296
+ children: /* @__PURE__ */ jsx(Form.Viewport, {
3297
+ ...composableProps(props),
3298
+ scroll: true,
3299
+ ref: forwardedRef,
3300
+ children: /* @__PURE__ */ jsxs(Form.Content, { children: [/* @__PURE__ */ jsx(Form.FieldSet, {}), /* @__PURE__ */ jsx(Form.Section, { children })] })
3301
+ })
3302
+ });
3303
+ });
3304
+ var createFieldMap = { hue: ({ type, label, presentation, getValue, onValueChange }) => {
3305
+ const handleChange = useCallback((nextHue) => onValueChange(type, nextHue), [onValueChange, type]);
3306
+ const handleReset = useCallback(() => onValueChange(type, void 0), [onValueChange, type]);
3307
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [presentation !== "inline" && /* @__PURE__ */ jsx(Form.Label, { label }), /* @__PURE__ */ jsx(HuePicker, {
3308
+ value: getValue(),
3309
+ onChange: handleChange,
3310
+ onReset: handleReset
3311
+ })] });
3312
+ } };
3313
+ //#endregion
3314
+ //#region src/components/ObjectTree/ObjectTree.tsx
3315
+ /**
3316
+ * Render a value as a sequence of key/value rows whose shape and order are
3317
+ * driven by `schema`. Object-typed properties become a header row whose
3318
+ * children are indented under it; arrays expand element-by-element.
3319
+ *
3320
+ * Composable: spreads unknown props (incl. `className` / `classNames` /
3321
+ * `style` / `role`) onto its root `<div>` and forwards its ref, so it can
3322
+ * be slotted into themed parents like `Panel.Content`.
3323
+ */
3324
+ var ObjectTreeImpl = composable(({ schema, value, ...props }, forwardedRef) => /* @__PURE__ */ jsx("div", {
3325
+ ...composableProps(props, {
3326
+ role: "table",
3327
+ classNames: "font-mono text-sm w-full"
3328
+ }),
3329
+ ref: forwardedRef,
3330
+ children: /* @__PURE__ */ jsx(Node, {
3331
+ ast: schema.ast,
3332
+ value,
3333
+ label: null,
3334
+ depth: 0
3335
+ })
3336
+ }));
3337
+ ObjectTreeImpl.displayName = "ObjectTree";
3338
+ var ObjectTree = ObjectTreeImpl;
3339
+ /**
3340
+ * Peel wrappers that don't affect the runtime shape: Refinements pass their
3341
+ * `from`, Suspends resolve via `f()`, and Transformations expose the encoded
3342
+ * (input) side. Repeated unwrapping handles chains like `Schema.suspend ->
3343
+ * Refinement -> TypeLiteral`.
3344
+ */
3345
+ var unwrap = (ast) => {
3346
+ if (SchemaAST.isRefinement(ast)) return unwrap(ast.from);
3347
+ if (SchemaAST.isSuspend(ast)) return unwrap(ast.f());
3348
+ if (SchemaAST.isTransformation(ast)) return unwrap(ast.from);
3349
+ return ast;
3350
+ };
3351
+ /**
3352
+ * Effect models `Schema.optional(X)` as a property whose type AST is
3353
+ * `Union(X, UndefinedKeyword)`. For walking purposes we want X -- the
3354
+ * undefined branch carries no schema information for the viewer.
3355
+ */
3356
+ var stripOptional = (ast) => {
3357
+ if (!SchemaAST.isUnion(ast)) return ast;
3358
+ const nonUndef = ast.types.filter((t) => !SchemaAST.isUndefinedKeyword(t));
3359
+ return nonUndef.length === 1 ? nonUndef[0] : ast;
3360
+ };
3361
+ var Node = ({ ast, value, label, depth }) => {
3362
+ const inner = unwrap(stripOptional(ast));
3363
+ if (SchemaAST.isTypeLiteral(inner) && isPlainObject(value)) {
3364
+ const childDepth = label === null ? depth : depth + 1;
3365
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [label !== null && /* @__PURE__ */ jsx(Row, {
3366
+ label,
3367
+ value: null,
3368
+ depth,
3369
+ kind: "group"
3370
+ }), inner.propertySignatures.map((prop) => /* @__PURE__ */ jsx(Node, {
3371
+ ast: prop.type,
3372
+ value: value[String(prop.name)],
3373
+ label: String(prop.name),
3374
+ depth: childDepth
3375
+ }, String(prop.name)))] });
3376
+ }
3377
+ if (SchemaAST.isTupleType(inner) && Array.isArray(value)) {
3378
+ const elemType = inner.rest[0]?.type;
3379
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [label !== null && /* @__PURE__ */ jsx(Row, {
3380
+ label,
3381
+ value: `Array(${value.length})`,
3382
+ depth,
3383
+ kind: "group"
3384
+ }), value.map((item, index) => /* @__PURE__ */ jsx(Node, {
3385
+ ast: elemType ?? SchemaAST.unknownKeyword,
3386
+ value: item,
3387
+ label: `[${index}]`,
3388
+ depth: depth + 1
3389
+ }, index))] });
3390
+ }
3391
+ return /* @__PURE__ */ jsx(Row, {
3392
+ label: label ?? "",
3393
+ value: formatLeaf(value),
3394
+ depth,
3395
+ kind: "leaf"
3396
+ });
3397
+ };
3398
+ var Row = ({ label, value, depth }) => /* @__PURE__ */ jsxs("div", {
3399
+ role: "row",
3400
+ className: mx("flex items-baseline gap-3 py-1"),
3401
+ style: { paddingInlineStart: 8 + depth * 16 },
3402
+ children: [/* @__PURE__ */ jsx("div", {
3403
+ role: "cell",
3404
+ className: "text-subdued",
3405
+ children: label
3406
+ }), /* @__PURE__ */ jsx("div", {
3407
+ role: "cell",
3408
+ className: mx("break-all text-base-fg"),
3409
+ children: value
3410
+ })]
3411
+ });
3412
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3413
+ /**
3414
+ * Format a leaf value for display. Strings are quoted so empty strings are
3415
+ * visible; missing values surface as an em-dash so the row stays present.
3416
+ */
3417
+ var formatLeaf = (value) => {
3418
+ if (value === void 0) return null;
3419
+ if (value === null) return /* @__PURE__ */ jsx("span", {
3420
+ className: "text-description",
3421
+ children: "null"
3422
+ });
3423
+ switch (typeof value) {
3424
+ case "string": return /* @__PURE__ */ jsx("span", {
3425
+ className: "text-blue-text",
3426
+ children: JSON.stringify(value)
3427
+ });
3428
+ case "number":
3429
+ case "bigint": return /* @__PURE__ */ jsx("span", {
3430
+ className: "text-green-text",
3431
+ children: String(value)
3432
+ });
3433
+ case "boolean": return /* @__PURE__ */ jsx("span", {
3434
+ className: "text-purple-text",
3435
+ children: String(value)
3436
+ });
3437
+ default: try {
3438
+ return JSON.stringify(value);
3439
+ } catch {
3440
+ return String(value);
3441
+ }
3442
+ }
3443
+ };
3444
+ //#endregion
3445
+ //#region src/components/RefEditor/ref-editor-extension.ts
3446
+ /** Matches an object reference token (`@<object id>`). */
3447
+ var REF_REGEX = /^@([A-Za-z0-9]+)$/;
3448
+ /** Matches a well-formed email address token. */
3449
+ var EMAIL_REGEX = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
3450
+ /** Matches an RFC 5322 name-addr (`Name <email>`); group 1 = display name, group 2 = addr-spec. */
3451
+ var NAME_ADDR_REGEX = /^(.*?)\s*<([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})>$/;
3452
+ /** Formats an RFC 5322 mailbox (`Name <email>`). */
3453
+ var formatMailbox = (name, email) => name ? `${name} <${email}>` : email;
3454
+ /**
3455
+ * Effect dispatched when reference data changes out-of-band (e.g. objects finish loading) so that
3456
+ * already-rendered reference decorations re-resolve without a document change.
3457
+ */
3458
+ var refEditorRedecorate = StateEffect.define();
3459
+ /**
3460
+ * CodeMirror extension rendering reference-editor tokens as tags. Tags are atomic: the cursor
3461
+ * cannot enter them and deletion removes the whole token.
3462
+ * - `'ref'` mode: `@<id>` references resolve to the object's label (via
3463
+ * {@link RefEditorExtensionOptions.getRef}); tokens accepted by the pluggable `match` regexp
3464
+ * render as neutral tags.
3465
+ * - `'email'` mode: RFC 5322 mailboxes render as tags labelled with the display name (falling
3466
+ * back to the address); the trailing comma separator is hidden inside the tag's atomic range.
3467
+ */
3468
+ var refEditor = ({ mode = "ref", getRef, match, colorize } = {}) => {
3469
+ let decoField;
3470
+ /** Delete the tag whose delete affordance (`node`) was clicked, keeping the delimited list well-formed. */
3471
+ const removeToken = (view, node) => {
3472
+ const index = [...view.contentDOM.querySelectorAll("button[aria-label=\"Remove\"]")].indexOf(node);
3473
+ if (index < 0) return;
3474
+ const cursor = view.state.field(decoField).iter();
3475
+ let range;
3476
+ for (let current = 0; cursor.value; current++, cursor.next()) if (current === index) {
3477
+ range = {
3478
+ from: cursor.from,
3479
+ to: cursor.to
3480
+ };
3481
+ break;
3482
+ }
3483
+ if (!range) return;
3484
+ const doc = view.state.doc.toString();
3485
+ let { from } = range;
3486
+ const { to } = range;
3487
+ if (mode === "email" && !/,\s*$/.test(doc.slice(from, to))) {
3488
+ const dangling = doc.slice(0, from).match(/,\s*$/);
3489
+ if (dangling) from -= dangling[0].length;
3490
+ }
3491
+ view.dispatch({
3492
+ changes: {
3493
+ from,
3494
+ to
3495
+ },
3496
+ selection: { anchor: from }
3497
+ });
3498
+ };
3499
+ const buildRefDecorations = (state) => {
3500
+ const deco = new RangeSetBuilder();
3501
+ const text = state.doc.toString();
3502
+ const tokenRegex = /\S+/g;
3503
+ let tokenMatch;
3504
+ while (tokenMatch = tokenRegex.exec(text)) {
3505
+ const token = tokenMatch[0];
3506
+ const from = tokenMatch.index;
3507
+ const to = from + token.length;
3508
+ const refMatch = token.match(REF_REGEX);
3509
+ if (refMatch) {
3510
+ const ref = getRef?.(refMatch[1]);
3511
+ if (ref) deco.add(from, to, Decoration.replace({ widget: new RefWidget(ref.label, ref.hue ?? (colorize ? getHashHue(refMatch[1]) : void 0), removeToken) }));
3512
+ continue;
3513
+ }
3514
+ if (match?.test(token)) deco.add(from, to, Decoration.replace({ widget: new RefWidget(token, void 0, removeToken) }));
3515
+ }
3516
+ return deco.finish();
3517
+ };
3518
+ const buildEmailDecorations = (state) => {
3519
+ const addrRegex = match ?? EMAIL_REGEX;
3520
+ const deco = new RangeSetBuilder();
3521
+ const text = state.doc.toString();
3522
+ const segmentRegex = /[^,]+(?:,\s*)?|,\s*/g;
3523
+ let segmentMatch;
3524
+ while (segmentMatch = segmentRegex.exec(text)) {
3525
+ const segment = segmentMatch[0];
3526
+ const commaIndex = segment.indexOf(",");
3527
+ const content = (commaIndex === -1 ? segment : segment.slice(0, commaIndex)).trim();
3528
+ if (!content) continue;
3529
+ if (commaIndex === -1) continue;
3530
+ const leading = segment.length - segment.trimStart().length;
3531
+ const from = segmentMatch.index + leading;
3532
+ const to = segmentMatch.index + segment.length;
3533
+ const nameAddr = content.match(NAME_ADDR_REGEX);
3534
+ if (nameAddr) deco.add(from, to, Decoration.replace({ widget: new RefWidget(nameAddr[1] || nameAddr[2], colorize ? getHashHue(nameAddr[2]) : void 0, removeToken) }));
3535
+ else if (addrRegex.test(content)) deco.add(from, to, Decoration.replace({ widget: new RefWidget(content, void 0, removeToken) }));
3536
+ }
3537
+ return deco.finish();
3538
+ };
3539
+ const buildDecorations = mode === "email" ? buildEmailDecorations : buildRefDecorations;
3540
+ decoField = StateField.define({
3541
+ create: (state) => buildDecorations(state),
3542
+ update: (deco, tr) => {
3543
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(refEditorRedecorate))) return buildDecorations(tr.state);
3544
+ return deco;
3545
+ },
3546
+ provide: (field) => [EditorView.decorations.from(field), EditorView.atomicRanges.of((view) => view.state.field(field))]
3547
+ });
3548
+ const separator = mode === "email" ? ", " : " ";
3549
+ const separatorFilter = EditorState.transactionFilter.of((tr) => {
3550
+ if (!tr.docChanged || !tr.isUserEvent("input")) return tr;
3551
+ const tags = tr.startState.field(decoField, false);
3552
+ if (!tags) return tr;
3553
+ let extra;
3554
+ tr.changes.iterChanges((fromA, toA, _fromB, toB, inserted) => {
3555
+ if (extra || fromA !== toA) return;
3556
+ const text = inserted.toString();
3557
+ if (!text || /[\s,]$/.test(text)) return;
3558
+ let abutsTag = false;
3559
+ for (const cursor = tags.iter(); cursor.value; cursor.next()) {
3560
+ if (cursor.from === fromA) {
3561
+ abutsTag = true;
3562
+ break;
3563
+ }
3564
+ if (cursor.from > fromA) break;
3565
+ }
3566
+ if (abutsTag) extra = {
3567
+ changes: {
3568
+ from: toB,
3569
+ insert: separator
3570
+ },
3571
+ sequential: true,
3572
+ selection: { anchor: toB }
3573
+ };
3574
+ });
3575
+ return extra ? [tr, extra] : tr;
3576
+ });
3577
+ return [
3578
+ styles,
3579
+ decoField,
3580
+ separatorFilter
3581
+ ];
3582
+ };
3583
+ /**
3584
+ * The outer span vertically aligns the tag with the surrounding line and adds a trailing gap so
3585
+ * adjacent tags don't touch. The inner span carries the border; `overflow-hidden` clips the child
3586
+ * fills to its radius (a square-cornered fill otherwise peeks past the rounded border).
3587
+ */
3588
+ var container = (classNames, ...children) => {
3589
+ const inner = Domino.of("span").classNames(mx("inline-flex items-stretch border rounded-xs overflow-hidden", classNames)).append(...children);
3590
+ return Domino.of("span").classNames("inline-flex align-middle pe-1").append(inner).root;
3591
+ };
3592
+ /** Trailing `x` affordance that deletes the tag it belongs to. */
3593
+ var deleteButton = (view, remove) => {
3594
+ const button = Domino.of("button").classNames("flex items-center px-1 text-description hover:text-error cursor-pointer").attributes({
3595
+ "type": "button",
3596
+ "aria-label": "Remove"
3597
+ }).append(Domino.svg("ph--x--bold").classNames("shrink-0 w-3.5 h-3.5")).on("mousedown", (event) => {
3598
+ event.preventDefault();
3599
+ remove(view, button.root);
3600
+ });
3601
+ return button;
3602
+ };
3603
+ /**
3604
+ * Resolved reference (object reference or RFC 5322 mailbox), rendered as the object's label with a
3605
+ * trailing delete affordance.
3606
+ */
3607
+ var RefWidget = class extends WidgetType {
3608
+ _label;
3609
+ _hue;
3610
+ _remove;
3611
+ constructor(_label, _hue, _remove) {
3612
+ super();
3613
+ this._label = _label;
3614
+ this._hue = _hue;
3615
+ this._remove = _remove;
3616
+ }
3617
+ eq(other) {
3618
+ return this._label === other._label && this._hue === other._hue;
3619
+ }
3620
+ toDOM(view) {
3621
+ return container(mx("border-separator rounded-sm", (this._hue ? getStyles(this._hue) : void 0)?.border), Domino.of("span").classNames("flex items-center ps-2 pe-1").text(this._label), deleteButton(view, this._remove));
3622
+ }
3623
+ };
3624
+ var styles = EditorView.theme({ ".cm-line": {
3625
+ padding: "0",
3626
+ display: "flex",
3627
+ alignItems: "center"
3628
+ } });
3629
+ //#endregion
3630
+ //#region src/components/RefEditor/RefEditor.tsx
3631
+ /**
3632
+ * Single-line input for a list of object references. Typing `@` (or any text, with
3633
+ * `activateOnTyping`) opens a typeahead menu matching objects of the given type by label (and
3634
+ * by their `getValues` values). The content model depends on {@link RefEditorProps.mode}.
3635
+ * Resolved entries render as atomic tags with the object's label; raw tokens accepted by the
3636
+ * pluggable `match` regexp render as neutral tags.
3637
+ */
3638
+ var RefEditor = forwardRef(({ db, type, value, readonly, mode = "ref", match, getLabel, getValues, icon = "ph--link--regular", placeholder: placeholderProp, activateOnTyping, numItems = 8, classNames, ...props }, forwardedRef) => {
3639
+ const { t } = useTranslation(translationKey);
3640
+ const { themeMode } = useThemeContext();
3641
+ const getObjectLabel = useCallback((object) => getLabel?.(object) ?? Obj.getLabel(object) ?? object.id, [getLabel]);
3642
+ const objects = useQuery(db, useMemo(() => Filter.type(type), [type]));
3643
+ const refs = useMemo(() => new Map(objects.map((object) => [object.id, {
3644
+ label: getObjectLabel(object),
3645
+ hue: getHashHue(object.id)
3646
+ }])), [objects, getObjectLabel]);
3647
+ const refsRef = useRef(refs);
3648
+ refsRef.current = refs;
3649
+ const controllerRef = useRef(null);
3650
+ const composedRef = useCallback((controller) => {
3651
+ controllerRef.current = controller;
3652
+ if (typeof forwardedRef === "function") forwardedRef(controller);
3653
+ else if (forwardedRef) forwardedRef.current = controller;
3654
+ }, [forwardedRef]);
3655
+ useEffect(() => {
3656
+ controllerRef.current?.view?.dispatch({ effects: refEditorRedecorate.of(null) });
3657
+ }, [refs]);
3658
+ const getMenu = useCallback(async ({ text }) => {
3659
+ if (!text) return [];
3660
+ const query = text.replace(/^@/, "").toLowerCase();
3661
+ const matchesQuery = (object) => !query || getObjectLabel(object).toLowerCase().includes(query) || (getValues?.(object) ?? []).some((entry) => entry.toLowerCase().includes(query));
3662
+ if (mode === "email") return [{
3663
+ id: "mailboxes",
3664
+ items: objects.flatMap((object) => (getValues?.(object) ?? []).map((entry) => ({
3665
+ object,
3666
+ entry
3667
+ }))).filter(({ object, entry }) => !query || getObjectLabel(object).toLowerCase().includes(query) || entry.toLowerCase().includes(query)).slice(0, numItems * 2).map(({ object, entry }) => ({
3668
+ id: `${object.id}-${entry}`,
3669
+ label: `${getObjectLabel(object)} (${entry})`,
3670
+ icon,
3671
+ onSelect: ({ view, head }) => insertMailbox(view, head, formatMailbox(getObjectLabel(object), entry))
3672
+ }))
3673
+ }];
3674
+ return [{
3675
+ id: "refs",
3676
+ items: objects.filter(matchesQuery).slice(0, numItems * 2).map((object) => {
3677
+ const label = getObjectLabel(object);
3678
+ const detail = getValues?.(object)?.[0];
3679
+ return {
3680
+ id: object.id,
3681
+ label: detail && detail !== label ? `${label} (${detail})` : label,
3682
+ icon,
3683
+ onSelect: ({ view, head }) => insertAtCursor(view, head, `@${object.id} `)
3684
+ };
3685
+ })
3686
+ }];
3687
+ }, [
3688
+ objects,
3689
+ numItems,
3690
+ mode,
3691
+ icon,
3692
+ getObjectLabel,
3693
+ getValues
3694
+ ]);
3695
+ const placeholder = useMemo(() => ({
3696
+ content: placeholderProp ?? t(activateOnTyping ? "ref-editor-auto.placeholder" : "ref-editor.placeholder"),
3697
+ focusOnly: false
3698
+ }), [
3699
+ t,
3700
+ placeholderProp,
3701
+ activateOnTyping
3702
+ ]);
3703
+ const extensions = useMemo(() => [
3704
+ createBasicExtensions({
3705
+ readOnly: readonly,
3706
+ lineWrapping: false
3707
+ }),
3708
+ createThemeExtensions({
3709
+ themeMode,
3710
+ slots: {
3711
+ editor: { className: "w-full" },
3712
+ scroller: { className: "scrollbar-none" }
3713
+ }
3714
+ }),
3715
+ refEditor({
3716
+ mode,
3717
+ match,
3718
+ getRef: (id) => refsRef.current.get(id)
3719
+ }),
3720
+ Prec.highest(keymap.of([{
3721
+ key: "Enter",
3722
+ run: (view) => {
3723
+ const { head } = view.state.selection.main;
3724
+ const line = view.state.doc.lineAt(head);
3725
+ const before = line.text.slice(0, head - line.from);
3726
+ if (mode === "email") {
3727
+ const segment = before.slice(before.lastIndexOf(",") + 1).trim();
3728
+ if (NAME_ADDR_REGEX.test(segment) || (match ?? EMAIL_REGEX).test(segment)) insertAtCursor(view, head, ", ");
3729
+ } else {
3730
+ const token = before.slice(before.search(/\S+$/));
3731
+ if (token && match?.test(token)) insertAtCursor(view, head, " ");
3732
+ }
3733
+ return true;
3734
+ }
3735
+ }, {
3736
+ key: "Space",
3737
+ run: (view) => {
3738
+ if (mode !== "email") return false;
3739
+ const { head } = view.state.selection.main;
3740
+ const line = view.state.doc.lineAt(head);
3741
+ const before = line.text.slice(0, head - line.from);
3742
+ const segment = before.slice(before.lastIndexOf(",") + 1).trim();
3743
+ if ((match ?? EMAIL_REGEX).test(segment)) {
3744
+ insertAtCursor(view, head, ", ");
3745
+ return true;
3746
+ }
3747
+ return false;
3748
+ }
3749
+ }]))
3750
+ ], [
3751
+ readonly,
3752
+ themeMode,
3753
+ mode,
3754
+ match
3755
+ ]);
3756
+ return /* @__PURE__ */ jsx(Editor.Root, {
3757
+ extensions,
3758
+ numItems,
3759
+ trigger: "@",
3760
+ activateOnTyping,
3761
+ placeholder,
3762
+ filter: false,
3763
+ getMenu,
3764
+ ref: composedRef,
3765
+ children: /* @__PURE__ */ jsx(Editor.View, {
3766
+ ...props,
3767
+ classNames: mx("flex items-center dx-input h-[2rem]", classNames),
3768
+ initialValue: value,
3769
+ selectionEnd: true
3770
+ })
3771
+ });
3772
+ });
3773
+ RefEditor.displayName = "RefEditor";
3774
+ /**
3775
+ * Insert an RFC 5322 mailbox, replacing the in-progress segment (anything typed after the last
3776
+ * comma — multi-word queries can leave words outside the popover's completion range).
3777
+ */
3778
+ var insertMailbox = (view, head, mailbox) => {
3779
+ const line = view.state.doc.lineAt(head);
3780
+ const lastComma = line.text.slice(0, head - line.from).lastIndexOf(",");
3781
+ const from = line.from + (lastComma === -1 ? 0 : lastComma + 1);
3782
+ const insert = `${lastComma === -1 ? "" : " "}${mailbox}, `;
3783
+ view.dispatch({
3784
+ changes: {
3785
+ from,
3786
+ to: head,
3787
+ insert
3788
+ },
3789
+ selection: {
3790
+ anchor: from + insert.length,
3791
+ head: from + insert.length
3792
+ }
3793
+ });
3794
+ };
3795
+ //#endregion
3796
+ //#region src/components/ViewEditor/ViewEditor.tsx
3797
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx";
3798
+ /**
3799
+ * View editor form.
3800
+ */
3801
+ var ViewEditor = forwardRef(({ classNames, type, view, mode = "schema", registry, db, readonly, showHeading = false, types, tags, onQueryChanged, onDelete }, forwardedRef) => {
3802
+ const atomRegistry = useContext(RegistryContext);
3803
+ const schemaReadonly = type == null || Type.getDatabase(type) == null;
3804
+ const { t } = useTranslation(translationKey);
3805
+ const projectionModel = useMemo(() => {
3806
+ if (!type) return null;
3807
+ const jsonSchema = type.jsonSchema;
3808
+ const change = createEchoChangeCallback(view, Type.getDatabase(type) != null ? type : void 0);
3809
+ return new ProjectionModel({
3810
+ registry: atomRegistry,
3811
+ view,
3812
+ baseSchema: jsonSchema,
3813
+ change
3814
+ });
3815
+ }, [
3816
+ atomRegistry,
3817
+ type,
3818
+ view
3819
+ ]);
3820
+ useImperativeHandle(forwardedRef, () => projectionModel, [projectionModel]);
3821
+ const feedTarget = Match.value(view.query.ast).pipe(Match.when({ type: "from" }, ({ from }) => {
3822
+ if (from._tag !== "scope") return;
3823
+ const feedScope = from.scopes.find((s) => s._tag === "feed");
3824
+ return Option.fromNullable(feedScope).pipe(Option.map((s) => s.feedUri), Option.getOrUndefined);
3825
+ }), Match.orElse(() => void 0));
3826
+ const feeds = useQuery(db, Filter.type(Feed.Feed));
3827
+ const targetRef = useMemo(() => {
3828
+ if (!feedTarget) return;
3829
+ const targetEid = EID.tryParse(feedTarget);
3830
+ const feed = feeds.find((feed) => {
3831
+ const feedEid = Feed.getFeedUri(feed);
3832
+ return feedEid != null && targetEid != null && EID.equals(feedEid, targetEid);
3833
+ });
3834
+ return feed ? Ref.fromURI(Entity.getURI(feed)) : void 0;
3835
+ }, [feedTarget, feeds]);
3836
+ const viewSchema = useMemo(() => {
3837
+ const base = Schema.Struct({ query: mode === "schema" ? Format.URL.annotations({ title: "Record type" }) : QueryAST.Query.annotations({ title: "Query" }) });
3838
+ if (mode === "tag") return Schema.Struct({
3839
+ ...base.fields,
3840
+ target: Ref.Ref(Feed.Feed).pipe(Schema.annotations({ title: "Target Feed" }), ParentLabelAnnotation.set(true), Schema.optional)
3841
+ }).pipe(Schema.mutable);
3842
+ return base.pipe(Schema.mutable);
3843
+ }, [mode]);
3844
+ const viewValues = useMemo(() => {
3845
+ const typeUri = getTypeURIFromQuery(view.query.ast);
3846
+ const typeDxn = typeUri ? DXN.tryMake(typeUri) : void 0;
3847
+ return {
3848
+ query: mode === "schema" ? typeDxn ? DXN.getName(typeDxn) : typeUri ?? "" : view.query.ast,
3849
+ target: targetRef
3850
+ };
3851
+ }, [
3852
+ mode,
3853
+ view.query.ast,
3854
+ targetRef
3855
+ ]);
3856
+ const fieldMap = useMemo(() => mode === "tag" ? customFields({
3857
+ types,
3858
+ tags
3859
+ }) : void 0, [
3860
+ mode,
3861
+ types,
3862
+ tags
3863
+ ]);
3864
+ const handleUpdate = useCallback((values) => {
3865
+ const targetValue = values.target;
3866
+ let feedDxn;
3867
+ if (Ref.isRef(targetValue)) {
3868
+ const feedUri = targetValue.uri;
3869
+ const feed = feeds.find((feed) => Obj.getURI(feed) === feedUri);
3870
+ if (feed) feedDxn = Feed.getFeedUri(feed);
3871
+ }
3872
+ const query = mode === "schema" ? Query.select(Filter.type(EID.isEID(values.query) ? values.query : DXN.make(values.query))).ast : JSON.parse(JSON.stringify(values.query));
3873
+ onQueryChanged?.(query, feedDxn);
3874
+ }, [
3875
+ onQueryChanged,
3876
+ mode,
3877
+ feeds
3878
+ ]);
3879
+ const handleDelete = useCallback((fieldId) => {
3880
+ invariant(!readonly, void 0, {
3881
+ "~LogMeta": "~LogMeta",
3882
+ F: __dxlog_file,
3883
+ L: 207,
3884
+ S: void 0,
3885
+ A: ["!readonly", ""]
3886
+ });
3887
+ onDelete?.(fieldId);
3888
+ }, [onDelete, readonly]);
3889
+ return /* @__PURE__ */ jsx("div", {
3890
+ className: mx(classNames),
3891
+ children: /* @__PURE__ */ jsx(Form.Root, {
3892
+ schema: viewSchema,
3893
+ values: viewValues,
3894
+ fieldMap,
3895
+ db,
3896
+ onValuesChanged: handleUpdate,
3897
+ children: /* @__PURE__ */ jsx(Form.Viewport, { children: /* @__PURE__ */ jsxs(Form.Content, { children: [
3898
+ schemaReadonly && !readonly && /* @__PURE__ */ jsx(Message.Root, {
3899
+ valence: "info",
3900
+ classNames: "my-form-padding",
3901
+ children: /* @__PURE__ */ jsx(Message.Title, { children: t("system-schema.description") })
3902
+ }),
3903
+ /* @__PURE__ */ jsx(Form.FieldSet, {}),
3904
+ type && /* @__PURE__ */ jsx(FieldList, {
3905
+ type,
3906
+ view,
3907
+ registry,
3908
+ readonly,
3909
+ showHeading,
3910
+ onDelete: handleDelete
3911
+ })
3912
+ ] }) })
3913
+ })
3914
+ });
3915
+ });
3916
+ var customFields = ({ types, tags }) => ({ query: ({ type, readonly, label, getValue, onValueChange }) => {
3917
+ const handleChange = useCallback((query) => onValueChange(type, query.ast), [onValueChange]);
3918
+ return /* @__PURE__ */ jsxs(Input.Root, { children: [/* @__PURE__ */ jsx(FormFieldLabel, {
3919
+ readonly,
3920
+ label
3921
+ }), /* @__PURE__ */ jsx(QueryForm, {
3922
+ initialQuery: getValue(),
3923
+ types,
3924
+ tags,
3925
+ onChange: handleChange
3926
+ })] });
3927
+ } });
3928
+ var FieldList = ({ type, view, registry, readonly, showHeading = false, onDelete }) => {
3929
+ const atomRegistry = useContext(RegistryContext);
3930
+ const schemaReadonly = Type.getDatabase(type) == null;
3931
+ const { t } = useTranslation(translationKey);
3932
+ const [viewSnapshot] = useObject(view);
3933
+ const projectionModel = useMemo(() => {
3934
+ const jsonSchema = type.jsonSchema;
3935
+ const change = createEchoChangeCallback(view, Type.getDatabase(type) != null ? type : void 0);
3936
+ return new ProjectionModel({
3937
+ registry: atomRegistry,
3938
+ view,
3939
+ baseSchema: jsonSchema,
3940
+ change
3941
+ });
3942
+ }, [
3943
+ atomRegistry,
3944
+ type,
3945
+ view
3946
+ ]);
3947
+ const [expandedField, setExpandedField] = useState();
3948
+ const handleAdd = useCallback(() => {
3949
+ invariant(!readonly, void 0, {
3950
+ "~LogMeta": "~LogMeta",
3951
+ F: __dxlog_file,
3952
+ L: 290,
3953
+ S: void 0,
3954
+ A: ["!readonly", ""]
3955
+ });
3956
+ const field = projectionModel.createFieldProjection();
3957
+ setExpandedField(field.id);
3958
+ }, [projectionModel, readonly]);
3959
+ const handleDelete = useCallback((fieldId) => {
3960
+ invariant(!readonly, void 0, {
3961
+ "~LogMeta": "~LogMeta",
3962
+ F: __dxlog_file,
3963
+ L: 297,
3964
+ S: void 0,
3965
+ A: ["!readonly", ""]
3966
+ });
3967
+ if (fieldId === expandedField) setExpandedField(void 0);
3968
+ onDelete?.(fieldId);
3969
+ }, [
3970
+ expandedField,
3971
+ onDelete,
3972
+ readonly
3973
+ ]);
3974
+ const handleMove = useCallback((fromIndex, toIndex) => {
3975
+ invariant(!readonly, void 0, {
3976
+ "~LogMeta": "~LogMeta",
3977
+ F: __dxlog_file,
3978
+ L: 309,
3979
+ S: void 0,
3980
+ A: ["!readonly", ""]
3981
+ });
3982
+ Obj.update(view, (view) => {
3983
+ const fields = [...view.projection.fields];
3984
+ const [moved] = fields.splice(fromIndex, 1);
3985
+ fields.splice(toIndex, 0, moved);
3986
+ view.projection.fields = fields;
3987
+ });
3988
+ }, [view, readonly]);
3989
+ const handleClose = useCallback(() => setExpandedField(void 0), []);
3990
+ const handleHide = useCallback((fieldId) => {
3991
+ setExpandedField(void 0);
3992
+ projectionModel.hideFieldProjection(fieldId);
3993
+ }, [projectionModel]);
3994
+ const handleShow = useCallback((property) => {
3995
+ setExpandedField(void 0);
3996
+ projectionModel.showFieldProjection(property);
3997
+ }, [projectionModel]);
3998
+ if (!viewSnapshot) return null;
3999
+ return /* @__PURE__ */ jsx(OrderedList.Root, {
4000
+ items: viewSnapshot.projection.fields,
4001
+ isItem: Schema.is(View.FieldSchema),
4002
+ getId: (field) => field.id,
4003
+ onMove: readonly ? void 0 : handleMove,
4004
+ readonly,
4005
+ expandedId: expandedField,
4006
+ onExpandedChange: setExpandedField,
4007
+ children: ({ items: fields }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
4008
+ /* @__PURE__ */ jsx(FormFieldHeader, {
4009
+ label: t("fields.label"),
4010
+ readonly,
4011
+ add: {
4012
+ label: t("add-property-button.label"),
4013
+ disabled: viewSnapshot.projection.fields.length >= VIEW_FIELD_LIMIT,
4014
+ onClick: handleAdd
4015
+ }
4016
+ }),
4017
+ showHeading && /* @__PURE__ */ jsx(FormFieldLabel, {
4018
+ standalone: true,
4019
+ classNames: "py-1",
4020
+ label: t("field-path.label")
4021
+ }),
4022
+ /* @__PURE__ */ jsx(OrderedList.Content, { children: fields.map((field) => {
4023
+ const hidden = field.visible === false;
4024
+ return /* @__PURE__ */ jsx(OrderedList.DetailItem, {
4025
+ id: field.id,
4026
+ item: field,
4027
+ canDrag: !readonly && !schemaReadonly,
4028
+ expandable: !readonly,
4029
+ title: field.path,
4030
+ titleClassNames: hidden ? "text-subdued" : void 0,
4031
+ actions: /* @__PURE__ */ jsx(ToggleIconButton, {
4032
+ iconOnly: true,
4033
+ variant: "ghost",
4034
+ active: hidden,
4035
+ icon: "ph--eye--regular",
4036
+ activeIcon: "ph--eye-closed--regular",
4037
+ label: t(hidden ? "show-field.label" : "hide-field.label"),
4038
+ "data-testid": hidden ? "show-field-button" : "hide-field-button",
4039
+ disabled: readonly || !hidden && projectionModel.getFields().length <= 1,
4040
+ onClick: () => hidden ? handleShow(field.path) : handleHide(field.id)
4041
+ }),
4042
+ trailing: !readonly && /* @__PURE__ */ jsx(CompactIconButton, {
4043
+ icon: "ph--x--regular",
4044
+ label: t("delete-field.label"),
4045
+ disabled: schemaReadonly || viewSnapshot.projection.fields.length <= 1,
4046
+ onClick: () => handleDelete(field.id),
4047
+ "data-testid": "field.delete"
4048
+ }),
4049
+ children: /* @__PURE__ */ jsx(FieldEditor, {
4050
+ readonly: readonly || schemaReadonly,
4051
+ registry,
4052
+ projection: projectionModel,
4053
+ field,
4054
+ onSave: handleClose
4055
+ })
4056
+ }, field.id);
4057
+ }) })
4058
+ ] })
4059
+ });
4060
+ };
4061
+ //#endregion
4062
+ export { ArrayField, AsyncSelectField, AutofillAnnotation, AutofillAnnotationId, AutofillField, BooleanField, ComboboxField, CompactIconButton, DateField, EMAIL_REGEX, FieldEditor, Form, FormContextProvider, FormField, FormFieldErrorBoundary, FormFieldHeader, FormFieldLabel, FormFieldSet, FormFieldSetContainer, FormRow, GeoPointField, InlineRefField, META_TAGS_KEY, MarkdownField, NAME_ADDR_REGEX, NumberField, ObjectForm, ObjectPicker, ObjectProperties, ObjectTree, OptionsLookupAnnotation, OptionsLookupAnnotationId, PasswordField, REF_REGEX, RefEditor, RefField, SelectField, SelectOptionField, TextAreaField, TextField, TupleField, ViewEditor, autofill, cellClassesForFieldType, createSelectField, findRefOption, formatMailbox, getDefaultValue, getFormProperties, getNumericConstraints, getRefProps, getRootFormProperties, omitHiddenFormFields, omitId, optionsLookup, parseValue, pathNotUniqueError, pickValues, presentationFor, refEditor, refEditorRedecorate, useAsyncFieldEffect, useFormContext, useFormFieldState, useFormHandler, useFormValues, useKeyHandler, withMetaTags };
4063
+
4064
+ //# sourceMappingURL=index.mjs.map