@dxos/react-ui-form 0.10.0 → 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
@@ -1,4675 +0,0 @@
1
- // src/annotations.ts
2
- import { createAnnotationHelper } from "@dxos/echo/internal";
3
- var OptionsLookupAnnotationId = /* @__PURE__ */ Symbol.for("@dxos/schema/annotation/OptionsLookup");
4
- var OptionsLookupAnnotation = createAnnotationHelper(OptionsLookupAnnotationId);
5
- var optionsLookup = () => (deps, load, options) => ({
6
- deps,
7
- load,
8
- combobox: options?.combobox
9
- });
10
- var AutofillAnnotationId = /* @__PURE__ */ Symbol.for("@dxos/schema/annotation/Autofill");
11
- var AutofillAnnotation = createAnnotationHelper(AutofillAnnotationId);
12
- var autofill = () => (deps, derive) => ({
13
- deps,
14
- derive
15
- });
16
-
17
- // src/components/FieldEditor/FieldEditor.tsx
18
- import React26, { useCallback as useCallback18, useEffect as useEffect9, useMemo as useMemo13, useState as useState10 } from "react";
19
- import { Filter as Filter2, Obj as Obj3, Type as Type4 } from "@dxos/echo";
20
- import { Format as Format6, FormatEnums, formatToType } from "@dxos/echo/Format";
21
- import { log as log3 } from "@dxos/log";
22
- import { useAsyncEffect, useTranslation as useTranslation11 } from "@dxos/react-ui";
23
- import { formatToAdditionalPropertyAttributes, getFormatSchema } from "@dxos/schema";
24
- import { translationKey as translationKey11 } from "#translations";
25
-
26
- // src/util/field.ts
27
- import { Format, GeoLocation, TypeEnum } from "@dxos/echo/Format";
28
- var parseValue = ({ type, format, value: value3 }) => {
29
- if (value3 === void 0 || value3 === null || value3 === "") {
30
- return void 0;
31
- }
32
- const parseScalar = (type2) => {
33
- switch (type2) {
34
- case TypeEnum.Boolean: {
35
- if (typeof value3 === "string") {
36
- const lowercaseValue = value3.toLowerCase();
37
- if (lowercaseValue === "0" || lowercaseValue === "false") {
38
- return false;
39
- } else if (lowercaseValue === "1" || lowercaseValue === "true") {
40
- return true;
41
- }
42
- }
43
- return Boolean(value3);
44
- }
45
- case TypeEnum.Number: {
46
- const cleaned = typeof value3 === "string" ? value3.replace(/[^0-9.-]+/g, "") : value3;
47
- const num = Number(cleaned);
48
- return Number.isNaN(num) ? null : num;
49
- }
50
- case TypeEnum.String:
51
- case TypeEnum.Ref:
52
- return String(value3);
53
- default: {
54
- return value3;
55
- }
56
- }
57
- };
58
- if (!format) {
59
- return parseScalar(type);
60
- }
61
- switch (format) {
62
- case Format.TypeFormat.Boolean:
63
- return parseScalar(TypeEnum.Boolean);
64
- case Format.TypeFormat.Number:
65
- case Format.TypeFormat.Percent:
66
- case Format.TypeFormat.Currency:
67
- return parseScalar(TypeEnum.Number);
68
- case Format.TypeFormat.String:
69
- case Format.TypeFormat.Markdown: {
70
- return parseScalar(TypeEnum.String);
71
- }
72
- case Format.TypeFormat.Ref:
73
- throw new Error(`unexpected format: ${Format.TypeFormat.Ref}`);
74
- case Format.TypeFormat.DateTime:
75
- case Format.TypeFormat.Date:
76
- case Format.TypeFormat.Time:
77
- case Format.TypeFormat.Timestamp: {
78
- const date = new Date(value3);
79
- return isNaN(date.getTime()) ? null : date;
80
- }
81
- case Format.TypeFormat.GeoPoint: {
82
- if (typeof value3 === "string") {
83
- const cleaned = value3.replace(/[^0-9.,-]+/g, "");
84
- const parts = cleaned.split(",");
85
- if (parts.length === 2) {
86
- const lat = parseFloat(parts[0]);
87
- const long = parseFloat(parts[1]);
88
- if (!isNaN(lat) && !isNaN(long)) {
89
- return GeoLocation.toGeoPoint({
90
- latitude: lat,
91
- longitude: long
92
- });
93
- }
94
- }
95
- }
96
- return [
97
- 0,
98
- 0
99
- ];
100
- }
101
- default: {
102
- return value3;
103
- }
104
- }
105
- };
106
- var cellClassesForFieldType = ({ type, format }) => {
107
- switch (format || type) {
108
- case Format.TypeFormat.Markdown:
109
- return void 0;
110
- case Format.TypeFormat.Time:
111
- case Format.TypeFormat.Timestamp:
112
- case Format.TypeFormat.DateTime:
113
- case Format.TypeFormat.Date:
114
- case Format.TypeFormat.Duration:
115
- return [
116
- "font-mono",
117
- "text-right"
118
- ];
119
- case Format.TypeFormat.Currency:
120
- return [
121
- "text-right"
122
- ];
123
- case Format.TypeFormat.JSON:
124
- case Format.TypeFormat.DID:
125
- return [
126
- "font-mono"
127
- ];
128
- case TypeEnum.Number:
129
- case TypeEnum.Boolean:
130
- return [
131
- "text-right",
132
- "font-mono"
133
- ];
134
- case TypeEnum.String:
135
- return void 0;
136
- case TypeEnum.Ref:
137
- return void 0;
138
- default:
139
- return void 0;
140
- }
141
- };
142
- var pathNotUniqueError = (path) => ({
143
- path: "path",
144
- message: `'${path}' is already present`
145
- });
146
-
147
- // src/util/formatting.ts
148
- import { format as formatDate } from "date-fns/format";
149
- import { Format as Format2, GeoLocation as GeoLocation2, TypeEnum as TypeEnum2 } from "@dxos/echo/Format";
150
- var formatForDisplay = ({ type, format, value: value3, locale = void 0 }) => {
151
- const formatScalar = (type2) => {
152
- switch (type2) {
153
- case TypeEnum2.Boolean:
154
- return value3.toLocaleString().toUpperCase();
155
- case TypeEnum2.Number:
156
- return value3.toLocaleString(locale);
157
- case TypeEnum2.String:
158
- case TypeEnum2.Ref:
159
- return String(value3);
160
- default: {
161
- return String(value3);
162
- }
163
- }
164
- };
165
- if (!format) {
166
- return formatScalar(type);
167
- }
168
- switch (format) {
169
- case Format2.TypeFormat.Boolean: {
170
- return formatScalar(TypeEnum2.Boolean);
171
- }
172
- case Format2.TypeFormat.Number: {
173
- return formatScalar(TypeEnum2.Number);
174
- }
175
- case Format2.TypeFormat.String:
176
- case Format2.TypeFormat.Ref: {
177
- return formatScalar(TypeEnum2.String);
178
- }
179
- case Format2.TypeFormat.Percent: {
180
- return `${value3 * 100}%`;
181
- }
182
- case Format2.TypeFormat.Currency: {
183
- return value3.toLocaleString(locale, {
184
- style: "currency",
185
- currency: "USD",
186
- minimumFractionDigits: 2,
187
- maximumFractionDigits: 2
188
- });
189
- }
190
- case Format2.TypeFormat.Date: {
191
- try {
192
- return formatDate(new Date(value3), "yyyy-MM-dd");
193
- } catch (error) {
194
- return "Invalid Date";
195
- }
196
- }
197
- case Format2.TypeFormat.Time: {
198
- try {
199
- return formatDate(new Date(value3), "HH:mm:ss");
200
- } catch (error) {
201
- return "Invalid Time";
202
- }
203
- }
204
- case Format2.TypeFormat.DateTime: {
205
- try {
206
- return formatDate(new Date(value3), "yyyy-MM-dd HH:mm:ss");
207
- } catch (error) {
208
- return "Invalid DateTime";
209
- }
210
- }
211
- case Format2.TypeFormat.GeoPoint: {
212
- if (value3 === null || value3 === void 0) {
213
- return "";
214
- }
215
- if (Array.isArray(value3) && value3.length >= 2 && value3.every(Number.isFinite)) {
216
- const { latitude, longitude } = GeoLocation2.fromGeoPoint(value3);
217
- return `${latitude},${longitude}`;
218
- }
219
- return String(value3);
220
- }
221
- default: {
222
- if (value3 === null || value3 === "undefined") {
223
- return "";
224
- }
225
- return String(value3);
226
- }
227
- }
228
- };
229
- var formatForEditing = ({ type, format, value: value3, locale = void 0 }) => {
230
- const formatScalar = (type2) => {
231
- switch (type2) {
232
- case TypeEnum2.Boolean:
233
- case TypeEnum2.Number:
234
- case TypeEnum2.String:
235
- case TypeEnum2.Ref:
236
- return String(value3);
237
- default:
238
- return String(value3);
239
- }
240
- };
241
- if (!format) {
242
- return formatScalar(type);
243
- }
244
- switch (format) {
245
- case Format2.TypeFormat.Boolean:
246
- case Format2.TypeFormat.Number:
247
- case Format2.TypeFormat.String:
248
- case Format2.TypeFormat.Ref: {
249
- return formatScalar(type);
250
- }
251
- case Format2.TypeFormat.Percent: {
252
- return String(value3 * 100);
253
- }
254
- case Format2.TypeFormat.Currency: {
255
- return String(value3);
256
- }
257
- case Format2.TypeFormat.DateTime: {
258
- const date = new Date(value3);
259
- return date.toISOString();
260
- }
261
- case Format2.TypeFormat.Date: {
262
- const date = new Date(value3);
263
- return date.toISOString().split("T")[0];
264
- }
265
- case Format2.TypeFormat.Time: {
266
- const date = new Date(value3);
267
- return date.toISOString().split("T")[1].split(".")[0];
268
- }
269
- case Format2.TypeFormat.GeoPoint: {
270
- if (value3 === null || value3 === void 0) {
271
- return "";
272
- }
273
- if (Array.isArray(value3) && value3.length >= 2 && value3.every(Number.isFinite)) {
274
- const { latitude, longitude } = GeoLocation2.fromGeoPoint(value3);
275
- return `${latitude},${longitude}`;
276
- }
277
- return String(value3);
278
- }
279
- default: {
280
- return String(value3);
281
- }
282
- }
283
- };
284
-
285
- // src/util/omit.ts
286
- import * as Option from "effect/Option";
287
- import * as Schema from "effect/Schema";
288
- import * as SchemaAST from "effect/SchemaAST";
289
- import { Annotation, Type } from "@dxos/echo";
290
- var omitId = (schemaOrType) => {
291
- const schema = Type.isType(schemaOrType) ? Type.getSchema(schemaOrType) : schemaOrType;
292
- return omitIdFromSchema(schema);
293
- };
294
- var omitIdFromSchema = (schema) => {
295
- const ast = schema.ast;
296
- if (SchemaAST.isUnion(ast)) {
297
- return Schema.Union(...ast.types.map((type) => omitIdFromSchema(Schema.make(type))));
298
- }
299
- const hasId = SchemaAST.getPropertySignatures(ast).some((prop) => prop.name === "id");
300
- return hasId ? schema.pipe(Schema.omit("id")) : schema;
301
- };
302
- var omitHiddenFormFields = (schema) => {
303
- const properties = SchemaAST.getPropertySignatures(schema.ast);
304
- const hidden = properties.filter((prop) => Option.getOrElse(Annotation.FormInputAnnotation.getFromAst(prop.type), () => true) === false).map((prop) => prop.name);
305
- return hidden.length === 0 ? schema : schema.pipe(Schema.omit(...hidden));
306
- };
307
-
308
- // src/util/properties.ts
309
- import * as Option2 from "effect/Option";
310
- import * as SchemaAST2 from "effect/SchemaAST";
311
- import { Annotation as Annotation2 } from "@dxos/echo";
312
- import { SchemaEx } from "@dxos/effect";
313
- var unwrapOptional = (prop) => {
314
- if (!prop.isOptional || !SchemaAST2.isUnion(prop.type)) {
315
- return prop.type;
316
- }
317
- const defined = prop.type.types.filter((type) => type._tag !== "UndefinedKeyword");
318
- if (defined.length === 0) {
319
- return prop.type;
320
- }
321
- return defined.length === 1 ? defined[0] : SchemaAST2.Union.make(defined, prop.type.annotations);
322
- };
323
- var getRootFormProperties = (ast, values) => {
324
- if (!SchemaEx.isDiscriminatedUnion(ast)) {
325
- return getFormProperties(ast);
326
- }
327
- const activeType = SchemaEx.getDiscriminatedType(ast, values ?? {});
328
- if (!activeType) {
329
- return getFormProperties(ast);
330
- }
331
- const activeProps = getFormProperties(activeType);
332
- const discriminators = new Set((SchemaEx.getDiscriminatingProps(ast) ?? []).map((name) => name.toString()));
333
- if (discriminators.size === 0) {
334
- return activeProps;
335
- }
336
- const fullType = SchemaEx.getDiscriminatedType(ast, {});
337
- const fullDiscriminatorProps = fullType ? getFormProperties(fullType) : activeProps;
338
- return activeProps.map((prop) => discriminators.has(prop.name.toString()) ? fullDiscriminatorProps.find((candidate) => candidate.name === prop.name) ?? prop : prop);
339
- };
340
- var getFormProperties = (ast) => {
341
- const signatures = SchemaAST2.getPropertySignatures(ast);
342
- const rawByName = new Map(signatures.map((prop) => [
343
- prop.name,
344
- unwrapOptional(prop)
345
- ]));
346
- const hidden = new Set(signatures.filter((prop) => Annotation2.FormInputAnnotation.getFromAst(unwrapOptional(prop)).pipe(Option2.getOrElse(() => true)) === false).map((prop) => prop.name));
347
- return SchemaEx.getProperties(ast).filter((prop) => !hidden.has(prop.name)).map((prop) => {
348
- const raw = rawByName.get(prop.name);
349
- return raw && (SchemaEx.isNestedType(raw) || SchemaEx.isArrayType(raw)) ? {
350
- ...prop,
351
- type: raw
352
- } : prop;
353
- });
354
- };
355
-
356
- // src/util/refs.ts
357
- import { Ref } from "@dxos/echo";
358
- import { ReferenceAnnotationId } from "@dxos/echo/Annotation";
359
- import { SchemaEx as SchemaEx2 } from "@dxos/effect";
360
- var getRefProps = (ast) => {
361
- if (SchemaEx2.isArrayType(ast)) {
362
- const elementType = SchemaEx2.getArrayElementType(ast);
363
- if (elementType) {
364
- if (Ref.isRefType(elementType)) {
365
- const typename = SchemaEx2.findAnnotation(elementType, ReferenceAnnotationId)?.typename;
366
- return {
367
- ast: elementType,
368
- isArray: true,
369
- typename
370
- };
371
- }
372
- }
373
- }
374
- if (Ref.isRefType(ast)) {
375
- const typename = SchemaEx2.findAnnotation(ast, ReferenceAnnotationId)?.typename;
376
- return {
377
- ast,
378
- isArray: false,
379
- typename
380
- };
381
- }
382
- return void 0;
383
- };
384
-
385
- // src/components/Form/meta-tags.ts
386
- import * as Schema2 from "effect/Schema";
387
- import { Ref as Ref2, Tag } from "@dxos/echo";
388
- var META_TAGS_KEY = "_tags";
389
- var withMetaTags = (schema) => omitId(Schema2.Struct({
390
- [META_TAGS_KEY]: Schema2.Array(Ref2.Ref(Tag.Tag)).pipe(Schema2.annotations({
391
- title: "Tags"
392
- }), Schema2.optional)
393
- }).pipe(Schema2.extend(schema)));
394
-
395
- // src/components/Form/FormControls.tsx
396
- import React25, { useRef as useRef7 } from "react";
397
- import { Column, IconButton as IconButton5, Input as Input14, ScrollArea, composable, composableProps, useMergeRefs, useTranslation as useTranslation10, withColumn } from "@dxos/react-ui";
398
- import { MarkdownView } from "@dxos/react-ui-markdown";
399
- import { mx as mx6 } from "@dxos/ui-theme";
400
- import { translationKey as translationKey10 } from "#translations";
401
-
402
- // src/hooks/useAsyncFieldEffect.ts
403
- import * as Cause from "effect/Cause";
404
- import * as Effect from "effect/Effect";
405
- import * as Exit from "effect/Exit";
406
- import { useEffect, useRef, useState } from "react";
407
- var pickValues = (values, deps) => {
408
- const subset = {};
409
- for (const dep of deps) {
410
- subset[dep] = values?.[dep];
411
- }
412
- return subset;
413
- };
414
- var useAsyncFieldEffect = (factory, key, { debounceMs = 400, enabled = true } = {}) => {
415
- const factoryRef = useRef(factory);
416
- factoryRef.current = factory;
417
- const [state, setState] = useState({
418
- loading: false,
419
- error: void 0,
420
- data: void 0
421
- });
422
- useEffect(() => {
423
- if (!enabled) {
424
- setState({
425
- loading: false,
426
- error: void 0,
427
- data: void 0
428
- });
429
- return;
430
- }
431
- let cancel;
432
- const timer = setTimeout(() => {
433
- setState((prev) => ({
434
- ...prev,
435
- loading: true,
436
- error: void 0
437
- }));
438
- cancel = Effect.runCallback(factoryRef.current(), {
439
- onExit: (exit) => {
440
- Exit.match(exit, {
441
- onSuccess: (data) => setState({
442
- loading: false,
443
- error: void 0,
444
- data
445
- }),
446
- onFailure: (cause) => {
447
- if (!Cause.isInterruptedOnly(cause)) {
448
- setState((prev) => ({
449
- loading: false,
450
- error: Cause.squash(cause),
451
- data: prev.data
452
- }));
453
- }
454
- }
455
- });
456
- }
457
- });
458
- }, debounceMs);
459
- return () => {
460
- clearTimeout(timer);
461
- cancel?.();
462
- };
463
- }, [
464
- key,
465
- enabled,
466
- debounceMs
467
- ]);
468
- return state;
469
- };
470
-
471
- // src/hooks/useFormContext.ts
472
- import { createContext } from "@radix-ui/react-context";
473
- import * as SchemaAST3 from "effect/SchemaAST";
474
- import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
475
- import { SchemaEx as SchemaEx3 } from "@dxos/effect";
476
- var [FormContextProvider, useFormContext] = createContext("Form");
477
- var useFormValues = (componentName, path, defaultValue) => {
478
- const stablePath = useMemo(() => path ?? [], [
479
- path ? path.join(".") : void 0
480
- ]);
481
- const jsonPath = SchemaEx3.createJsonPath(stablePath);
482
- const { form: { values, onValueChange } } = useFormContext(componentName);
483
- const value3 = SchemaEx3.getValue(values, jsonPath);
484
- const lastAppliedPathRef = useRef2(null);
485
- useEffect2(() => {
486
- if (value3 == null && defaultValue && lastAppliedPathRef.current !== jsonPath) {
487
- lastAppliedPathRef.current = jsonPath;
488
- onValueChange(stablePath, SchemaAST3.stringKeyword, defaultValue());
489
- }
490
- }, [
491
- value3,
492
- defaultValue,
493
- onValueChange,
494
- stablePath,
495
- jsonPath
496
- ]);
497
- return value3;
498
- };
499
- var useFormFieldState = (componentName, path = []) => {
500
- const stablePath = useMemo(() => path, [
501
- Array.isArray(path) ? path.join(".") : path
502
- ]);
503
- const { form: { getStatus, getValue, onBlur, onValueChange } } = useFormContext(componentName);
504
- return useMemo(() => ({
505
- getStatus: () => getStatus(stablePath),
506
- getValue: () => getValue(stablePath),
507
- onBlur: () => onBlur(stablePath),
508
- onValueChange: (ast, value3) => onValueChange(stablePath, ast, value3)
509
- }), [
510
- getStatus,
511
- getValue,
512
- onBlur,
513
- onValueChange,
514
- stablePath
515
- ]);
516
- };
517
-
518
- // src/hooks/useFormHandler.ts
519
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
520
- import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState2 } from "react";
521
- import { SchemaEx as SchemaEx4 } from "@dxos/effect";
522
- import { log } from "@dxos/log";
523
- import { useDefaultValue } from "@dxos/react-ui";
524
- import { validateSchema } from "@dxos/schema";
525
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-form/src/hooks/useFormHandler.ts";
526
- var useFormHandler = ({ schema, autoSave, values: valuesProp, defaultValues: defaultValuesProp, onValuesChanged, onValidate, onSave, onCancel, ...props }) => {
527
- const [changed, setChanged] = useState2({});
528
- const [touched, setTouched] = useState2({});
529
- const [errors, setErrors] = useState2({});
530
- const [saving, setSaving] = useState2(false);
531
- const defaultValues = useDefaultValue(defaultValuesProp, () => ({}));
532
- const [values$, setValues] = useControllableState({
533
- prop: valuesProp,
534
- defaultProp: defaultValues,
535
- onChange: () => {
536
- if (valuesProp !== void 0) {
537
- setValues(valuesProp ?? defaultValues);
538
- setChanged({});
539
- setTouched({});
540
- setErrors({});
541
- setSaving(false);
542
- }
543
- }
544
- });
545
- const values = values$;
546
- const validate = useCallback((values2) => {
547
- if (!schema) {
548
- return false;
549
- }
550
- let errors2 = validateSchema(schema, values2) ?? [];
551
- log("validate", {
552
- values: values2,
553
- errors: errors2
554
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 42, S: void 0 });
555
- if (errors2.length === 0 && onValidate) {
556
- const validatedValues = values2;
557
- errors2 = onValidate(validatedValues) ?? [];
558
- }
559
- setErrors(flatMap(errors2));
560
- const valid = errors2.length === 0;
561
- return valid;
562
- }, [
563
- schema,
564
- onValidate
565
- ]);
566
- const [schemaChanged, setSchemaChanged] = useState2(false);
567
- useEffect3(() => setSchemaChanged(true), [
568
- schema
569
- ]);
570
- useEffect3(() => {
571
- if (schemaChanged) {
572
- validate(values);
573
- setSchemaChanged(false);
574
- }
575
- }, [
576
- validate,
577
- values,
578
- schemaChanged
579
- ]);
580
- const isValid = useMemo2(() => {
581
- return Object.keys(errors).length === 0;
582
- }, [
583
- errors
584
- ]);
585
- const canSave = useMemo2(() => !saving && // Check if any error paths that are touched have errors.
586
- !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
587
- touched,
588
- errors,
589
- saving
590
- ]);
591
- const handleSave = useCallback(async () => {
592
- if (validate(values)) {
593
- setSaving(true);
594
- try {
595
- await onSave?.(values, {
596
- changed,
597
- isValid
598
- });
599
- } finally {
600
- setSaving(false);
601
- }
602
- }
603
- }, [
604
- values,
605
- validate,
606
- onSave,
607
- changed,
608
- isValid
609
- ]);
610
- const handleCancel = useCallback(() => {
611
- onCancel?.();
612
- }, [
613
- onCancel
614
- ]);
615
- const getStatus = useCallback((path) => {
616
- const jsonPath = Array.isArray(path) ? SchemaEx4.createJsonPath(path) : path;
617
- const [_, error] = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`)) ?? [];
618
- const isTouched = touched[jsonPath];
619
- if (!isTouched) {
620
- return {
621
- status: void 0,
622
- error: void 0
623
- };
624
- }
625
- return {
626
- status: error ? "error" : void 0,
627
- error: error ? error ?? void 0 : void 0
628
- };
629
- }, [
630
- errors,
631
- touched
632
- ]);
633
- const getValue = useCallback((path) => {
634
- return SchemaEx4.getValue(values, SchemaEx4.createJsonPath(path));
635
- }, [
636
- values
637
- ]);
638
- const onValueChange = useCallback((path, type, value3) => {
639
- log("onValueChange", {
640
- path,
641
- value: value3
642
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 138, S: void 0 });
643
- const jsonPath = SchemaEx4.createJsonPath(path);
644
- const pathArray = path;
645
- let parsedValue = value3;
646
- try {
647
- if (type._tag === "NumberKeyword") {
648
- parsedValue = parseFloat(value3) || 0;
649
- }
650
- } catch (err) {
651
- log.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 150, S: void 0 });
652
- parsedValue = void 0;
653
- }
654
- const newValues = mergeAtPath(values, pathArray, parsedValue);
655
- const newChanged = {
656
- [jsonPath]: true
657
- };
658
- setChanged((prev) => ({
659
- ...prev,
660
- ...newChanged
661
- }));
662
- const isValid2 = validate(newValues);
663
- onValuesChanged?.(newValues, {
664
- isValid: isValid2,
665
- changed: newChanged
666
- });
667
- if (valuesProp === void 0) {
668
- setValues(newValues);
669
- }
670
- }, [
671
- values,
672
- validate,
673
- onValuesChanged,
674
- valuesProp
675
- ]);
676
- const onBlur = useCallback(async (path) => {
677
- const jsonPath = SchemaEx4.createJsonPath(path);
678
- setTouched((touched2) => ({
679
- ...touched2,
680
- [jsonPath]: true
681
- }));
682
- const isValid2 = validate(values);
683
- if (Object.keys(changed).length > 0 && isValid2 && autoSave) {
684
- await onSave?.(values, {
685
- changed,
686
- isValid: isValid2
687
- });
688
- }
689
- }, [
690
- validate,
691
- values,
692
- changed,
693
- autoSave,
694
- onSave
695
- ]);
696
- return useMemo2(() => ({
697
- // State.
698
- schema,
699
- values,
700
- errors,
701
- touched,
702
- changed,
703
- isValid,
704
- canSave,
705
- autoSave,
706
- // Actions.
707
- onSave: handleSave,
708
- onCancel: handleCancel,
709
- // Field utils.
710
- getStatus,
711
- getValue,
712
- onBlur,
713
- onValueChange,
714
- ...props
715
- }), [
716
- schema,
717
- autoSave,
718
- values,
719
- errors,
720
- touched,
721
- changed,
722
- canSave,
723
- isValid,
724
- getStatus,
725
- getValue,
726
- onBlur,
727
- onValueChange,
728
- handleSave,
729
- handleCancel
730
- ]);
731
- };
732
- var mergeAtPath = (obj, path, value3) => {
733
- if (path.length === 0) {
734
- return value3;
735
- }
736
- const [head, ...rest] = path;
737
- const child = rest.length === 0 ? value3 : mergeAtPath(obj?.[head], rest, value3);
738
- if (Array.isArray(obj)) {
739
- const copy = [
740
- ...obj
741
- ];
742
- copy[head] = child;
743
- return copy;
744
- }
745
- return {
746
- ...obj,
747
- [head]: child
748
- };
749
- };
750
- var flatMap = (errors) => {
751
- return errors.reduce((result, { path, message }) => {
752
- const jsonPath = SchemaEx4.fromEffectValidationPath(path);
753
- if (!(jsonPath in result)) {
754
- result[jsonPath] = message;
755
- }
756
- return result;
757
- }, {});
758
- };
759
-
760
- // src/hooks/useKeyHandler.tsx
761
- import { useCallback as useCallback2, useLayoutEffect } from "react";
762
- import { addEventListener } from "@dxos/async";
763
- var useKeyHandler = (elRef, form) => {
764
- const handleKeyDown = useCallback2((event) => {
765
- switch (event.key) {
766
- case "Enter": {
767
- if (event.metaKey && form.canSave) {
768
- form.onSave();
769
- }
770
- break;
771
- }
772
- }
773
- }, [
774
- form.isValid,
775
- form.canSave,
776
- form.onSave,
777
- form.autoSave
778
- ]);
779
- useLayoutEffect(() => {
780
- const el = elRef.current;
781
- if (!el) {
782
- return;
783
- }
784
- return addEventListener(el, "keydown", handleKeyDown);
785
- }, [
786
- elRef,
787
- handleKeyDown
788
- ]);
789
- };
790
-
791
- // src/components/Form/Form.theme.ts
792
- import { mx, tv } from "@dxos/ui-theme";
793
- var formStyles = tv({
794
- slots: {
795
- viewport: "",
796
- content: "",
797
- section: "flex flex-col py-form-section-gap first:pt-0",
798
- group: "flex flex-col gap-3 p-trim-md border border-separator rounded-sm",
799
- sectionHeader: "",
800
- sectionTitle: "text-lg",
801
- sectionDescription: "text-description",
802
- fieldSet: "",
803
- field: "",
804
- fieldLabel: "h-8 grid grid-cols-[1fr_auto_auto] items-center select-none",
805
- fieldLabelText: "",
806
- fieldDescription: "text-description",
807
- fieldControl: "",
808
- fieldValidation: "",
809
- // Action bar (cancel/save), equal-width columns flowing horizontally.
810
- actions: "grid grid-flow-col gap-form-gap auto-cols-fr py-form-padding",
811
- // Standalone submit row (full-width primary button).
812
- submit: "flex w-full pt-form-padding",
813
- // Collapsible field-set body: indented column of sub-fields.
814
- fieldSetBody: "flex flex-col gap-2 px-2 pb-2",
815
- // Bordered container wrapping a collapsible nested group, plus its top spacing.
816
- fieldSetBox: "border border-subdued-separator rounded-sm",
817
- fieldSetBoxOuter: "pt-trim-md"
818
- },
819
- variants: {
820
- variant: {
821
- default: {},
822
- settings: {
823
- content: "dx-document",
824
- // Gap on the section spaces its direct children — section title/description and, for action
825
- // panels, the `Form.Row`s placed directly in the section (which have no `fieldSet` wrapper).
826
- section: "py-form-section-gap! gap-trim-md",
827
- sectionHeader: "pb-form-section-gap",
828
- sectionTitle: "px-trim-md text-xl",
829
- sectionDescription: "px-trim-md",
830
- // No top padding: the section gap already separates the field set from the title above it.
831
- fieldSet: "flex flex-col gap-trim-md",
832
- 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-lg gap-y-0 p-trim-md border border-input-separator rounded-md"),
833
- fieldLabel: "[grid-area:header]",
834
- fieldLabelText: "text-base-fg text-lg",
835
- fieldDescription: "[grid-area:description] pt-1 text-description",
836
- fieldControl: "[grid-area:control] flex justify-end items-start md:text-right pt-3 md:pt-0",
837
- fieldValidation: "[grid-area:validation]"
838
- }
839
- }
840
- },
841
- defaultVariants: {
842
- variant: "default"
843
- }
844
- });
845
- var formTheme = {
846
- styles: formStyles,
847
- behavior: {
848
- default: {
849
- showDescription: false
850
- },
851
- settings: {
852
- showDescription: true
853
- }
854
- }
855
- };
856
- var formSlots = Object.keys(formTheme.styles());
857
-
858
- // src/components/Form/FormFieldSet/FormFieldSet.tsx
859
- import * as Option7 from "effect/Option";
860
- import React24, { useMemo as useMemo12 } from "react";
861
- import { DEFAULT_LAYOUT_NAME, FormLayoutAnnotation } from "@dxos/echo/Annotation";
862
- import { SchemaEx as SchemaEx10 } from "@dxos/effect";
863
-
864
- // src/components/Form/FormField/fields/ArrayField/ArrayField.tsx
865
- import * as Option4 from "effect/Option";
866
- import * as SchemaAST5 from "effect/SchemaAST";
867
- import React4, { useCallback as useCallback3, useRef as useRef3 } from "react";
868
- import { Annotation as Annotation4, Ref as Ref3 } from "@dxos/echo";
869
- import { useType as defaultUseType } from "@dxos/echo-react";
870
- import { SchemaEx as SchemaEx6 } from "@dxos/effect";
871
- import { DXN } from "@dxos/keys";
872
- import { log as log2 } from "@dxos/log";
873
- import { useTranslation as useTranslation2 } from "@dxos/react-ui";
874
- import { OrderedList } from "@dxos/react-ui-list";
875
- import { mx as mx3 } from "@dxos/ui-theme";
876
- import { arrayMove } from "@dxos/util";
877
- import { translationKey as translationKey2 } from "#translations";
878
-
879
- // src/components/Form/FormField/FormField.tsx
880
- import * as Match from "effect/Match";
881
- import * as Option3 from "effect/Option";
882
- import * as Schema3 from "effect/Schema";
883
- import * as SchemaAST4 from "effect/SchemaAST";
884
- import * as String2 from "effect/String";
885
- import React, { useMemo as useMemo3 } from "react";
886
- import { Annotation as Annotation3, Format as Format3 } from "@dxos/echo";
887
- import { SchemaEx as SchemaEx5 } from "@dxos/effect";
888
- import { IconButton, useTranslation } from "@dxos/react-ui";
889
- import { translationKey } from "#translations";
890
- var FormField = (props) => {
891
- const {
892
- type,
893
- name,
894
- label: labelProp,
895
- path,
896
- required,
897
- projection,
898
- fieldMap,
899
- fieldProvider,
900
- readonly,
901
- layout,
902
- // RefFieldProps
903
- createTypename,
904
- createOptionLabel,
905
- createOptionIcon,
906
- createInitialValuePath,
907
- createFieldMap: createFieldMap3,
908
- db,
909
- useType: useType2,
910
- getOptions,
911
- onCreate,
912
- resolveCreateEntry,
913
- refInline
914
- } = props;
915
- const { t } = useTranslation(translationKey);
916
- const title = SchemaEx5.getAnnotation(SchemaAST4.TitleAnnotationId)(type);
917
- const description = SchemaEx5.getAnnotation(SchemaAST4.DescriptionAnnotationId)(type);
918
- const examples = SchemaEx5.getAnnotation(SchemaAST4.ExamplesAnnotationId)(type);
919
- const label = useMemo3(() => labelProp ?? title ?? (name == null ? "" : String2.capitalize(name)), [
920
- labelProp,
921
- title,
922
- name
923
- ]);
924
- const placeholder = useMemo3(() => examples?.length ? `${t("example.placeholder")}: ${examples[0]}` : description ?? label, [
925
- examples,
926
- description,
927
- label,
928
- t
929
- ]);
930
- const providerSchema = useMemo3(() => fieldProvider ? Schema3.make(type) : void 0, [
931
- fieldProvider,
932
- type
933
- ]);
934
- const fieldState = useFormFieldState(FormField.displayName, path);
935
- const jsonPath = SchemaEx5.createJsonPath(path ?? []);
936
- const fieldProps = {
937
- type,
938
- format: Format3.FormatAnnotation.getFromAst(type).pipe((annotation) => Option3.getOrUndefined(annotation)),
939
- readonly,
940
- label,
941
- description,
942
- jsonPath,
943
- placeholder,
944
- presentation: layout,
945
- required,
946
- db,
947
- ...fieldState
948
- };
949
- if (readonly && fieldState.getValue() == null) {
950
- return null;
951
- }
952
- const CustomField = fieldMap?.[jsonPath];
953
- if (CustomField) {
954
- return /* @__PURE__ */ React.createElement(CustomField, fieldProps);
955
- }
956
- if (fieldProvider && providerSchema) {
957
- const component = fieldProvider({
958
- schema: providerSchema,
959
- prop: name ?? "",
960
- fieldProps
961
- });
962
- if (component) {
963
- return component;
964
- }
965
- }
966
- const optionsLookup2 = Option3.getOrUndefined(OptionsLookupAnnotation.getFromAst(type));
967
- if (optionsLookup2) {
968
- return optionsLookup2.combobox ? /* @__PURE__ */ React.createElement(ComboboxField, {
969
- ...fieldProps,
970
- lookup: optionsLookup2
971
- }) : /* @__PURE__ */ React.createElement(AsyncSelectField, {
972
- ...fieldProps,
973
- lookup: optionsLookup2
974
- });
975
- }
976
- const autofill2 = Option3.getOrUndefined(AutofillAnnotation.getFromAst(type));
977
- if (autofill2) {
978
- return /* @__PURE__ */ React.createElement(AutofillField, {
979
- ...fieldProps,
980
- autofill: autofill2
981
- });
982
- }
983
- if (SchemaEx5.isArrayType(type)) {
984
- return /* @__PURE__ */ React.createElement(ArrayField, {
985
- fieldProps: fieldState,
986
- label,
987
- ...props
988
- });
989
- }
990
- const Field = getFormField(fieldProps);
991
- if (Field) {
992
- return /* @__PURE__ */ React.createElement(Field, fieldProps);
993
- }
994
- const options = getSelectOptions(type);
995
- if (options) {
996
- const fieldProjections = projection?.getFieldProjections();
997
- const fieldProjection = fieldProjections?.find((fp) => fp.field.path === name);
998
- const selectOptions = fieldProjection?.props.options;
999
- return /* @__PURE__ */ React.createElement(SelectField, {
1000
- ...fieldProps,
1001
- options: options.map((option) => {
1002
- const selectOption = selectOptions?.find((so) => so.id === globalThis.String(option));
1003
- return {
1004
- value: option,
1005
- label: selectOption?.title ?? option.toString()
1006
- };
1007
- })
1008
- });
1009
- }
1010
- const refProps = getRefProps(type);
1011
- if (refProps) {
1012
- const inline = refInline || Annotation3.FormInlineAnnotation.getFromAst(refProps.ast).pipe(Option3.getOrElse(() => false));
1013
- if (inline && !refProps.isArray) {
1014
- return /* @__PURE__ */ React.createElement(InlineRefField, {
1015
- ...fieldProps,
1016
- ...refProps,
1017
- db,
1018
- useType: useType2,
1019
- onCreate,
1020
- resolveCreateEntry
1021
- });
1022
- }
1023
- const isCreateTarget = !createTypename || refProps.typename === createTypename;
1024
- return /* @__PURE__ */ React.createElement(RefField, {
1025
- ...fieldProps,
1026
- ...refProps,
1027
- createOptionLabel: isCreateTarget ? createOptionLabel : void 0,
1028
- createOptionIcon: isCreateTarget ? createOptionIcon : void 0,
1029
- createInitialValuePath: isCreateTarget ? createInitialValuePath : void 0,
1030
- createFieldMap: isCreateTarget ? createFieldMap3 : void 0,
1031
- db,
1032
- useType: useType2,
1033
- getOptions,
1034
- onCreate,
1035
- resolveCreateEntry
1036
- });
1037
- }
1038
- if (SchemaEx5.isNestedType(type)) {
1039
- const baseNode = SchemaEx5.findNode(type, SchemaEx5.isDiscriminatedUnion);
1040
- const typeLiteral = baseNode ? SchemaEx5.getDiscriminatedType(baseNode, fieldState.getValue()) : SchemaEx5.findNode(type, SchemaAST4.isTypeLiteral);
1041
- if (typeLiteral) {
1042
- const schema = Schema3.make(typeLiteral);
1043
- return /* @__PURE__ */ React.createElement(FormFieldSet, {
1044
- schema,
1045
- path,
1046
- readonly,
1047
- layout,
1048
- label,
1049
- collapsible: true,
1050
- projection,
1051
- fieldMap,
1052
- fieldProvider,
1053
- createOptionLabel,
1054
- createOptionIcon,
1055
- createInitialValuePath,
1056
- db,
1057
- useType: useType2,
1058
- getOptions,
1059
- onCreate,
1060
- resolveCreateEntry
1061
- });
1062
- }
1063
- }
1064
- return null;
1065
- };
1066
- FormField.displayName = "Form.FormField";
1067
- var CompactIconButton = (props) => {
1068
- return /* @__PURE__ */ React.createElement(IconButton, {
1069
- variant: "ghost",
1070
- iconOnly: true,
1071
- ...props
1072
- });
1073
- };
1074
- var getFormField = ({ type, format }) => {
1075
- if (SchemaAST4.isRefinement(type)) {
1076
- return getFormField({
1077
- type: type.from,
1078
- format
1079
- });
1080
- }
1081
- const formatField = Match.value(format).pipe(Match.withReturnType(), Match.when(Format3.TypeFormat.Date, () => DateField), Match.when(Format3.TypeFormat.DateTime, () => DateField), Match.when(Format3.TypeFormat.GeoPoint, () => GeoPointField), Match.when(Format3.TypeFormat.Markdown, () => MarkdownField), Match.when(Format3.TypeFormat.Password, () => PasswordField), Match.when(Format3.TypeFormat.Text, () => TextAreaField), Match.when(Format3.TypeFormat.Time, () => DateField), Match.orElse(() => void 0));
1082
- if (formatField) {
1083
- return formatField;
1084
- }
1085
- switch (type._tag) {
1086
- // TODO(wittjosiah): Schema.Any is currently used to represent template inputs.
1087
- case "AnyKeyword":
1088
- case "StringKeyword":
1089
- return TextField;
1090
- case "NumberKeyword":
1091
- return NumberField;
1092
- case "BooleanKeyword":
1093
- return BooleanField;
1094
- }
1095
- return void 0;
1096
- };
1097
- var getSelectOptions = (ast) => {
1098
- if (SchemaEx5.isLiteralUnion(ast)) {
1099
- return ast.types.map((type) => type.literal).filter((v) => v !== null);
1100
- }
1101
- return Format3.OptionsAnnotation.getFromAst(ast).pipe((annotation) => Option3.getOrUndefined(annotation));
1102
- };
1103
-
1104
- // src/components/Form/FormField/FormFieldHeader.tsx
1105
- import React3 from "react";
1106
-
1107
- // src/components/Form/FormField/FormRow.tsx
1108
- import { format as formatDate2 } from "date-fns";
1109
- import React2, { Component } from "react";
1110
- import { Format as Format4 } from "@dxos/echo";
1111
- import { Icon, Input, Tooltip } from "@dxos/react-ui";
1112
- import { mx as mx2 } from "@dxos/ui-theme";
1113
-
1114
- // src/components/Form/FormField/presentation.tsx
1115
- var presentationFor = (layout) => ({
1116
- layout,
1117
- isStatic: layout === "static",
1118
- showLabel: layout !== "inline",
1119
- showError: layout === "full"
1120
- });
1121
-
1122
- // src/components/Form/FormField/FormRow.tsx
1123
- var FormFieldLabel = ({ classNames, labelClassName, variant = "default", label, error, readonly, required, standalone, button, onClick }) => {
1124
- const styles3 = formTheme.styles({
1125
- variant
1126
- });
1127
- const labelClassNames = mx2("text-sm text-description", required && "after:content-['*'] after:ms-0.5 after:text-warning-text", styles3.fieldLabelText({
1128
- class: labelClassName
1129
- }));
1130
- const labelNode = readonly || standalone ? /* @__PURE__ */ React2.createElement("span", {
1131
- className: labelClassNames
1132
- }, label) : /* @__PURE__ */ React2.createElement(Input.Label, {
1133
- classNames: labelClassNames
1134
- }, label);
1135
- return /* @__PURE__ */ React2.createElement("div", {
1136
- className: styles3.fieldLabel({
1137
- class: mx2(onClick && "cursor-pointer", classNames)
1138
- }),
1139
- onClick
1140
- }, labelNode, error ? /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
1141
- asChild: true,
1142
- content: error,
1143
- side: "bottom"
1144
- }, /* @__PURE__ */ React2.createElement(Icon, {
1145
- icon: "ph--warning--regular",
1146
- size: 4,
1147
- classNames: "text-error-text"
1148
- })) : /* @__PURE__ */ React2.createElement("span", null), button);
1149
- };
1150
- FormFieldLabel.displayName = "Form.FieldLabel";
1151
- var formatStaticValue = (value3, format) => {
1152
- if (value3 == null) {
1153
- return "";
1154
- }
1155
- switch (format) {
1156
- case Format4.TypeFormat.DateTime:
1157
- case Format4.TypeFormat.Date:
1158
- case Format4.TypeFormat.Time: {
1159
- const date = new Date(value3);
1160
- if (Number.isNaN(date.getTime())) {
1161
- return String(value3);
1162
- }
1163
- const pattern = format === Format4.TypeFormat.DateTime ? "PPp" : format === Format4.TypeFormat.Date ? "PP" : "p";
1164
- return formatDate2(date, pattern);
1165
- }
1166
- default:
1167
- return String(value3);
1168
- }
1169
- };
1170
- var FORM_ROW_NAME = "Form.Row";
1171
- var FormRow = ({ classNames, children, readonly, presentation, label, description, jsonPath, format, required, standalone, renderStatic, validation, getStatus, getValue, rootRef }) => {
1172
- const { variant = "default" } = useFormContext(FORM_ROW_NAME);
1173
- const styles3 = formTheme.styles({
1174
- variant
1175
- });
1176
- const { showDescription } = formTheme.behavior[variant];
1177
- const resolved = presentationFor(presentation);
1178
- if (typeof children === "function" && getStatus && getValue) {
1179
- const { status, error } = getStatus();
1180
- const value3 = getValue();
1181
- if (resolved.isStatic && value3 == null) {
1182
- return null;
1183
- }
1184
- const control = resolved.isStatic ? renderStatic?.(value3) ?? /* @__PURE__ */ React2.createElement("p", {
1185
- className: "truncate min-w-0"
1186
- }, formatStaticValue(value3, format)) : children({
1187
- value: value3,
1188
- presentation: resolved
1189
- });
1190
- return /* @__PURE__ */ React2.createElement(Input.Root, {
1191
- validationValence: status
1192
- }, /* @__PURE__ */ React2.createElement("div", {
1193
- className: styles3.field({
1194
- class: classNames
1195
- }),
1196
- ref: rootRef
1197
- }, resolved.showLabel && /* @__PURE__ */ React2.createElement(FormFieldLabel, {
1198
- variant,
1199
- error,
1200
- readonly,
1201
- required,
1202
- standalone,
1203
- label,
1204
- path: jsonPath
1205
- }), showDescription && description && /* @__PURE__ */ React2.createElement(Input.Description, {
1206
- classNames: styles3.fieldDescription()
1207
- }, description), /* @__PURE__ */ React2.createElement("div", {
1208
- className: styles3.fieldControl()
1209
- }, control), resolved.showError && error && /* @__PURE__ */ React2.createElement("div", {
1210
- className: styles3.fieldValidation()
1211
- }, /* @__PURE__ */ React2.createElement(Input.DescriptionAndValidation, null, /* @__PURE__ */ React2.createElement(Input.Validation, null, error)))));
1212
- }
1213
- return /* @__PURE__ */ React2.createElement("div", {
1214
- ref: rootRef,
1215
- className: styles3.field({
1216
- class: classNames
1217
- })
1218
- }, resolved.showLabel && label && /* @__PURE__ */ React2.createElement(FormFieldLabel, {
1219
- variant,
1220
- readonly,
1221
- required,
1222
- standalone: true,
1223
- label,
1224
- path: jsonPath
1225
- }), showDescription && description && /* @__PURE__ */ React2.createElement("p", {
1226
- className: styles3.fieldDescription()
1227
- }, description), /* @__PURE__ */ React2.createElement("div", {
1228
- className: styles3.fieldControl()
1229
- }, typeof children === "function" ? null : children), validation && /* @__PURE__ */ React2.createElement("div", {
1230
- className: styles3.fieldValidation()
1231
- }, validation));
1232
- };
1233
- FormRow.displayName = FORM_ROW_NAME;
1234
- var FormFieldErrorBoundary = class extends Component {
1235
- static getDerivedStateFromError(error) {
1236
- return {
1237
- error
1238
- };
1239
- }
1240
- state = {
1241
- error: void 0
1242
- };
1243
- componentDidUpdate(prevProps) {
1244
- if (prevProps.path !== this.props.path) {
1245
- this.resetError();
1246
- }
1247
- }
1248
- render() {
1249
- if (this.state.error) {
1250
- return /* @__PURE__ */ React2.createElement("div", {
1251
- className: "flex gap-2 border border-error-border font-mono text-sm"
1252
- }, /* @__PURE__ */ React2.createElement("span", {
1253
- className: "bg-error-bg text-base-fg px-1 font-thin"
1254
- }, "ERROR"), String(this.props.path?.join(".")));
1255
- }
1256
- return this.props.children;
1257
- }
1258
- resetError() {
1259
- this.setState({
1260
- error: void 0
1261
- });
1262
- }
1263
- };
1264
-
1265
- // src/components/Form/FormField/FormFieldHeader.tsx
1266
- var FormFieldHeader = ({ label, path, required, readonly, classNames, add, actions, onClick }) => /* @__PURE__ */ React3.createElement(FormFieldLabel, {
1267
- standalone: true,
1268
- classNames,
1269
- label,
1270
- required,
1271
- readonly,
1272
- path,
1273
- button: !readonly && add || actions ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, !readonly && add && /* @__PURE__ */ React3.createElement(CompactIconButton, {
1274
- disabled: add.disabled,
1275
- icon: add.icon ?? "ph--plus--regular",
1276
- label: add.label,
1277
- onClick: (event) => {
1278
- event.stopPropagation();
1279
- add.onClick();
1280
- }
1281
- }), actions) : void 0,
1282
- onClick
1283
- });
1284
-
1285
- // src/components/Form/FormField/fields/ArrayField/ArrayField.tsx
1286
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx";
1287
- var DND_ID = "__dndId";
1288
- var isOrderedItem = (item) => !!item && typeof item === "object" && typeof item[DND_ID] === "string";
1289
- var getOrderedId = (item) => item[DND_ID];
1290
- var ArrayField = ({ type, path, label, readonly, layout, fieldProps: inputProps, ...props }) => {
1291
- const { t } = useTranslation2(translationKey2);
1292
- const elementType = SchemaEx6.getArrayElementType(type);
1293
- const { onValueChange } = inputProps;
1294
- const values = useFormValues(ArrayField.displayName, path, () => []);
1295
- const ordered = Annotation4.FormOrderedAnnotation.getFromAst(type).pipe(Option4.getOrElse(() => false));
1296
- const { db, onCreate, getCreateDefaults, useType: useType2 = defaultUseType } = props;
1297
- const createTypename = Annotation4.FormCreateAnnotation.getFromAst(type).pipe(Option4.getOrUndefined);
1298
- const createSchema = useType2(db, createTypename ? DXN.make(createTypename) : void 0);
1299
- const createInline = !!createTypename && !!elementType && Ref3.isRefType(elementType) && !readonly && layout !== "static";
1300
- const idsRef = useRef3([]);
1301
- const counterRef = useRef3(0);
1302
- const nextId = useCallback3(() => `${DND_ID}-${counterRef.current++}`, []);
1303
- const handleAddCreate = useCallback3(async () => {
1304
- if (!createSchema || !onCreate) {
1305
- return;
1306
- }
1307
- const defaults = getCreateDefaults?.({
1308
- jsonPath: SchemaEx6.createJsonPath(path ?? []),
1309
- schema: createSchema
1310
- }) ?? {};
1311
- const created = await onCreate(createSchema, defaults);
1312
- if (!created) {
1313
- return;
1314
- }
1315
- idsRef.current.push(nextId());
1316
- onValueChange(type, [
1317
- ...values ?? [],
1318
- Ref3.make(created)
1319
- ]);
1320
- }, [
1321
- createSchema,
1322
- onCreate,
1323
- getCreateDefaults,
1324
- onValueChange,
1325
- type,
1326
- path,
1327
- values,
1328
- nextId
1329
- ]);
1330
- const getDefaultObjectValue = (typeNode) => {
1331
- const baseNode = SchemaEx6.findNode(typeNode, SchemaEx6.isDiscriminatedUnion);
1332
- const typeLiteral = baseNode ? SchemaEx6.getDiscriminatedType(baseNode, {}) : SchemaEx6.findNode(typeNode, SchemaAST5.isTypeLiteral);
1333
- if (!typeLiteral) {
1334
- return {};
1335
- }
1336
- return Object.fromEntries(getFormProperties(typeLiteral).map((prop) => {
1337
- const defaultValue = SchemaAST5.getDefaultAnnotation(prop.type).pipe((annotation) => Option4.getOrUndefined(annotation));
1338
- return [
1339
- prop.name,
1340
- defaultValue
1341
- ];
1342
- }));
1343
- };
1344
- const handleAdd = useCallback3(() => {
1345
- let defaultValue;
1346
- try {
1347
- defaultValue = elementType && SchemaEx6.isNestedType(elementType) ? getDefaultObjectValue(elementType) : getDefaultValue(elementType);
1348
- } catch (err) {
1349
- log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 97, S: void 0 });
1350
- return;
1351
- }
1352
- idsRef.current.push(nextId());
1353
- onValueChange(type, [
1354
- ...values ?? [],
1355
- defaultValue
1356
- ]);
1357
- }, [
1358
- onValueChange,
1359
- type,
1360
- elementType,
1361
- values,
1362
- nextId
1363
- ]);
1364
- const handleDelete = useCallback3((idx) => {
1365
- idsRef.current.splice(idx, 1);
1366
- onValueChange(type, values?.filter((_, i) => i !== idx));
1367
- }, [
1368
- onValueChange,
1369
- type,
1370
- values
1371
- ]);
1372
- const handleMove = useCallback3((fromIndex, toIndex) => {
1373
- const next = [
1374
- ...values ?? []
1375
- ];
1376
- arrayMove(next, fromIndex, toIndex);
1377
- arrayMove(idsRef.current, fromIndex, toIndex);
1378
- onValueChange(type, next);
1379
- }, [
1380
- onValueChange,
1381
- type,
1382
- values
1383
- ]);
1384
- if (!elementType || (readonly || layout === "static") && values && values.length < 1) {
1385
- return null;
1386
- }
1387
- const renderItemAsObject = elementType && SchemaEx6.isNestedType(elementType) && !Ref3.isRefType(elementType);
1388
- const renderField = (index, isLast) => {
1389
- const field = /* @__PURE__ */ React4.createElement(FormField, {
1390
- ...props,
1391
- autoFocus: isLast,
1392
- type: elementType,
1393
- ...renderItemAsObject || createInline ? {
1394
- name: null
1395
- } : {
1396
- label
1397
- },
1398
- path: [
1399
- ...path ?? [],
1400
- index
1401
- ],
1402
- readonly: readonly || layout === "static",
1403
- layout: renderItemAsObject ? layout === "static" ? "static" : void 0 : "inline",
1404
- refInline: createInline || void 0
1405
- });
1406
- return createInline ? /* @__PURE__ */ React4.createElement("div", {
1407
- role: "none",
1408
- className: "w-full min-w-0 [--dx-col:auto]"
1409
- }, field) : field;
1410
- };
1411
- const header = (layout !== "static" || values && values.length > 0) && /* @__PURE__ */ React4.createElement(FormFieldHeader, {
1412
- label,
1413
- path: SchemaEx6.createJsonPath(path ?? []),
1414
- readonly,
1415
- add: layout !== "static" ? {
1416
- label: t("add-item.button"),
1417
- onClick: createInline ? () => void handleAddCreate() : handleAdd
1418
- } : void 0
1419
- });
1420
- if (ordered && !readonly && layout !== "static") {
1421
- const ids = idsRef.current;
1422
- const count = values?.length ?? 0;
1423
- while (ids.length < count) {
1424
- ids.push(nextId());
1425
- }
1426
- if (ids.length > count) {
1427
- ids.length = count;
1428
- }
1429
- const items = (values ?? []).map((value3, index) => ({
1430
- value: value3,
1431
- index,
1432
- [DND_ID]: ids[index]
1433
- }));
1434
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, header, /* @__PURE__ */ React4.createElement(OrderedList.Root, {
1435
- items,
1436
- isItem: isOrderedItem,
1437
- getId: getOrderedId,
1438
- onMove: handleMove
1439
- }, ({ items: items2 }) => /* @__PURE__ */ React4.createElement(OrderedList.Content, null, items2.map((item) => /* @__PURE__ */ React4.createElement(OrderedList.Item, {
1440
- key: getOrderedId(item),
1441
- id: getOrderedId(item),
1442
- item,
1443
- classNames: "grid grid-cols-[min-content_1fr_min-content] items-start gap-1 p-0 pb-1"
1444
- }, /* @__PURE__ */ React4.createElement(OrderedList.DragHandle, null), renderField(item.index, item.index === items2.length - 1), /* @__PURE__ */ React4.createElement(CompactIconButton, {
1445
- icon: "ph--x--regular",
1446
- label: t("remove-item.button"),
1447
- onClick: () => handleDelete(item.index)
1448
- }))))));
1449
- }
1450
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, header, /* @__PURE__ */ React4.createElement("div", {
1451
- className: "flex flex-col gap-2"
1452
- }, values?.map((_, index) => {
1453
- const isLast = index === values.length - 1;
1454
- return /* @__PURE__ */ React4.createElement("div", {
1455
- key: index,
1456
- className: mx3("grid grid-cols-[minmax(0,1fr)_min-content] gap-1", renderItemAsObject || createInline ? "items-start" : "items-center")
1457
- }, renderField(index, isLast), !readonly && layout !== "static" && /* @__PURE__ */ React4.createElement(CompactIconButton, {
1458
- icon: "ph--x--regular",
1459
- label: t("remove-item.button"),
1460
- onClick: () => handleDelete(index)
1461
- }));
1462
- })));
1463
- };
1464
- ArrayField.displayName = "Form.ArrayField";
1465
- var getDefaultValue = (ast) => {
1466
- switch (ast?._tag) {
1467
- case "StringKeyword": {
1468
- return "";
1469
- }
1470
- case "NumberKeyword": {
1471
- return 0;
1472
- }
1473
- case "BooleanKeyword": {
1474
- return false;
1475
- }
1476
- case "Suspend": {
1477
- return getDefaultValue(ast.f());
1478
- }
1479
- case "Refinement": {
1480
- const jsonSchema = Option4.getOrUndefined(SchemaAST5.getJSONSchemaAnnotation(ast));
1481
- if (jsonSchema != null && "minimum" in jsonSchema && typeof jsonSchema.minimum === "number") {
1482
- return jsonSchema.minimum;
1483
- }
1484
- return getDefaultValue(ast.from);
1485
- }
1486
- default: {
1487
- if (ast && SchemaEx6.isNestedType(ast)) {
1488
- return {};
1489
- } else {
1490
- throw new Error(`Unsupported type: ${ast?._tag}`);
1491
- }
1492
- }
1493
- }
1494
- };
1495
-
1496
- // src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.tsx
1497
- import React6, { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef4 } from "react";
1498
-
1499
- // src/components/Form/FormField/fields/SelectField/SelectField.tsx
1500
- import React5, { useCallback as useCallback4 } from "react";
1501
- import { invariant } from "@dxos/invariant";
1502
- import { Icon as Icon2, Select } from "@dxos/react-ui";
1503
- import { getStyles } from "@dxos/ui-theme";
1504
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/Form/FormField/fields/SelectField/SelectField.tsx";
1505
- var SelectField = ({ type, readonly, placeholder, options, onValueChange, ...props }) => {
1506
- const handleValueChange = useCallback4((value3) => onValueChange(type, value3), [
1507
- type,
1508
- onValueChange
1509
- ]);
1510
- return /* @__PURE__ */ React5.createElement(FormRow, {
1511
- readonly,
1512
- renderStatic: (value3) => /* @__PURE__ */ React5.createElement("p", {
1513
- className: "truncate min-w-0"
1514
- }, options?.find(({ value: optionValue }) => optionValue === value3)?.label ?? String(value3)),
1515
- ...props
1516
- }, ({ value: value3 }) => /* @__PURE__ */ React5.createElement(Select.Root, {
1517
- value: value3,
1518
- onValueChange: handleValueChange
1519
- }, /* @__PURE__ */ React5.createElement(Select.TriggerButton, {
1520
- classNames: "w-full",
1521
- disabled: !!readonly,
1522
- placeholder
1523
- }), options?.length !== 0 && /* @__PURE__ */ React5.createElement(Select.Portal, null, /* @__PURE__ */ React5.createElement(Select.Content, null, /* @__PURE__ */ React5.createElement(Select.Viewport, null, options?.map(({ value: value4, label, secondaryLabel, icon, iconHue }) => (
1524
- // NOTE: Numeric values are converted to and from strings.
1525
- /* @__PURE__ */ React5.createElement(Select.Option, {
1526
- key: String(value4),
1527
- value: String(value4)
1528
- }, /* @__PURE__ */ React5.createElement("span", {
1529
- className: "flex items-center flex-row gap-2"
1530
- }, icon && /* @__PURE__ */ React5.createElement(Icon2, {
1531
- icon,
1532
- classNames: getIconHueStyles(iconHue)
1533
- }), label ?? String(value4), secondaryLabel && /* @__PURE__ */ React5.createElement("span", {
1534
- className: "text-subdued text-xs"
1535
- }, secondaryLabel)))
1536
- ))), /* @__PURE__ */ React5.createElement(Select.Arrow, null)))));
1537
- };
1538
- var getIconHueStyles = (iconHue) => {
1539
- const styles3 = iconHue ? getStyles(iconHue) : void 0;
1540
- return styles3?.fg;
1541
- };
1542
- var createSelectField = ({ options, defaultLabel = "Default" }) => {
1543
- const normalized = options.map((option) => typeof option === "string" ? {
1544
- value: option,
1545
- label: option
1546
- } : option);
1547
- const hasDefault = defaultLabel !== null;
1548
- const sentinel = "__default__";
1549
- invariant(!normalized.some((option) => option.value === sentinel), `createSelectField: option value '${sentinel}' is reserved.`, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 57, S: void 0, A: ["!normalized.some((option)=>option.value === sentinel)", "`createSelectField: option value '${sentinel}' is reserved.`"] });
1550
- return ({ type, readonly, onValueChange, ...props }) => /* @__PURE__ */ React5.createElement(FormRow, {
1551
- readonly,
1552
- renderStatic: (value3) => /* @__PURE__ */ React5.createElement("p", {
1553
- className: "truncate min-w-0"
1554
- }, normalized.find((option) => option.value === value3)?.label ?? String(value3 ?? "")),
1555
- ...props
1556
- }, ({ value: value3 }) => /* @__PURE__ */ React5.createElement(Select.Root, {
1557
- disabled: !!readonly,
1558
- value: value3 ?? sentinel,
1559
- onValueChange: (next) => onValueChange(type, hasDefault && next === sentinel ? void 0 : next)
1560
- }, /* @__PURE__ */ React5.createElement(Select.TriggerButton, {
1561
- classNames: "w-full",
1562
- disabled: !!readonly
1563
- }), normalized.length > 0 && /* @__PURE__ */ React5.createElement(Select.Portal, null, /* @__PURE__ */ React5.createElement(Select.Content, null, /* @__PURE__ */ React5.createElement(Select.Viewport, null, hasDefault && /* @__PURE__ */ React5.createElement(Select.Option, {
1564
- value: sentinel
1565
- }, defaultLabel), normalized.map((option) => /* @__PURE__ */ React5.createElement(Select.Option, {
1566
- key: option.value,
1567
- value: option.value
1568
- }, option.label ?? option.value))), /* @__PURE__ */ React5.createElement(Select.Arrow, null)))));
1569
- };
1570
-
1571
- // src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.tsx
1572
- var AsyncSelectField = ({ lookup, ...fieldProps }) => {
1573
- const values = useFormValues(AsyncSelectField.displayName);
1574
- const subset = useMemo4(() => pickValues(values, lookup.deps), [
1575
- values,
1576
- lookup.deps
1577
- ]);
1578
- const key = useMemo4(() => JSON.stringify(subset), [
1579
- subset
1580
- ]);
1581
- const { loading, data } = useAsyncFieldEffect(() => lookup.load(subset), key);
1582
- const options = data ?? [];
1583
- const { type, placeholder } = fieldProps;
1584
- const current = fieldProps.getValue();
1585
- const onValueChangeRef = useRef4(fieldProps.onValueChange);
1586
- onValueChangeRef.current = fieldProps.onValueChange;
1587
- const autoSelectedDataRef = useRef4(void 0);
1588
- useEffect4(() => {
1589
- if ((current == null || current === "") && data?.length === 1 && data !== autoSelectedDataRef.current) {
1590
- autoSelectedDataRef.current = data;
1591
- onValueChangeRef.current(type, data[0].value);
1592
- }
1593
- }, [
1594
- current,
1595
- data,
1596
- type
1597
- ]);
1598
- const resolvedPlaceholder = loading ? "Loading\u2026" : options.length === 0 ? "No options available" : placeholder;
1599
- return /* @__PURE__ */ React6.createElement(SelectField, {
1600
- ...fieldProps,
1601
- placeholder: resolvedPlaceholder,
1602
- options: options.map(({ value: value3, label, secondaryLabel, icon }) => ({
1603
- value: value3,
1604
- label,
1605
- secondaryLabel,
1606
- icon
1607
- }))
1608
- });
1609
- };
1610
- AsyncSelectField.displayName = "Form.AsyncSelectField";
1611
-
1612
- // src/components/Form/FormField/fields/AutofillField/AutofillField.tsx
1613
- import React8, { useEffect as useEffect5, useMemo as useMemo5, useRef as useRef5 } from "react";
1614
-
1615
- // src/components/Form/FormField/fields/TextField/TextField.tsx
1616
- import React7, { useCallback as useCallback5 } from "react";
1617
- import { Input as Input2 } from "@dxos/react-ui";
1618
- var TextField = ({ type, readonly, placeholder, onBlur, onValueChange, ...props }) => {
1619
- const handleChange = useCallback5((event) => onValueChange(type, event.target.value), [
1620
- type,
1621
- onValueChange
1622
- ]);
1623
- return /* @__PURE__ */ React7.createElement(FormRow, {
1624
- readonly,
1625
- ...props
1626
- }, ({ value: value3 = "" }) => /* @__PURE__ */ React7.createElement(Input2.TextInput, {
1627
- noAutoFill: true,
1628
- disabled: !!readonly,
1629
- placeholder,
1630
- value: value3,
1631
- onBlur,
1632
- onChange: handleChange
1633
- }));
1634
- };
1635
-
1636
- // src/components/Form/FormField/fields/AutofillField/AutofillField.tsx
1637
- var AutofillField = ({ autofill: autofill2, ...fieldProps }) => {
1638
- const values = useFormValues(AutofillField.displayName);
1639
- const subset = useMemo5(() => pickValues(values, autofill2.deps), [
1640
- values,
1641
- autofill2.deps
1642
- ]);
1643
- const key = useMemo5(() => JSON.stringify(subset), [
1644
- subset
1645
- ]);
1646
- const { data } = useAsyncFieldEffect(() => autofill2.derive(subset), key);
1647
- const { type } = fieldProps;
1648
- const getValueRef = useRef5(fieldProps.getValue);
1649
- getValueRef.current = fieldProps.getValue;
1650
- const onValueChangeRef = useRef5(fieldProps.onValueChange);
1651
- onValueChangeRef.current = fieldProps.onValueChange;
1652
- const lastFilledRef = useRef5(void 0);
1653
- useEffect5(() => {
1654
- const current = getValueRef.current();
1655
- if (typeof data !== "string" || data.length === 0) {
1656
- if (lastFilledRef.current != null && current === lastFilledRef.current) {
1657
- lastFilledRef.current = void 0;
1658
- onValueChangeRef.current(type, "");
1659
- }
1660
- return;
1661
- }
1662
- if (current === data) {
1663
- lastFilledRef.current = data;
1664
- return;
1665
- }
1666
- if (current == null || current === "" || current === lastFilledRef.current) {
1667
- lastFilledRef.current = data;
1668
- onValueChangeRef.current(type, data);
1669
- }
1670
- }, [
1671
- data,
1672
- type
1673
- ]);
1674
- return /* @__PURE__ */ React8.createElement(TextField, fieldProps);
1675
- };
1676
- AutofillField.displayName = "Form.AutofillField";
1677
-
1678
- // src/components/Form/FormField/fields/BooleanField/BooleanField.tsx
1679
- import React9, { useCallback as useCallback6 } from "react";
1680
- import { Input as Input3 } from "@dxos/react-ui";
1681
- var BooleanField = ({ type, readonly, onValueChange, ...props }) => {
1682
- const handleChange = useCallback6((value3) => onValueChange?.(type, value3), [
1683
- type,
1684
- onValueChange
1685
- ]);
1686
- return /* @__PURE__ */ React9.createElement(FormRow, {
1687
- readonly,
1688
- ...props
1689
- }, ({ value: value3 }) => /* @__PURE__ */ React9.createElement(Input3.Block, null, /* @__PURE__ */ React9.createElement(Input3.Switch, {
1690
- disabled: !!readonly,
1691
- checked: value3,
1692
- onCheckedChange: handleChange
1693
- })));
1694
- };
1695
-
1696
- // src/components/Form/FormField/fields/ComboboxField/ComboboxField.tsx
1697
- import React10, { useCallback as useCallback7, useMemo as useMemo6, useState as useState3 } from "react";
1698
- import { Combobox } from "@dxos/react-ui-list";
1699
- var ComboboxField = ({ lookup, type, readonly, placeholder, onValueChange, ...props }) => {
1700
- const values = useFormValues(ComboboxField.displayName);
1701
- const ownKey = props.jsonPath;
1702
- const [query, setQuery] = useState3("");
1703
- const lookupValues = useMemo6(() => {
1704
- const subset = pickValues(values, lookup.deps);
1705
- if (ownKey && lookup.deps.includes(ownKey)) {
1706
- subset[ownKey] = query;
1707
- }
1708
- return subset;
1709
- }, [
1710
- values,
1711
- lookup.deps,
1712
- ownKey,
1713
- query
1714
- ]);
1715
- const key = useMemo6(() => JSON.stringify(lookupValues), [
1716
- lookupValues
1717
- ]);
1718
- const { data } = useAsyncFieldEffect(() => lookup.load(lookupValues), key);
1719
- const trimmed = query.trim();
1720
- const normalized = trimmed.toLowerCase();
1721
- const results = useMemo6(() => normalized.length === 0 ? [] : (data ?? []).filter((option) => (option.label ?? option.value).toLowerCase().includes(normalized)), [
1722
- data,
1723
- normalized
1724
- ]);
1725
- const hasExact = results.some((option) => option.value === trimmed);
1726
- const handleValueChange = useCallback7((next) => onValueChange(type, next), [
1727
- onValueChange,
1728
- type
1729
- ]);
1730
- const handleOpenChange = useCallback7((open) => {
1731
- if (!open) {
1732
- setQuery("");
1733
- }
1734
- }, []);
1735
- return /* @__PURE__ */ React10.createElement(FormRow, {
1736
- readonly,
1737
- renderStatic: (value3) => /* @__PURE__ */ React10.createElement("p", {
1738
- className: "truncate min-w-0"
1739
- }, value3 ?? ""),
1740
- ...props
1741
- }, ({ value: value3 = "" }) => /* @__PURE__ */ React10.createElement(Combobox.Root, {
1742
- value: value3,
1743
- onValueChange: handleValueChange,
1744
- onOpenChange: handleOpenChange,
1745
- placeholder
1746
- }, /* @__PURE__ */ React10.createElement(Combobox.Trigger, {
1747
- disabled: !!readonly,
1748
- classNames: "w-full"
1749
- }), /* @__PURE__ */ React10.createElement(Combobox.Portal, null, /* @__PURE__ */ React10.createElement(Combobox.Content, {
1750
- resetSelectionOnChange: true
1751
- }, /* @__PURE__ */ React10.createElement(Combobox.Input, {
1752
- autoFocus: true,
1753
- value: query,
1754
- onValueChange: setQuery,
1755
- placeholder
1756
- }), /* @__PURE__ */ React10.createElement(Combobox.List, null, results.map((option) => /* @__PURE__ */ React10.createElement(Combobox.Item, {
1757
- key: option.value,
1758
- value: option.value,
1759
- label: option.label ?? option.value,
1760
- suffix: option.secondaryLabel
1761
- })), normalized.length > 0 && !hasExact && /* @__PURE__ */ React10.createElement(Combobox.Item, {
1762
- value: trimmed,
1763
- label: trimmed
1764
- })), /* @__PURE__ */ React10.createElement(Combobox.Arrow, null)))));
1765
- };
1766
- ComboboxField.displayName = "Form.ComboboxField";
1767
-
1768
- // src/components/Form/FormField/fields/DateField/DateField.tsx
1769
- import { format as formatDate3 } from "date-fns";
1770
- import React11, { useCallback as useCallback8 } from "react";
1771
- import { Format as Format5 } from "@dxos/echo";
1772
- import { Input as Input4 } from "@dxos/react-ui";
1773
- var isoToLocalDateTime = (value3) => {
1774
- if (!value3) {
1775
- return "";
1776
- }
1777
- const date = new Date(value3);
1778
- if (Number.isNaN(date.getTime())) {
1779
- return "";
1780
- }
1781
- return formatDate3(date, "yyyy-MM-dd'T'HH:mm");
1782
- };
1783
- var localDateTimeToIso = (value3) => {
1784
- if (!value3) {
1785
- return void 0;
1786
- }
1787
- const date = new Date(value3);
1788
- return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
1789
- };
1790
- var DateField = ({ type, format, readonly, placeholder: _placeholder, onValueChange, onBlur: _onBlur, ...props }) => {
1791
- const handleSimpleChange = useCallback8((next) => onValueChange(type, next), [
1792
- type,
1793
- onValueChange
1794
- ]);
1795
- const handleDateTimeChange = useCallback8((next) => {
1796
- const iso = localDateTimeToIso(next);
1797
- onValueChange(type, iso);
1798
- }, [
1799
- type,
1800
- onValueChange
1801
- ]);
1802
- return /* @__PURE__ */ React11.createElement(FormRow, {
1803
- ...props,
1804
- readonly,
1805
- format
1806
- }, ({ value: value3 }) => {
1807
- switch (format) {
1808
- case Format5.TypeFormat.Date:
1809
- return /* @__PURE__ */ React11.createElement("div", {
1810
- className: "grid grid-cols-[minmax(0,1fr)_min-content] gap-1 items-stretch"
1811
- }, /* @__PURE__ */ React11.createElement(Input4.Date, {
1812
- classNames: "min-w-0 overflow-hidden",
1813
- disabled: readonly,
1814
- value: value3 ?? "",
1815
- onValueChange: handleSimpleChange
1816
- }), /* @__PURE__ */ React11.createElement(Input4.TriggerIcon, null));
1817
- case Format5.TypeFormat.Time:
1818
- return /* @__PURE__ */ React11.createElement(Input4.Time, {
1819
- disabled: !!readonly,
1820
- value: value3 ?? "",
1821
- onValueChange: handleSimpleChange
1822
- });
1823
- case Format5.TypeFormat.DateTime:
1824
- default:
1825
- return /* @__PURE__ */ React11.createElement("div", {
1826
- className: "grid grid-cols-[minmax(0,1fr)_min-content] gap-1 items-stretch"
1827
- }, /* @__PURE__ */ React11.createElement(Input4.DateTime, {
1828
- classNames: "min-w-0 overflow-hidden",
1829
- disabled: readonly,
1830
- value: isoToLocalDateTime(value3),
1831
- onValueChange: handleDateTimeChange
1832
- }), /* @__PURE__ */ React11.createElement(Input4.TriggerIcon, null));
1833
- }
1834
- });
1835
- };
1836
-
1837
- // src/components/Form/FormField/fields/GeoPointField/GeoPointField.tsx
1838
- import React12, { useCallback as useCallback9, useEffect as useEffect6, useMemo as useMemo7, useState as useState4 } from "react";
1839
- import { GeoLocation as GeoLocation3 } from "@dxos/echo/Format";
1840
- import { Input as Input5, useTranslation as useTranslation3 } from "@dxos/react-ui";
1841
- import { safeParseFloat } from "@dxos/util";
1842
- import { translationKey as translationKey3 } from "#translations";
1843
- var GeoPointField = ({ type, readonly, getValue, onValueChange, onBlur, ...props }) => {
1844
- const { t } = useTranslation3(translationKey3);
1845
- const geoPoint = useMemo7(() => getValue() ?? [
1846
- 0,
1847
- 0
1848
- ], [
1849
- getValue
1850
- ]);
1851
- const value3 = useMemo7(() => GeoLocation3.fromGeoPoint(geoPoint), [
1852
- geoPoint
1853
- ]);
1854
- const [longitudeText, setLongitudeText] = useState4(value3.longitude?.toString());
1855
- const [latitudeText, setLatitudeText] = useState4(value3.latitude?.toString());
1856
- useEffect6(() => {
1857
- const location = GeoLocation3.fromGeoPoint(geoPoint);
1858
- setLongitudeText(location.longitude?.toString());
1859
- setLatitudeText(location.latitude?.toString());
1860
- }, [
1861
- geoPoint
1862
- ]);
1863
- const handleChange = useCallback9((coordinateType, setText) => (event) => {
1864
- const inputText = event.target.value;
1865
- setText(inputText);
1866
- if (inputText !== "" && inputText !== "-") {
1867
- const coord = safeParseFloat(inputText);
1868
- if (coord !== void 0 && !isNaN(coord)) {
1869
- const currentLocation = GeoLocation3.fromGeoPoint(getValue() ?? [
1870
- 0,
1871
- 0
1872
- ]);
1873
- const newLocation = {
1874
- ...currentLocation,
1875
- [coordinateType]: coord
1876
- };
1877
- const newValue = GeoLocation3.toGeoPoint(newLocation);
1878
- onValueChange(type, newValue);
1879
- }
1880
- }
1881
- }, [
1882
- type,
1883
- getValue,
1884
- onValueChange
1885
- ]);
1886
- return /* @__PURE__ */ React12.createElement(FormRow, {
1887
- readonly,
1888
- getValue,
1889
- standalone: true,
1890
- renderStatic: (point) => {
1891
- const location = GeoLocation3.fromGeoPoint(point ?? [
1892
- 0,
1893
- 0
1894
- ]);
1895
- return !location.latitude && !location.longitude ? null : /* @__PURE__ */ React12.createElement(LatLng, location);
1896
- },
1897
- ...props
1898
- }, ({ presentation }) => /* @__PURE__ */ React12.createElement("div", {
1899
- className: "grid grid-cols-2 gap-form-gap"
1900
- }, /* @__PURE__ */ React12.createElement("div", null, /* @__PURE__ */ React12.createElement(Input5.Root, null, presentation.showLabel && /* @__PURE__ */ React12.createElement(Input5.Label, null, t("latitude.label")), /* @__PURE__ */ React12.createElement(Input5.TextInput, {
1901
- type: "number",
1902
- step: "0.00001",
1903
- min: "-90",
1904
- max: "90",
1905
- disabled: !!readonly,
1906
- placeholder: t("latitude.placeholder"),
1907
- value: latitudeText ?? "",
1908
- onChange: handleChange("latitude", setLatitudeText),
1909
- onBlur
1910
- }))), /* @__PURE__ */ React12.createElement("div", null, /* @__PURE__ */ React12.createElement(Input5.Root, null, presentation.showLabel && /* @__PURE__ */ React12.createElement(Input5.Label, null, t("longitude.label")), /* @__PURE__ */ React12.createElement(Input5.TextInput, {
1911
- type: "number",
1912
- step: "0.00001",
1913
- min: "-180",
1914
- max: "180",
1915
- disabled: !!readonly,
1916
- placeholder: t("longitude.placeholder"),
1917
- value: longitudeText ?? "",
1918
- onChange: handleChange("longitude", setLongitudeText),
1919
- onBlur
1920
- })))));
1921
- };
1922
- var LatLng = ({ latitude = 0, longitude = 0 }) => {
1923
- const latHem = latitude >= 0 ? "N" : "S";
1924
- const lngHem = longitude >= 0 ? "E" : "W";
1925
- return /* @__PURE__ */ React12.createElement("span", {
1926
- className: "inline-flex items-center gap-form-gap"
1927
- }, /* @__PURE__ */ React12.createElement("span", null, /* @__PURE__ */ React12.createElement("span", null, Math.abs(latitude).toFixed(5)), /* @__PURE__ */ React12.createElement("span", {
1928
- className: "text-subdued"
1929
- }, "\xB0", latHem)), /* @__PURE__ */ React12.createElement("span", null, /* @__PURE__ */ React12.createElement("span", null, Math.abs(longitude).toFixed(5)), /* @__PURE__ */ React12.createElement("span", {
1930
- className: "text-subdued"
1931
- }, "\xB0", lngHem)));
1932
- };
1933
-
1934
- // src/components/Form/FormField/fields/MarkdownField/MarkdownField.tsx
1935
- import { useAtomValue } from "@effect-atom/atom-react";
1936
- import React13, { useCallback as useCallback10, useMemo as useMemo8 } from "react";
1937
- import { Ref as Ref4 } from "@dxos/echo";
1938
- import { Doc } from "@dxos/echo-doc";
1939
- import { IconButton as IconButton2, useTranslation as useTranslation4 } from "@dxos/react-ui";
1940
- import { Editor, useBasicMarkdownExtensions } from "@dxos/react-ui-editor";
1941
- import { Text } from "@dxos/schema";
1942
- import { createDataExtensions } from "@dxos/ui-editor";
1943
- import { translationKey as translationKey4 } from "#translations";
1944
- var editorClassNames = [
1945
- "min-h-[6lh] transition-colors rounded-xs p-1 px-2 bg-input-surface border border-input-separator",
1946
- "focus:border-separator focus-within:border-separator focus-within:bg-focus-surface",
1947
- "focus-within:ring-2 focus-within:ring-offset-0 focus-within:ring-focus-ring-subtle focus-within:z-[1]"
1948
- ];
1949
- var MarkdownField = ({ type, readonly, placeholder, db, getValue, onValueChange, ...props }) => {
1950
- const isRef = Ref4.isRefType(type);
1951
- const renderStatic = (value3) => {
1952
- if (isRef) {
1953
- const reference = value3;
1954
- return reference ? /* @__PURE__ */ React13.createElement(RefStaticText, {
1955
- reference
1956
- }) : null;
1957
- }
1958
- return /* @__PURE__ */ React13.createElement("p", {
1959
- className: "whitespace-pre-wrap"
1960
- }, value3 ?? "");
1961
- };
1962
- const renderEditor = (value3) => {
1963
- if (isRef) {
1964
- const reference = value3;
1965
- if (reference) {
1966
- return /* @__PURE__ */ React13.createElement(RefMarkdownEditor, {
1967
- reference,
1968
- placeholder,
1969
- readonly: !!readonly
1970
- });
1971
- }
1972
- if (readonly) {
1973
- return null;
1974
- }
1975
- return /* @__PURE__ */ React13.createElement(CreateTextButton, {
1976
- db,
1977
- disabled: !db,
1978
- onCreate: (ref) => onValueChange(type, ref)
1979
- });
1980
- }
1981
- return /* @__PURE__ */ React13.createElement(StringMarkdownEditor, {
1982
- value: value3 ?? "",
1983
- placeholder,
1984
- readonly: !!readonly,
1985
- onChange: (next) => onValueChange(type, next)
1986
- });
1987
- };
1988
- return /* @__PURE__ */ React13.createElement(FormRow, {
1989
- readonly,
1990
- getValue,
1991
- renderStatic,
1992
- ...props
1993
- }, ({ value: value3 }) => renderEditor(value3));
1994
- };
1995
- var RefStaticText = ({ reference }) => {
1996
- const text = useAtomValue(useMemo8(() => reference.atom, [
1997
- reference
1998
- ]));
1999
- if (!text?.content) {
2000
- return null;
2001
- }
2002
- return /* @__PURE__ */ React13.createElement("p", {
2003
- className: "whitespace-pre-wrap"
2004
- }, text.content);
2005
- };
2006
- var RefMarkdownEditor = ({ reference, placeholder, readonly }) => {
2007
- const text = useAtomValue(useMemo8(() => reference.atom, [
2008
- reference
2009
- ]));
2010
- const dataExtensions = useMemo8(() => text ? [
2011
- createDataExtensions({
2012
- id: reference.uri,
2013
- text: Doc.createAccessor(text, [
2014
- "content"
2015
- ])
2016
- })
2017
- ] : [], [
2018
- text,
2019
- reference
2020
- ]);
2021
- const extensions = useBasicMarkdownExtensions({
2022
- placeholder,
2023
- readonly,
2024
- extensions: dataExtensions
2025
- });
2026
- if (!text) {
2027
- return null;
2028
- }
2029
- return /* @__PURE__ */ React13.createElement(Editor.Root, null, /* @__PURE__ */ React13.createElement(Editor.View, {
2030
- classNames: editorClassNames,
2031
- extensions
2032
- }));
2033
- };
2034
- var StringMarkdownEditor = ({ value: value3, placeholder, readonly, onChange }) => {
2035
- const extensions = useBasicMarkdownExtensions({
2036
- placeholder,
2037
- readonly
2038
- });
2039
- const handleChange = useCallback10((next) => onChange(next), [
2040
- onChange
2041
- ]);
2042
- return /* @__PURE__ */ React13.createElement(Editor.Root, null, /* @__PURE__ */ React13.createElement(Editor.View, {
2043
- classNames: editorClassNames,
2044
- extensions,
2045
- value: value3,
2046
- onChange: readonly ? void 0 : handleChange
2047
- }));
2048
- };
2049
- var CreateTextButton = ({ db, disabled, onCreate }) => {
2050
- const { t } = useTranslation4(translationKey4);
2051
- const handleClick = useCallback10(() => {
2052
- if (!db) {
2053
- return;
2054
- }
2055
- onCreate(Ref4.make(db.add(Text.make())));
2056
- }, [
2057
- db,
2058
- onCreate
2059
- ]);
2060
- return /* @__PURE__ */ React13.createElement(IconButton2, {
2061
- icon: "ph--plus--regular",
2062
- label: t("create-text.label"),
2063
- onClick: handleClick,
2064
- disabled
2065
- });
2066
- };
2067
-
2068
- // src/components/Form/FormField/fields/NumberField/NumberField.tsx
2069
- import * as Option5 from "effect/Option";
2070
- import * as SchemaAST6 from "effect/SchemaAST";
2071
- import React14, { useCallback as useCallback11, useEffect as useEffect7, useState as useState5 } from "react";
2072
- import { Input as Input6 } from "@dxos/react-ui";
2073
- import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
2074
- var getNumericConstraints = (ast) => {
2075
- let node = ast;
2076
- let min;
2077
- let max;
2078
- let integer = false;
2079
- while (node && SchemaAST6.isRefinement(node)) {
2080
- const jsonSchema = Option5.getOrUndefined(SchemaAST6.getJSONSchemaAnnotation(node));
2081
- if (jsonSchema != null) {
2082
- if ("minimum" in jsonSchema && typeof jsonSchema.minimum === "number") {
2083
- min = min === void 0 ? jsonSchema.minimum : Math.max(min, jsonSchema.minimum);
2084
- }
2085
- if ("maximum" in jsonSchema && typeof jsonSchema.maximum === "number") {
2086
- max = max === void 0 ? jsonSchema.maximum : Math.min(max, jsonSchema.maximum);
2087
- }
2088
- if ("type" in jsonSchema && jsonSchema.type === "integer" || "multipleOf" in jsonSchema && jsonSchema.multipleOf === 1) {
2089
- integer = true;
2090
- }
2091
- }
2092
- node = node.from;
2093
- }
2094
- return {
2095
- min,
2096
- max,
2097
- integer
2098
- };
2099
- };
2100
- var NumberField = ({ type, readonly, placeholder, getValue, onValueChange, onBlur, ...props }) => {
2101
- const { min, max, integer } = getNumericConstraints(type);
2102
- const clamp = useCallback11((n) => {
2103
- let value3 = integer ? Math.round(n) : n;
2104
- if (min !== void 0) {
2105
- value3 = Math.max(min, value3);
2106
- }
2107
- if (max !== void 0) {
2108
- value3 = Math.min(max, value3);
2109
- }
2110
- return value3;
2111
- }, [
2112
- min,
2113
- max,
2114
- integer
2115
- ]);
2116
- const [raw, setRaw] = useState5(() => {
2117
- const v = getValue();
2118
- return v !== void 0 ? String(v) : "";
2119
- });
2120
- const externalValue = getValue();
2121
- useEffect7(() => {
2122
- if (externalValue !== safeParseFloat2(raw)) {
2123
- setRaw(externalValue !== void 0 ? String(externalValue) : "");
2124
- }
2125
- }, [
2126
- externalValue
2127
- ]);
2128
- const handleChange = useCallback11((event) => {
2129
- const value3 = event.target.value;
2130
- setRaw(value3);
2131
- const parsed = safeParseFloat2(value3);
2132
- if (parsed !== void 0) {
2133
- onValueChange(type, clamp(parsed));
2134
- }
2135
- }, [
2136
- type,
2137
- onValueChange,
2138
- clamp
2139
- ]);
2140
- const handleBlur = useCallback11((event) => {
2141
- const parsed = safeParseFloat2(raw);
2142
- if (parsed === void 0) {
2143
- const committed = getValue();
2144
- setRaw(committed !== void 0 ? String(committed) : "");
2145
- } else {
2146
- setRaw(String(clamp(parsed)));
2147
- }
2148
- onBlur(event);
2149
- }, [
2150
- raw,
2151
- getValue,
2152
- onBlur,
2153
- clamp
2154
- ]);
2155
- return /* @__PURE__ */ React14.createElement(FormRow, {
2156
- readonly,
2157
- getValue,
2158
- ...props
2159
- }, () => /* @__PURE__ */ React14.createElement(Input6.TextInput, {
2160
- type: "number",
2161
- disabled: !!readonly,
2162
- placeholder,
2163
- value: raw,
2164
- min,
2165
- max,
2166
- step: integer ? 1 : void 0,
2167
- onChange: handleChange,
2168
- onBlur: handleBlur
2169
- }));
2170
- };
2171
-
2172
- // src/components/Form/FormField/fields/PasswordField/PasswordField.tsx
2173
- import React15, { useCallback as useCallback12 } from "react";
2174
- import { Input as Input7 } from "@dxos/react-ui";
2175
- var PasswordField = ({ type, readonly, placeholder, onBlur, onValueChange, ...props }) => {
2176
- const handleChange = useCallback12((event) => onValueChange(type, event.target.value), [
2177
- type,
2178
- onValueChange
2179
- ]);
2180
- return /* @__PURE__ */ React15.createElement(FormRow, {
2181
- readonly,
2182
- ...props
2183
- }, ({ value: value3 = "" }) => /* @__PURE__ */ React15.createElement(Input7.TextInput, {
2184
- type: "password",
2185
- noAutoFill: true,
2186
- disabled: !!readonly,
2187
- placeholder,
2188
- value: value3,
2189
- onBlur,
2190
- onChange: handleChange
2191
- }));
2192
- };
2193
-
2194
- // src/components/Form/FormField/fields/RefField/RefField.tsx
2195
- import React17, { useCallback as useCallback14, useMemo as useMemo9, useState as useState7 } from "react";
2196
- import "@dxos/lit-ui/dx-tag-picker.pcss";
2197
- import { Entity, Filter, Obj, Query, Ref as Ref5, Scope, Type as Type2 } from "@dxos/echo";
2198
- import { useType as defaultUseType2, useQuery } from "@dxos/echo-react";
2199
- import { ANY_OBJECT_TYPENAME, ReferenceAnnotationId as ReferenceAnnotationId2 } from "@dxos/echo/internal";
2200
- import { SchemaEx as SchemaEx7 } from "@dxos/effect";
2201
- import { DXN as DXN2, EID, URI } from "@dxos/keys";
2202
- import { DxAnchor } from "@dxos/lit-ui/react";
2203
- import { Button, Icon as Icon3, Input as Input8, useTranslation as useTranslation6 } from "@dxos/react-ui";
2204
- import { ParentLabelAnnotationId } from "@dxos/schema";
2205
- import { translationKey as translationKey6 } from "#translations";
2206
-
2207
- // src/components/ObjectPicker/ObjectPicker.tsx
2208
- import React16, { forwardRef, useCallback as useCallback13, useState as useState6 } from "react";
2209
- import { Popover, useTranslation as useTranslation5 } from "@dxos/react-ui";
2210
- import { Combobox as Combobox2 } from "@dxos/react-ui-list";
2211
- import { useSearchListResults } from "@dxos/react-ui-search";
2212
- import { translationKey as translationKey5 } from "#translations";
2213
- var ObjectPickerContent = /* @__PURE__ */ forwardRef(({ options, selectedIds, createSchema, createOptionLabel, createOptionIcon, createInitialValuePath, createFieldMap: createFieldMap3, onSelect, onCreate, ...props }, forwardedRef) => {
2214
- const { t } = useTranslation5(translationKey5);
2215
- const [showForm, setShowForm] = useState6(false);
2216
- const [formInitialValue, setFormInitialValue] = useState6("");
2217
- const { results, query, handleSearch } = useSearchListResults({
2218
- items: options
2219
- });
2220
- const handleFormSave = useCallback13(async (values) => {
2221
- await onCreate?.(values);
2222
- setShowForm(false);
2223
- setFormInitialValue("");
2224
- }, [
2225
- onCreate
2226
- ]);
2227
- const handleFormCancel = useCallback13(() => {
2228
- setShowForm(false);
2229
- setFormInitialValue("");
2230
- }, []);
2231
- const handleKeyDown = useCallback13((event) => {
2232
- if (event.key === "Enter" && createSchema && event.currentTarget.querySelector('[role="option"][aria-selected="true"][data-value="__create__"]')) {
2233
- event.stopPropagation();
2234
- event.preventDefault();
2235
- const input = event.currentTarget.querySelector('input[type="text"]');
2236
- const currentQuery = input?.value || "";
2237
- setFormInitialValue(currentQuery);
2238
- setShowForm(true);
2239
- }
2240
- }, [
2241
- createSchema
2242
- ]);
2243
- if (showForm && createSchema) {
2244
- return /* @__PURE__ */ React16.createElement(Combobox2.Content, {
2245
- ...props,
2246
- onKeyDownCapture: handleKeyDown,
2247
- ref: forwardedRef
2248
- }, /* @__PURE__ */ React16.createElement(Popover.Viewport, null, /* @__PURE__ */ React16.createElement(Form.Root, {
2249
- testId: "create-referenced-object-form",
2250
- schema: createSchema,
2251
- defaultValues: createInitialValuePath ? {
2252
- [createInitialValuePath]: formInitialValue
2253
- } : {},
2254
- fieldMap: createFieldMap3,
2255
- onSave: handleFormSave,
2256
- onCancel: handleFormCancel
2257
- }, /* @__PURE__ */ React16.createElement(Form.Viewport, null, /* @__PURE__ */ React16.createElement(Form.Content, null, /* @__PURE__ */ React16.createElement(Form.FieldSet, null), /* @__PURE__ */ React16.createElement(Form.Actions, null))))), /* @__PURE__ */ React16.createElement(Combobox2.Arrow, null));
2258
- }
2259
- return /* @__PURE__ */ React16.createElement(Combobox2.Content, {
2260
- ...props,
2261
- onKeyDownCapture: handleKeyDown,
2262
- ref: forwardedRef
2263
- }, /* @__PURE__ */ React16.createElement(Combobox2.Input, {
2264
- placeholder: t("ref-field-combobox-input.placeholder"),
2265
- autoFocus: true,
2266
- value: query,
2267
- onValueChange: handleSearch
2268
- }), /* @__PURE__ */ React16.createElement(Combobox2.List, null, results.map((option) => /* @__PURE__ */ React16.createElement(Combobox2.Item, {
2269
- key: option.id,
2270
- value: option.id,
2271
- label: option.label,
2272
- description: option.description,
2273
- checked: selectedIds?.includes(option.id),
2274
- onSelect: () => onSelect?.(option.id),
2275
- classNames: "flex items-center gap-2"
2276
- })), createSchema && onCreate && /* @__PURE__ */ React16.createElement(CreateItem, {
2277
- query,
2278
- createOptionLabel,
2279
- createOptionIcon: createOptionIcon ?? "ph--plus--regular",
2280
- onCreateItemSelect: (currentQuery) => {
2281
- setFormInitialValue(currentQuery);
2282
- setShowForm(true);
2283
- }
2284
- })), /* @__PURE__ */ React16.createElement(Combobox2.Arrow, null));
2285
- });
2286
- var CreateItem = ({ query, createOptionLabel, createOptionIcon, onCreateItemSelect }) => {
2287
- const { t } = useTranslation5(translationKey5);
2288
- const label = createOptionLabel ? t(createOptionLabel[0], {
2289
- ns: createOptionLabel[1].ns,
2290
- text: query
2291
- }) : t("create-option.label");
2292
- return /* @__PURE__ */ React16.createElement(Combobox2.Item, {
2293
- value: "__create__",
2294
- label,
2295
- icon: createOptionIcon,
2296
- classNames: "flex items-center gap-2",
2297
- closeOnSelect: false,
2298
- onSelect: () => {
2299
- onCreateItemSelect(query);
2300
- }
2301
- });
2302
- };
2303
- ObjectPickerContent.displayName = "ObjectPicker.Content";
2304
- var ObjectPicker = {
2305
- Root: Combobox2.Root,
2306
- Portal: Combobox2.Portal,
2307
- Trigger: Combobox2.Trigger,
2308
- VirtualTrigger: Combobox2.VirtualTrigger,
2309
- Content: ObjectPickerContent
2310
- };
2311
-
2312
- // src/components/Form/FormField/fields/RefField/RefField.tsx
2313
- var isRefSnapshot = (val) => {
2314
- return typeof val === "object" && typeof val?.["/"] === "string";
2315
- };
2316
- var findRefOption = (value3, options) => {
2317
- const isRef = Ref5.isRef(value3);
2318
- if (!isRef && !isRefSnapshot(value3)) {
2319
- return void 0;
2320
- }
2321
- const valueUri = isRef ? value3.uri : value3["/"];
2322
- const directMatch = options.find((option) => option.id === valueUri);
2323
- if (directMatch) {
2324
- return directMatch;
2325
- }
2326
- const valueEid = EID.tryParse(valueUri);
2327
- if (!valueEid) {
2328
- return void 0;
2329
- }
2330
- const valueSpaceId = EID.getSpaceId(valueEid);
2331
- const valueLocal = EID.toLocal(valueEid);
2332
- return options.find((option) => {
2333
- const optionEid = EID.tryParse(option.id);
2334
- if (!optionEid) {
2335
- return false;
2336
- }
2337
- const optionSpaceId = EID.getSpaceId(optionEid);
2338
- if (valueSpaceId != null && optionSpaceId != null && valueSpaceId !== optionSpaceId) {
2339
- return false;
2340
- }
2341
- return EID.equals(EID.toLocal(optionEid), valueLocal);
2342
- });
2343
- };
2344
- var defaultGetOptions = (results, { parentLabel, getTypePlaceholder } = {}) => results.filter((result) => !parentLabel || Obj.getParent(result) != null).map((result) => {
2345
- const id = Entity.getURI(result, {
2346
- prefer: "named"
2347
- });
2348
- const parent = parentLabel ? Obj.getParent(result) : void 0;
2349
- const labelEntity = parent ?? result;
2350
- const typename = Entity.getTypename(labelEntity);
2351
- const label = Entity.getLabel(labelEntity) ?? (typename ? getTypePlaceholder?.(typename) : void 0) ?? id;
2352
- return {
2353
- id,
2354
- label
2355
- };
2356
- });
2357
- var defaultUseResults = (db, typename) => useQuery(db, !typename ? Query.select(Filter.nothing()) : typename === ANY_OBJECT_TYPENAME ? Query.select(Filter.everything()) : Query.select(Filter.type(DXN2.make(typename))).from(Scope.space(), Scope.registry()));
2358
- var RefField = (props) => {
2359
- const { type, readonly, label, jsonPath, placeholder, presentation, required, getStatus, getValue, createOptionLabel, createOptionIcon, createInitialValuePath, createFieldMap: createFieldMap3, db, useType: useType2 = defaultUseType2, useResults = defaultUseResults, getOptions = defaultGetOptions, onCreate, resolveCreateEntry, onValueChange } = props;
2360
- const { t } = useTranslation6(translationKey6);
2361
- const { status, error } = getStatus();
2362
- const resolved = presentationFor(presentation);
2363
- const typename = useMemo9(() => type ? SchemaEx7.findAnnotation(type, ReferenceAnnotationId2)?.typename : void 0, [
2364
- type
2365
- ]);
2366
- const getTypePlaceholder = useCallback14((placeholderTypename) => {
2367
- const segment = placeholderTypename.split(/[.:/]/).filter(Boolean).pop() ?? placeholderTypename;
2368
- const humanized = segment.charAt(0).toUpperCase() + segment.slice(1);
2369
- return t("object-name.placeholder", {
2370
- ns: placeholderTypename,
2371
- defaultValue: humanized
2372
- });
2373
- }, [
2374
- t
2375
- ]);
2376
- const results = useResults(db, typename);
2377
- const options = useMemo9(() => {
2378
- const parentLabel = type ? SchemaEx7.findAnnotation(type, ParentLabelAnnotationId) === true : false;
2379
- return getOptions(results, {
2380
- parentLabel,
2381
- getTypePlaceholder
2382
- });
2383
- }, [
2384
- results,
2385
- getOptions,
2386
- type,
2387
- getTypePlaceholder
2388
- ]);
2389
- const handleGetValue = useCallback14(() => findRefOption(getValue(), options), [
2390
- options,
2391
- getValue
2392
- ]);
2393
- const item = handleGetValue();
2394
- const selectedIds = useMemo9(() => item ? [
2395
- item.id
2396
- ] : [], [
2397
- item
2398
- ]);
2399
- const createSchema = useType2(db, typename && typename !== ANY_OBJECT_TYPENAME ? DXN2.make(typename) : void 0);
2400
- const createEntry = typename ? resolveCreateEntry?.(typename) : void 0;
2401
- const [open, setOpen] = useState7(false);
2402
- const handleCreate = useCallback14(async (values) => {
2403
- if (createEntry?.createObject && db) {
2404
- const newObject = await createEntry.createObject(values, db);
2405
- if (newObject) {
2406
- onValueChange(type, Ref5.make(newObject));
2407
- }
2408
- } else if (createSchema && onCreate) {
2409
- const newObject = await onCreate(createSchema, values);
2410
- if (newObject) {
2411
- onValueChange(type, Ref5.make(newObject));
2412
- }
2413
- }
2414
- setOpen(false);
2415
- }, [
2416
- createEntry,
2417
- db,
2418
- createSchema,
2419
- onCreate,
2420
- type,
2421
- onValueChange
2422
- ]);
2423
- const handleUpdate = useCallback14((id) => {
2424
- const item2 = options.find((option) => option.id === id);
2425
- const ref = item2 ? Ref5.fromURI(URI.make(item2.id)) : void 0;
2426
- onValueChange(type, ref);
2427
- }, [
2428
- options,
2429
- type,
2430
- onValueChange
2431
- ]);
2432
- const handleSelect = useCallback14((id) => {
2433
- if (item?.id === id) {
2434
- handleUpdate(void 0);
2435
- } else {
2436
- handleUpdate(id);
2437
- }
2438
- }, [
2439
- item,
2440
- handleUpdate
2441
- ]);
2442
- if (!typename || (readonly || resolved.isStatic) && !item) {
2443
- return null;
2444
- }
2445
- return /* @__PURE__ */ React17.createElement(Input8.Root, {
2446
- validationValence: status
2447
- }, resolved.showLabel && /* @__PURE__ */ React17.createElement(FormFieldLabel, {
2448
- error,
2449
- readonly,
2450
- required,
2451
- label,
2452
- path: jsonPath
2453
- }), /* @__PURE__ */ React17.createElement("div", null, readonly ? !item ? /* @__PURE__ */ React17.createElement("p", {
2454
- className: "text-description mb-2"
2455
- }, t("empty-readonly-ref-field.label")) : /* @__PURE__ */ React17.createElement(DxAnchor, {
2456
- key: item.id,
2457
- dxn: item.id,
2458
- rootclassname: "me-1"
2459
- }, item.label) : /* @__PURE__ */ React17.createElement(ObjectPicker.Root, {
2460
- open,
2461
- onOpenChange: setOpen
2462
- }, /* @__PURE__ */ React17.createElement(ObjectPicker.Trigger, {
2463
- asChild: true,
2464
- classNames: "p-0"
2465
- }, item ? /* @__PURE__ */ React17.createElement("div", {
2466
- className: "flex gap-form-gap w-full"
2467
- }, /* @__PURE__ */ React17.createElement(Input8.Root, {
2468
- key: item.id
2469
- }, /* @__PURE__ */ React17.createElement(Input8.TextInput, {
2470
- value: item.label,
2471
- readOnly: true,
2472
- classNames: "w-full"
2473
- }))) : /* @__PURE__ */ React17.createElement(Button, {
2474
- classNames: "w-full text-start gap-form-gap"
2475
- }, /* @__PURE__ */ React17.createElement("div", {
2476
- className: "grow overflow-hidden"
2477
- }, /* @__PURE__ */ React17.createElement("span", {
2478
- className: "truncate text-description"
2479
- }, placeholder || label || t("ref-field.placeholder"))), /* @__PURE__ */ React17.createElement(Icon3, {
2480
- size: 3,
2481
- icon: "ph--caret-down--bold"
2482
- }))), /* @__PURE__ */ React17.createElement(ObjectPicker.Portal, null, /* @__PURE__ */ React17.createElement(ObjectPicker.Content, {
2483
- classNames: "dx-card-popover-width",
2484
- options,
2485
- selectedIds,
2486
- // Prefer the plugin-registered inputSchema; fall back to stripping hidden fields
2487
- // (`FormInputAnnotation.set(false)`) from the raw ECHO type schema so the form's
2488
- // validator doesn't reject required-but-hidden fields such as backing-object refs
2489
- // supplied by a `FactoryAnnotation`.
2490
- createSchema: createEntry?.inputSchema ?? (createSchema && omitHiddenFormFields(omitId(Type2.getSchema(createSchema)))),
2491
- createOptionLabel,
2492
- createOptionIcon,
2493
- createInitialValuePath,
2494
- createFieldMap: createFieldMap3,
2495
- // Offer inline create when the caller wired a handler OR a plugin-registered
2496
- // createObject override is available. A resolvable `createSchema` alone is not
2497
- // enough (e.g. operation refs, whose objects can't be created ad hoc).
2498
- onCreate: onCreate || createEntry?.createObject ? handleCreate : void 0,
2499
- onSelect: handleSelect
2500
- })))), resolved.showError && /* @__PURE__ */ React17.createElement(Input8.DescriptionAndValidation, null, error));
2501
- };
2502
-
2503
- // src/components/Form/FormField/fields/RefField/InlineRefField.tsx
2504
- import { useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
2505
- import React18, { useCallback as useCallback15, useMemo as useMemo10 } from "react";
2506
- import { Obj as Obj2, Ref as Ref6, Type as Type3 } from "@dxos/echo";
2507
- import { useType as defaultUseType3 } from "@dxos/echo-react";
2508
- import { ReferenceAnnotationId as ReferenceAnnotationId3 } from "@dxos/echo/Annotation";
2509
- import { SchemaEx as SchemaEx8 } from "@dxos/effect";
2510
- import { DXN as DXN3 } from "@dxos/keys";
2511
- import { IconButton as IconButton3, Input as Input9, useTranslation as useTranslation7 } from "@dxos/react-ui";
2512
- import { translationKey as translationKey7 } from "#translations";
2513
- var InlineRefField = (props) => {
2514
- const { type, readonly, label, jsonPath, presentation, required, db, getValue, onValueChange, onCreate, useType: useType2 = defaultUseType3 } = props;
2515
- const { t } = useTranslation7(translationKey7);
2516
- const resolved = presentationFor(presentation);
2517
- const reference = getValue();
2518
- const typename = useMemo10(() => type ? SchemaEx8.findAnnotation(type, ReferenceAnnotationId3)?.typename : void 0, [
2519
- type
2520
- ]);
2521
- const createType = useType2(db, typename ? DXN3.make(typename) : void 0);
2522
- const handleCreate = useCallback15(async () => {
2523
- if (!createType || !onCreate) {
2524
- return;
2525
- }
2526
- const newObject = await onCreate(createType, {});
2527
- if (newObject) {
2528
- onValueChange(type, Ref6.make(newObject));
2529
- }
2530
- }, [
2531
- createType,
2532
- onCreate,
2533
- type,
2534
- onValueChange
2535
- ]);
2536
- if (readonly && !reference) {
2537
- return null;
2538
- }
2539
- return /* @__PURE__ */ React18.createElement(Input9.Root, null, resolved.showLabel && /* @__PURE__ */ React18.createElement(FormFieldLabel, {
2540
- readonly,
2541
- required,
2542
- label,
2543
- path: jsonPath
2544
- }), reference ? /* @__PURE__ */ React18.createElement(InlineForm, {
2545
- reference,
2546
- db,
2547
- readonly,
2548
- useType: useType2
2549
- }) : !readonly && onCreate && /* @__PURE__ */ React18.createElement(IconButton3, {
2550
- classNames: "w-full gap-form-gap",
2551
- disabled: !createType || !db,
2552
- icon: "ph--plus--regular",
2553
- label: label || t("ref-field.placeholder"),
2554
- onClick: () => void handleCreate()
2555
- }));
2556
- };
2557
- var InlineForm = ({ reference, db, readonly, useType: useType2 = defaultUseType3 }) => {
2558
- const target = useAtomValue2(useMemo10(() => reference.atom, [
2559
- reference
2560
- ]));
2561
- const typeUri = target ? Obj2.getTypeURI(target) : void 0;
2562
- const typeFromRegistry = useType2(db, typeUri);
2563
- const targetType = target && Obj2.getType(target) || typeFromRegistry;
2564
- const formSchema = useMemo10(() => targetType ? omitId(Type3.getSchema(targetType)) : void 0, [
2565
- targetType
2566
- ]);
2567
- const defaultValues = useMemo10(() => target ? {
2568
- ...target
2569
- } : {}, [
2570
- target
2571
- ]);
2572
- const handleChange = useCallback15((values, { isValid, changed }) => {
2573
- if (!isValid || !target) {
2574
- return;
2575
- }
2576
- const changedPaths = Object.keys(changed).filter((path) => changed[path]);
2577
- if (changedPaths.length === 0) {
2578
- return;
2579
- }
2580
- Obj2.update(target, () => {
2581
- for (const path of changedPaths) {
2582
- const parts = SchemaEx8.splitJsonPath(path);
2583
- Obj2.setValue(target, parts, Obj2.getValue(values, parts));
2584
- }
2585
- });
2586
- }, [
2587
- target
2588
- ]);
2589
- if (!target || !formSchema) {
2590
- return null;
2591
- }
2592
- return /* @__PURE__ */ React18.createElement(FormRoot, {
2593
- db,
2594
- schema: formSchema,
2595
- defaultValues,
2596
- onValuesChanged: handleChange
2597
- }, /* @__PURE__ */ React18.createElement(FormContent, null, /* @__PURE__ */ React18.createElement(FormFieldSetController, {
2598
- collapsible: true,
2599
- readonly
2600
- })));
2601
- };
2602
-
2603
- // src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.tsx
2604
- import React19, { useCallback as useCallback16, useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
2605
- import { PublicKey } from "@dxos/keys";
2606
- import { IconButton as IconButton4, Input as Input10, Tag as Tag2, ToggleIconButton, useTranslation as useTranslation8 } from "@dxos/react-ui";
2607
- import { OrderedList as OrderedList2 } from "@dxos/react-ui-list";
2608
- import { HuePicker } from "@dxos/react-ui-pickers";
2609
- import { hues, osTranslations } from "@dxos/ui-theme";
2610
- import { translationKey as translationKey8 } from "#translations";
2611
- var SelectOptionField = ({ type, readonly, label, jsonPath, getStatus, getValue, onValueChange }) => {
2612
- const { t } = useTranslation8(translationKey8);
2613
- const { status, error } = getStatus();
2614
- const [selected, setSelectedId] = useState8(null);
2615
- const [isNewOption, setIsNewOption] = useState8(false);
2616
- const inputRef = useRef6(null);
2617
- const options = getValue();
2618
- useEffect8(() => {
2619
- if (options === void 0) {
2620
- onValueChange(type, []);
2621
- }
2622
- }, [
2623
- options,
2624
- type,
2625
- onValueChange
2626
- ]);
2627
- const randomHue = () => {
2628
- const usedHues = new Set(options?.map((option) => option.color) ?? []);
2629
- const unusedHues = hues.filter((hue) => !usedHues.has(hue));
2630
- if (unusedHues.length > 0) {
2631
- return unusedHues[Math.floor(Math.random() * unusedHues.length)];
2632
- }
2633
- const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
2634
- const availableHues = hues.filter((hue) => !lastThree.has(hue));
2635
- return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
2636
- };
2637
- const handleAdd = useCallback16(() => {
2638
- const newId = PublicKey.random().truncate();
2639
- const newOption = {
2640
- id: newId,
2641
- title: "",
2642
- color: randomHue()
2643
- };
2644
- onValueChange(type, [
2645
- ...options ?? [],
2646
- newOption
2647
- ]);
2648
- setSelectedId(newId);
2649
- setIsNewOption(true);
2650
- }, [
2651
- options,
2652
- type,
2653
- onValueChange
2654
- ]);
2655
- const handleDelete = useCallback16((id) => {
2656
- const newOptions = options?.filter((option) => option.id !== id) ?? [];
2657
- onValueChange(type, newOptions);
2658
- setSelectedId(null);
2659
- }, [
2660
- options,
2661
- type,
2662
- onValueChange
2663
- ]);
2664
- const handleMove = useCallback16((from, to) => {
2665
- if (!options) {
2666
- return;
2667
- }
2668
- const newOptions = [
2669
- ...options
2670
- ];
2671
- const [removed] = newOptions.splice(from, 1);
2672
- newOptions.splice(to, 0, removed);
2673
- onValueChange(type, newOptions);
2674
- }, [
2675
- options,
2676
- type,
2677
- onValueChange
2678
- ]);
2679
- const handleClick = useCallback16((id) => {
2680
- setSelectedId((current) => current === id ? null : id);
2681
- }, []);
2682
- const handleTitleChange = useCallback16((id) => (e) => {
2683
- const newOptions = options?.map((o) => o.id === id ? {
2684
- ...o,
2685
- title: e.target.value
2686
- } : o);
2687
- onValueChange(type, newOptions ?? []);
2688
- }, [
2689
- options,
2690
- type,
2691
- onValueChange
2692
- ]);
2693
- const handleKeyDown = useCallback16((ev) => {
2694
- if (ev.key === "Enter") {
2695
- setSelectedId(null);
2696
- }
2697
- }, []);
2698
- const handleColorChange = useCallback16((id) => (hue) => {
2699
- const newOptions = options?.map((o) => o.id === id ? {
2700
- ...o,
2701
- color: hue
2702
- } : o);
2703
- onValueChange(type, newOptions ?? []);
2704
- }, [
2705
- options,
2706
- type,
2707
- onValueChange
2708
- ]);
2709
- useEffect8(() => {
2710
- if (selected && isNewOption && inputRef.current) {
2711
- inputRef.current.focus();
2712
- setIsNewOption(false);
2713
- }
2714
- }, [
2715
- selected,
2716
- isNewOption
2717
- ]);
2718
- return /* @__PURE__ */ React19.createElement(Input10.Root, {
2719
- validationValence: status
2720
- }, /* @__PURE__ */ React19.createElement(FormFieldLabel, {
2721
- error,
2722
- readonly,
2723
- label,
2724
- path: jsonPath
2725
- }), /* @__PURE__ */ React19.createElement("div", null, options && /* @__PURE__ */ React19.createElement(OrderedList2.Root, {
2726
- items: options,
2727
- isItem: (_item) => true,
2728
- getId: (option) => option.id,
2729
- onMove: readonly ? void 0 : handleMove,
2730
- readonly: !!readonly
2731
- }, ({ items }) => /* @__PURE__ */ React19.createElement(OrderedList2.Content, {
2732
- classNames: "w-full overflow-auto"
2733
- }, items?.map((item) => /* @__PURE__ */ React19.createElement(OrderedList2.Item, {
2734
- key: item.id,
2735
- id: item.id,
2736
- item,
2737
- canDrag: !readonly,
2738
- classNames: "flex flex-col cursor-pointer rounded-xs dx-hover"
2739
- }, /* @__PURE__ */ React19.createElement("div", {
2740
- className: "flex items-center"
2741
- }, /* @__PURE__ */ React19.createElement(OrderedList2.DragHandle, null), /* @__PURE__ */ React19.createElement("div", {
2742
- className: "flex grow items-center truncate px-2",
2743
- onClick: () => handleClick(item.id)
2744
- }, /* @__PURE__ */ React19.createElement(Tag2, {
2745
- hue: item.color
2746
- }, item.title || "\u200B")), /* @__PURE__ */ React19.createElement(ToggleIconButton, {
2747
- iconOnly: true,
2748
- variant: "ghost",
2749
- active: selected === item.id,
2750
- icon: "ph--caret-right--regular",
2751
- label: t(selected === item.id ? "collapse.label" : "expand.label", {
2752
- ns: osTranslations
2753
- }),
2754
- onClick: () => handleClick(item.id)
2755
- })), selected === item.id && /* @__PURE__ */ React19.createElement("div", {
2756
- className: "flex flex-col p-form-padding gap-form-gap dx-density-md"
2757
- }, /* @__PURE__ */ React19.createElement(Input10.Label, {
2758
- classNames: "text-sm"
2759
- }, t("select-option.label")), /* @__PURE__ */ React19.createElement("div", {
2760
- className: "grid grid-cols-[1fr_min-content_min-content] gap-form-gap"
2761
- }, /* @__PURE__ */ React19.createElement(Input10.TextInput, {
2762
- disabled: !!readonly,
2763
- placeholder: t("select-option-label.placeholder"),
2764
- ref: selected === item.id ? inputRef : void 0,
2765
- value: item.title,
2766
- onChange: handleTitleChange(item.id),
2767
- onKeyDown: handleKeyDown,
2768
- classNames: "flex-1"
2769
- }), /* @__PURE__ */ React19.createElement(HuePicker, {
2770
- disabled: !!readonly,
2771
- value: item.color,
2772
- onChange: handleColorChange(item.id)
2773
- }), /* @__PURE__ */ React19.createElement(IconButton4, {
2774
- disabled: !!readonly,
2775
- icon: "ph--trash--fill",
2776
- iconOnly: true,
2777
- label: t("select-option-delete.button"),
2778
- onClick: () => handleDelete(item.id)
2779
- }))))), /* @__PURE__ */ React19.createElement(IconButton4, {
2780
- classNames: "w-full",
2781
- variant: "ghost",
2782
- icon: "ph--plus--regular",
2783
- label: t("select-option-add.button"),
2784
- onClick: handleAdd,
2785
- disabled: !!readonly
2786
- })))));
2787
- };
2788
-
2789
- // src/components/Form/FormField/fields/TextAreaField/TextAreaField.tsx
2790
- import React20, { useCallback as useCallback17 } from "react";
2791
- import { Input as Input11 } from "@dxos/react-ui";
2792
- var TextAreaField = ({ type, readonly, placeholder, onValueChange, onBlur, ...props }) => {
2793
- const handleChange = useCallback17((event) => onValueChange(type, event.target.value), [
2794
- type,
2795
- onValueChange
2796
- ]);
2797
- return /* @__PURE__ */ React20.createElement(FormRow, {
2798
- readonly,
2799
- ...props
2800
- }, ({ value: value3 = "" }) => /* @__PURE__ */ React20.createElement(Input11.TextArea, {
2801
- rows: 5,
2802
- disabled: !!readonly,
2803
- placeholder,
2804
- value: value3,
2805
- onChange: handleChange,
2806
- onBlur
2807
- }));
2808
- };
2809
-
2810
- // src/components/Form/FormField/fields/TupleField/TupleField.tsx
2811
- import React21 from "react";
2812
- import { Input as Input12 } from "@dxos/react-ui";
2813
- import { mx as mx4 } from "@dxos/ui-theme";
2814
- import { safeParseFloat as safeParseFloat3 } from "@dxos/util";
2815
- var gridCols = [
2816
- "grid-cols-1",
2817
- "grid-cols-2",
2818
- "grid-cols-3",
2819
- "grid-cols-4"
2820
- ];
2821
- var TupleField = ({ binding, type, readonly, label, jsonPath, getStatus, getValue, onValueChange, onBlur }) => {
2822
- const { status, error } = getStatus();
2823
- const values = getValue() ?? {};
2824
- return /* @__PURE__ */ React21.createElement(Input12.Root, {
2825
- validationValence: status
2826
- }, /* @__PURE__ */ React21.createElement(FormFieldLabel, {
2827
- error,
2828
- readonly,
2829
- label,
2830
- path: jsonPath
2831
- }), /* @__PURE__ */ React21.createElement("div", {
2832
- className: mx4("grid gap-form-gap", gridCols[binding.length - 1])
2833
- }, binding.map((prop) => /* @__PURE__ */ React21.createElement(Input12.TextInput, {
2834
- key: prop,
2835
- type: "number",
2836
- disabled: !!readonly,
2837
- value: values[prop],
2838
- onChange: (event) => {
2839
- onValueChange(type, {
2840
- ...values,
2841
- [prop]: safeParseFloat3(event.target.value, 0)
2842
- });
2843
- },
2844
- onBlur
2845
- }))));
2846
- };
2847
-
2848
- // src/components/Form/FormLayout/FormLayout.tsx
2849
- import * as Option6 from "effect/Option";
2850
- import * as Schema4 from "effect/Schema";
2851
- import * as SchemaAST7 from "effect/SchemaAST";
2852
- import * as String3 from "effect/String";
2853
- import React22, { Fragment, useMemo as useMemo11 } from "react";
2854
- import { Annotation as Annotation5 } from "@dxos/echo";
2855
- import { SchemaEx as SchemaEx9 } from "@dxos/effect";
2856
- import { Input as Input13 } from "@dxos/react-ui";
2857
-
2858
- // src/components/Form/FormLayout/parser.ts
2859
- var LayoutParseError = class extends Error {
2860
- position;
2861
- _tag = "LayoutParseError";
2862
- constructor(message, position) {
2863
- super(position !== void 0 ? `${message} (at position ${position})` : message), this.position = position;
2864
- }
2865
- };
2866
- var TAG_RE = /<\s*(\/?)\s*(\w+)([^>]*?)(\/?)\s*>/g;
2867
- var ATTR_RE = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\d+))/g;
2868
- var parseAttrs = (raw) => {
2869
- const attrs = {};
2870
- ATTR_RE.lastIndex = 0;
2871
- let match2;
2872
- while ((match2 = ATTR_RE.exec(raw)) !== null) {
2873
- const [, key, doubleQuoted, singleQuoted, bare] = match2;
2874
- attrs[key] = doubleQuoted ?? singleQuoted ?? bare;
2875
- }
2876
- return attrs;
2877
- };
2878
- var tokenize = (input) => {
2879
- const tokens = [];
2880
- TAG_RE.lastIndex = 0;
2881
- let match2;
2882
- let cursor = 0;
2883
- while ((match2 = TAG_RE.exec(input)) !== null) {
2884
- const between = input.slice(cursor, match2.index);
2885
- if (between.trim().length > 0) {
2886
- throw new LayoutParseError(`unexpected text content: ${JSON.stringify(between.trim())}`, cursor);
2887
- }
2888
- const [, slash, name, attrsRaw, selfSlash] = match2;
2889
- if (slash) {
2890
- tokens.push({
2891
- type: "close",
2892
- name,
2893
- position: match2.index
2894
- });
2895
- } else {
2896
- tokens.push({
2897
- type: "open",
2898
- name,
2899
- attrs: parseAttrs(attrsRaw),
2900
- selfClosing: selfSlash === "/",
2901
- position: match2.index
2902
- });
2903
- }
2904
- cursor = match2.index + match2[0].length;
2905
- }
2906
- const trailing = input.slice(cursor);
2907
- if (trailing.trim().length > 0) {
2908
- throw new LayoutParseError(`unexpected text content: ${JSON.stringify(trailing.trim())}`, cursor);
2909
- }
2910
- return tokens;
2911
- };
2912
- var requireAttr = (attrs, key, tag, position) => {
2913
- const value3 = attrs[key];
2914
- if (value3 === void 0) {
2915
- throw new LayoutParseError(`<${tag}> missing required attribute "${key}"`, position);
2916
- }
2917
- return value3;
2918
- };
2919
- var parseInteger = (value3, attr, tag, position) => {
2920
- if (!/^\d+$/.test(value3)) {
2921
- throw new LayoutParseError(`<${tag}> attribute "${attr}" expects an integer, got ${JSON.stringify(value3)}`, position);
2922
- }
2923
- return Number.parseInt(value3, 10);
2924
- };
2925
- var parseLayout = (template) => {
2926
- const tokens = tokenize(template);
2927
- if (tokens.length === 0) {
2928
- throw new LayoutParseError("empty template");
2929
- }
2930
- let index = 0;
2931
- const parseNode = () => {
2932
- const token = tokens[index];
2933
- if (!token || token.type !== "open") {
2934
- throw new LayoutParseError(`expected an opening tag, got ${JSON.stringify(token)}`, token?.position);
2935
- }
2936
- if (token.name === "field") {
2937
- if (!token.selfClosing) {
2938
- throw new LayoutParseError('<field> must be self-closing (e.g. <field name="x"/>)', token.position);
2939
- }
2940
- const name = requireAttr(token.attrs, "name", "field", token.position);
2941
- const spanRaw = token.attrs.span;
2942
- const span = spanRaw !== void 0 ? parseInteger(spanRaw, "span", "field", token.position) : void 0;
2943
- index += 1;
2944
- return {
2945
- kind: "field",
2946
- name,
2947
- span
2948
- };
2949
- }
2950
- if (token.name === "grid") {
2951
- if (token.selfClosing) {
2952
- throw new LayoutParseError("<grid> must wrap children (cannot self-close)", token.position);
2953
- }
2954
- const cols = parseInteger(requireAttr(token.attrs, "cols", "grid", token.position), "cols", "grid", token.position);
2955
- index += 1;
2956
- const children = [];
2957
- while (index < tokens.length && !(tokens[index].type === "close" && tokens[index].name === "grid")) {
2958
- children.push(parseNode());
2959
- }
2960
- const closing = tokens[index];
2961
- if (!closing || closing.type !== "close" || closing.name !== "grid") {
2962
- throw new LayoutParseError("<grid> not closed", token.position);
2963
- }
2964
- index += 1;
2965
- return {
2966
- kind: "grid",
2967
- cols,
2968
- children
2969
- };
2970
- }
2971
- throw new LayoutParseError(`unknown element <${token.name}>`, token.position);
2972
- };
2973
- const root = parseNode();
2974
- if (index !== tokens.length) {
2975
- const trailing = tokens[index];
2976
- throw new LayoutParseError(`unexpected trailing tag <${trailing.type === "open" ? "" : "/"}${trailing.name}>`, trailing.position);
2977
- }
2978
- if (root.kind !== "grid") {
2979
- throw new LayoutParseError("root element must be <grid>");
2980
- }
2981
- return root;
2982
- };
2983
-
2984
- // src/components/Form/FormLayout/FormLayout.tsx
2985
- var FORM_LAYOUT_NAME = "Form.Layout";
2986
- var FormLayout = ({ schema, template, name = Annotation5.DEFAULT_LAYOUT_NAME, path, readonly, layout, projection, ...props }) => {
2987
- const annotated = Option6.getOrUndefined(Annotation5.FormLayoutAnnotation.get(schema));
2988
- const source = template ?? annotated?.[name];
2989
- if (source === void 0) {
2990
- throw new LayoutParseError(annotated ? `no layout named "${name}" on schema (available: ${Object.keys(annotated).join(", ") || "\u2205"})` : `no template provided and schema has no FormLayoutAnnotation`);
2991
- }
2992
- const tree = useMemo11(() => parseLayout(source), [
2993
- source
2994
- ]);
2995
- return /* @__PURE__ */ React22.createElement(RenderNode, {
2996
- node: tree,
2997
- schema,
2998
- basePath: path ?? [],
2999
- readonly,
3000
- layout,
3001
- projection,
3002
- ...props
3003
- });
3004
- };
3005
- FormLayout.displayName = FORM_LAYOUT_NAME;
3006
- var RenderNode = ({ node, schema, basePath, ...props }) => {
3007
- if (node.kind === "grid") {
3008
- return /* @__PURE__ */ React22.createElement("div", {
3009
- className: "grid gap-x-form-gap",
3010
- style: {
3011
- gridTemplateColumns: `repeat(${node.cols}, minmax(0, 1fr))`
3012
- }
3013
- }, node.children.map((child, index) => /* @__PURE__ */ React22.createElement(Fragment, {
3014
- key: index
3015
- }, /* @__PURE__ */ React22.createElement(RenderNode, {
3016
- node: child,
3017
- schema,
3018
- basePath,
3019
- ...props
3020
- }))));
3021
- }
3022
- const resolved = resolveLayoutField(schema, node.name);
3023
- if (!resolved) {
3024
- throw new LayoutParseError(`field "${node.name}" not found on schema`);
3025
- }
3026
- const { type, segments, leafName, title, labelType, required } = resolved;
3027
- const path = [
3028
- ...basePath,
3029
- ...segments
3030
- ];
3031
- const span = node.span ? {
3032
- gridColumn: `span ${node.span} / span ${node.span}`
3033
- } : void 0;
3034
- return /* @__PURE__ */ React22.createElement("div", {
3035
- className: "min-w-0",
3036
- style: span
3037
- }, /* @__PURE__ */ React22.createElement(FormFieldErrorBoundary, {
3038
- path
3039
- }, labelType ? /* @__PURE__ */ React22.createElement(LabelField, {
3040
- schema: Schema4.make(labelType),
3041
- label: title ?? String3.capitalize(leafName),
3042
- path,
3043
- layout: props.layout
3044
- }) : /* @__PURE__ */ React22.createElement(FormField, {
3045
- type,
3046
- name: leafName,
3047
- path,
3048
- required,
3049
- ...props
3050
- })));
3051
- };
3052
- var resolvePropertySignature = (ast, segments) => {
3053
- let node = ast;
3054
- for (let index = 0; index < segments.length; index++) {
3055
- const typeLiteral = SchemaEx9.findNode(node, SchemaAST7.isTypeLiteral);
3056
- if (!typeLiteral) {
3057
- return void 0;
3058
- }
3059
- const prop = SchemaAST7.getPropertySignatures(typeLiteral).find((p) => globalThis.String(p.name) === segments[index]);
3060
- if (!prop) {
3061
- return void 0;
3062
- }
3063
- if (index === segments.length - 1) {
3064
- return prop;
3065
- }
3066
- node = prop.type;
3067
- }
3068
- return void 0;
3069
- };
3070
- var resolveLayoutField = (schema, name) => {
3071
- const segments = name.split(".");
3072
- const prop = resolvePropertySignature(schema.ast, segments);
3073
- if (!prop) {
3074
- return void 0;
3075
- }
3076
- const { type: baseType } = SchemaEx9.getBaseType(prop);
3077
- const type = prop.annotations && Reflect.ownKeys(prop.annotations).length > 0 ? {
3078
- ...baseType,
3079
- annotations: {
3080
- ...baseType.annotations,
3081
- ...prop.annotations
3082
- }
3083
- } : baseType;
3084
- const title = SchemaEx9.getAnnotation(SchemaAST7.TitleAnnotationId)(type);
3085
- const labelType = SchemaEx9.findNode(prop.type, SchemaAST7.isTypeLiteral);
3086
- const labelled = labelType != null && Option6.isSome(Annotation5.LabelAnnotation.getFromAst(labelType));
3087
- return {
3088
- type,
3089
- segments,
3090
- leafName: segments[segments.length - 1],
3091
- title,
3092
- labelType: labelled ? labelType : void 0,
3093
- required: !prop.isOptional
3094
- };
3095
- };
3096
- var LabelField = ({ schema, label, path, layout }) => {
3097
- const { variant = "default" } = useFormContext(FORM_LAYOUT_NAME);
3098
- const styles3 = formTheme.styles({
3099
- variant
3100
- });
3101
- const { getValue } = useFormFieldState(FORM_LAYOUT_NAME, path);
3102
- const value3 = getValue();
3103
- const text = value3 == null ? void 0 : Annotation5.getLabelWithSchema(schema, value3);
3104
- if (text == null || text.trim().length === 0) {
3105
- return null;
3106
- }
3107
- const presentation = presentationFor(layout);
3108
- return /* @__PURE__ */ React22.createElement(Input13.Root, null, /* @__PURE__ */ React22.createElement("div", {
3109
- className: styles3.field()
3110
- }, presentation.showLabel && /* @__PURE__ */ React22.createElement(FormFieldLabel, {
3111
- variant,
3112
- readonly: true,
3113
- label,
3114
- path: SchemaEx9.createJsonPath(path)
3115
- }), /* @__PURE__ */ React22.createElement("div", {
3116
- className: styles3.fieldControl()
3117
- }, /* @__PURE__ */ React22.createElement("p", {
3118
- className: "truncate min-w-0",
3119
- title: text
3120
- }, text))));
3121
- };
3122
-
3123
- // src/components/Form/FormFieldSet/FormFieldSetContainer.tsx
3124
- import React23, { useState as useState9 } from "react";
3125
- import { ToggleIconButton as ToggleIconButton2, useTranslation as useTranslation9 } from "@dxos/react-ui";
3126
- import { mx as mx5 } from "@dxos/ui-theme";
3127
- import { translationKey as translationKey9 } from "#translations";
3128
- var styles = formTheme.styles();
3129
- var FormFieldSetContainer = ({ classNames, label, path, readonly, presentation, collapsible, children }) => {
3130
- const { t } = useTranslation9(translationKey9);
3131
- const [collapsed, setCollapsed] = useState9(false);
3132
- const showBody = !(collapsible && collapsed);
3133
- const content = /* @__PURE__ */ React23.createElement(React23.Fragment, null, presentation.showLabel && label && /* @__PURE__ */ React23.createElement(FormFieldHeader, {
3134
- label,
3135
- path,
3136
- readonly,
3137
- classNames: "pl-2",
3138
- actions: collapsible ? /* @__PURE__ */ React23.createElement(ToggleIconButton2, {
3139
- active: !collapsed,
3140
- classNames: "px-1 mr-0.5",
3141
- variant: "ghost",
3142
- density: "xs",
3143
- iconOnly: true,
3144
- icon: "ph--caret-right--regular",
3145
- label: t(collapsed ? "expand-fields.label" : "collapse-fields.label")
3146
- }) : void 0,
3147
- onClick: collapsible ? () => setCollapsed((value3) => !value3) : void 0
3148
- }), showBody && (collapsible ? /* @__PURE__ */ React23.createElement("div", {
3149
- className: styles.fieldSetBody()
3150
- }, children) : children));
3151
- if (collapsible) {
3152
- return /* @__PURE__ */ React23.createElement("div", {
3153
- className: styles.fieldSetBoxOuter()
3154
- }, /* @__PURE__ */ React23.createElement("div", {
3155
- className: styles.fieldSetBox({
3156
- class: mx5(classNames)
3157
- })
3158
- }, content));
3159
- }
3160
- return classNames ? /* @__PURE__ */ React23.createElement("div", {
3161
- className: mx5(classNames)
3162
- }, content) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, content);
3163
- };
3164
-
3165
- // src/components/Form/FormFieldSet/FormFieldSet.tsx
3166
- var FORM_FIELDSET_NAME = "Form.FieldSet";
3167
- var FormFieldSet = ({ classNames, label, schema, readonly, path, sort, collapsible, filter, projection, layout, layoutName = DEFAULT_LAYOUT_NAME, ...props }) => {
3168
- const values = useFormValues(FORM_FIELDSET_NAME, path);
3169
- const properties = useFormFieldSetProperties({
3170
- schema,
3171
- values,
3172
- filter,
3173
- sort,
3174
- projection
3175
- });
3176
- if ((readonly || layout === "static") && values == null) {
3177
- return null;
3178
- }
3179
- const layouts = schema ? Option7.getOrUndefined(FormLayoutAnnotation.get(schema)) : void 0;
3180
- const body = layouts?.[layoutName] !== void 0 && schema ? /* @__PURE__ */ React24.createElement(FormLayout, {
3181
- schema,
3182
- name: layoutName,
3183
- path,
3184
- readonly,
3185
- layout,
3186
- projection,
3187
- ...props
3188
- }) : properties.map((property) => {
3189
- const name = property.name.toString();
3190
- return /* @__PURE__ */ React24.createElement(FormFieldErrorBoundary, {
3191
- key: name,
3192
- path: [
3193
- ...path ?? [],
3194
- name
3195
- ]
3196
- }, /* @__PURE__ */ React24.createElement(FormField, {
3197
- type: property.type,
3198
- name,
3199
- path: [
3200
- ...path ?? [],
3201
- name
3202
- ],
3203
- required: !property.isOptional,
3204
- readonly,
3205
- layout,
3206
- projection,
3207
- ...props
3208
- }));
3209
- });
3210
- return /* @__PURE__ */ React24.createElement(FormFieldSetContainer, {
3211
- classNames,
3212
- label,
3213
- path: SchemaEx10.createJsonPath(path ?? []),
3214
- readonly,
3215
- presentation: presentationFor(layout),
3216
- collapsible
3217
- }, body);
3218
- };
3219
- FormFieldSet.displayName = FORM_FIELDSET_NAME;
3220
- var useFormFieldSetProperties = ({ schema, filter, projection, values, sort }) => {
3221
- return useMemo12(() => {
3222
- if (!schema) {
3223
- return [];
3224
- }
3225
- const schemaProps = getRootFormProperties(schema.ast, values);
3226
- const filteredProps = filter ? filter(schemaProps) : schemaProps;
3227
- if (projection) {
3228
- const fieldProjections = projection.getFieldProjections();
3229
- const hiddenProperties = new Set(projection.getHiddenProperties());
3230
- const visibleProps = filteredProps.filter((prop) => !hiddenProperties.has(prop.name.toString()));
3231
- const orderedProps = [];
3232
- for (const fieldProjection of fieldProjections) {
3233
- const fieldPath = String(fieldProjection.field.path);
3234
- const prop = visibleProps.find((prop2) => prop2.name === fieldPath);
3235
- if (prop) {
3236
- orderedProps.push(prop);
3237
- }
3238
- }
3239
- const projectionPaths = new Set(fieldProjections.map((projection2) => String(projection2.field.path)));
3240
- const remainingProps = visibleProps.filter((prop) => !projectionPaths.has(prop.name.toString()));
3241
- orderedProps.push(...remainingProps);
3242
- return orderedProps;
3243
- }
3244
- return sort ? [
3245
- ...filteredProps
3246
- ].sort(({ name: a }, { name: b }) => sort.indexOf(a.toString()) - sort.indexOf(b.toString())) : filteredProps;
3247
- }, [
3248
- schema,
3249
- values,
3250
- filter,
3251
- sort,
3252
- projection
3253
- ]);
3254
- };
3255
-
3256
- // src/components/Form/FormControls.tsx
3257
- var FormRoot = ({ children, schema, values, onSave, onCancel, ...props }) => {
3258
- const form = useFormHandler({
3259
- schema,
3260
- values,
3261
- onSave,
3262
- onCancel,
3263
- ...props
3264
- });
3265
- return /* @__PURE__ */ React25.createElement(FormContextProvider, {
3266
- form,
3267
- ...props
3268
- }, children);
3269
- };
3270
- FormRoot.displayName = "Form.Root";
3271
- var FORM_VIEWPORT_NAME = "Form.Viewport";
3272
- var FormViewport = composable(({ children, scroll, gutter = "sm", ...props }, forwardedRef) => {
3273
- const { variant = "default" } = useFormContext(FORM_VIEWPORT_NAME);
3274
- const styles3 = formTheme.styles({
3275
- variant
3276
- });
3277
- const span = "[.dx-column-root_&]:col-span-full";
3278
- if (scroll) {
3279
- return /* @__PURE__ */ React25.createElement(Column.Root, {
3280
- gutter,
3281
- classNames: [
3282
- "dx-expander",
3283
- span
3284
- ]
3285
- }, /* @__PURE__ */ React25.createElement(ScrollArea.Root, {
3286
- ...composableProps(props, {
3287
- classNames: styles3.viewport()
3288
- }),
3289
- orientation: "vertical",
3290
- centered: true,
3291
- padding: true,
3292
- thin: true,
3293
- ref: forwardedRef
3294
- }, /* @__PURE__ */ React25.createElement(ScrollArea.Viewport, null, children)));
3295
- }
3296
- return /* @__PURE__ */ React25.createElement(Column.Root, {
3297
- ...composableProps(props, {
3298
- classNames: [
3299
- "w-full min-w-0",
3300
- span,
3301
- styles3.viewport()
3302
- ]
3303
- }),
3304
- gutter,
3305
- ref: forwardedRef
3306
- }, children);
3307
- });
3308
- FormViewport.displayName = FORM_VIEWPORT_NAME;
3309
- var FORM_CONTENT_NAME = "Form.Content";
3310
- var FormContent = composable(({ children, ...props }, forwardedRef) => {
3311
- const { form, testId, variant = "default" } = useFormContext(FORM_CONTENT_NAME);
3312
- const styles3 = formTheme.styles({
3313
- variant
3314
- });
3315
- const localRef = useRef7(null);
3316
- const mergedRef = useMergeRefs([
3317
- forwardedRef,
3318
- localRef
3319
- ]);
3320
- useKeyHandler(localRef, form);
3321
- return /* @__PURE__ */ React25.createElement("div", {
3322
- ...composableProps(props, {
3323
- role: "form",
3324
- classNames: mx6(withColumn.center(), "flex flex-col w-full dx-document", styles3.content())
3325
- }),
3326
- "data-testid": testId,
3327
- ref: mergedRef
3328
- }, children);
3329
- });
3330
- FormContent.displayName = FORM_CONTENT_NAME;
3331
- var FORM_FIELDSET_NAME2 = "Form.FieldSet";
3332
- var FormFieldSetController = ({ classNames, ...props }) => {
3333
- const { form, variant = "default", ...contextProps } = useFormContext(FORM_FIELDSET_NAME2);
3334
- const styles3 = formTheme.styles({
3335
- variant
3336
- });
3337
- return /* @__PURE__ */ React25.createElement(FormFieldSet, {
3338
- schema: form.schema,
3339
- classNames: styles3.fieldSet({
3340
- class: classNames
3341
- }),
3342
- ...contextProps,
3343
- ...props
3344
- });
3345
- };
3346
- FormFieldSetController.displayName = FORM_FIELDSET_NAME2;
3347
- var FORM_LAYOUT_NAME2 = "Form.Layout";
3348
- var FormLayoutController = ({ schema, ...props }) => {
3349
- const { form, ...contextProps } = useFormContext(FORM_LAYOUT_NAME2);
3350
- const resolvedSchema = schema ?? form.schema;
3351
- if (!resolvedSchema) {
3352
- return null;
3353
- }
3354
- return /* @__PURE__ */ React25.createElement(FormLayout, {
3355
- schema: resolvedSchema,
3356
- ...contextProps,
3357
- ...props
3358
- });
3359
- };
3360
- FormLayoutController.displayName = FORM_LAYOUT_NAME2;
3361
- var FORM_ACTIONS_NAME = "Form.Actions";
3362
- var FormActions = ({ classNames }) => {
3363
- const { t } = useTranslation10(translationKey10);
3364
- const { form: { canSave, onSave, onCancel }, readonly, layout } = useFormContext(FORM_ACTIONS_NAME);
3365
- if (readonly || layout === "static") {
3366
- return null;
3367
- }
3368
- return /* @__PURE__ */ React25.createElement("div", {
3369
- className: mx6(withColumn.center(), formTheme.styles().actions(), classNames)
3370
- }, onCancel && /* @__PURE__ */ React25.createElement(IconButton5, {
3371
- icon: "ph--x--regular",
3372
- iconEnd: true,
3373
- label: t("cancel-button.label"),
3374
- onClick: onCancel,
3375
- "data-testid": "cancel-button"
3376
- }), onSave && /* @__PURE__ */ React25.createElement(IconButton5, {
3377
- type: "submit",
3378
- variant: "primary",
3379
- disabled: !canSave,
3380
- icon: "ph--check--regular",
3381
- iconEnd: true,
3382
- label: t("save-button.label"),
3383
- onClick: onSave,
3384
- "data-testid": "save-button"
3385
- }));
3386
- };
3387
- FormActions.displayName = FORM_ACTIONS_NAME;
3388
- var FORM_SECTION_NAME = "Form.Section";
3389
- var FormSection = composable(({ children, title, description, ...props }, forwardedRef) => {
3390
- const { variant = "default" } = useFormContext(FORM_SECTION_NAME);
3391
- const styles3 = formTheme.styles({
3392
- variant
3393
- });
3394
- return /* @__PURE__ */ React25.createElement("div", {
3395
- ...composableProps(props, {
3396
- classNames: styles3.section()
3397
- }),
3398
- ref: forwardedRef
3399
- }, (title || description) && /* @__PURE__ */ React25.createElement("div", {
3400
- className: styles3.sectionHeader()
3401
- }, title && /* @__PURE__ */ React25.createElement("h2", {
3402
- className: styles3.sectionTitle()
3403
- }, title), description && /* @__PURE__ */ React25.createElement(MarkdownView, {
3404
- classNames: styles3.sectionDescription(),
3405
- content: description
3406
- })), children);
3407
- });
3408
- FormSection.displayName = FORM_SECTION_NAME;
3409
- var FORM_GROUP_NAME = "Form.Group";
3410
- var FormGroup = composable(({ children, ...props }, forwardedRef) => {
3411
- const { variant = "default" } = useFormContext(FORM_SECTION_NAME);
3412
- const styles3 = formTheme.styles({
3413
- variant
3414
- });
3415
- return /* @__PURE__ */ React25.createElement("div", {
3416
- ...composableProps(props, {
3417
- classNames: styles3.group()
3418
- }),
3419
- ref: forwardedRef
3420
- }, children);
3421
- });
3422
- FormGroup.displayName = FORM_GROUP_NAME;
3423
- var FORM_SUBMIT_NAME = "Form.Submit";
3424
- var FormSubmit = ({ classNames, label, icon, disabled }) => {
3425
- const { t } = useTranslation10(translationKey10);
3426
- const { form: { canSave, onSave }, readonly, layout } = useFormContext(FORM_SUBMIT_NAME);
3427
- if (readonly || layout === "static") {
3428
- return null;
3429
- }
3430
- return /* @__PURE__ */ React25.createElement("div", {
3431
- className: mx6(formTheme.styles().submit(), classNames)
3432
- }, /* @__PURE__ */ React25.createElement(IconButton5, {
3433
- classNames: "w-full",
3434
- type: "submit",
3435
- variant: "primary",
3436
- disabled: disabled ?? !canSave,
3437
- icon: icon ?? "ph--check--regular",
3438
- label: label ?? t("save-button.label"),
3439
- onClick: onSave,
3440
- "data-testid": "save-button"
3441
- }));
3442
- };
3443
- FormSubmit.displayName = FORM_SUBMIT_NAME;
3444
- var FORM_ERROR_NAME = "Form.Error";
3445
- var FormError = ({ children, classNames }) => {
3446
- if (!children) {
3447
- return null;
3448
- }
3449
- return /* @__PURE__ */ React25.createElement(Input14.Root, {
3450
- validationValence: "error"
3451
- }, /* @__PURE__ */ React25.createElement(Input14.Validation, {
3452
- classNames
3453
- }, children));
3454
- };
3455
- FormError.displayName = FORM_ERROR_NAME;
3456
-
3457
- // src/components/Form/Form.tsx
3458
- var Form = {
3459
- Root: FormRoot,
3460
- Viewport: FormViewport,
3461
- Content: FormContent,
3462
- Section: FormSection,
3463
- Group: FormGroup,
3464
- FieldSet: FormFieldSetController,
3465
- Layout: FormLayoutController,
3466
- Label: FormFieldLabel,
3467
- Row: FormRow,
3468
- Actions: FormActions,
3469
- Submit: FormSubmit,
3470
- Error: FormError
3471
- };
3472
-
3473
- // src/components/FieldEditor/FieldEditor.tsx
3474
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
3475
- var FieldEditor = ({ readonly, projection, field, registry, view, onSave, onCancel }) => {
3476
- const { t } = useTranslation11(translationKey11);
3477
- const [props, setProps] = useState10(projection.getFieldProjection(field.id).props);
3478
- useEffect9(() => setProps(projection.getFieldProjection(field.id).props), [
3479
- field,
3480
- projection
3481
- ]);
3482
- const [schemas, setSchemas] = useState10([]);
3483
- useAsyncEffect(async () => {
3484
- if (!registry) {
3485
- return;
3486
- }
3487
- const subscription = registry.query(Filter2.type(Type4.Type)).subscribe((query) => setSchemas(query.results), {
3488
- fire: true
3489
- });
3490
- const schemas2 = await registry.query(Filter2.type(Type4.Type)).run();
3491
- setSchemas(schemas2);
3492
- return () => subscription?.();
3493
- }, [
3494
- registry
3495
- ]);
3496
- const [referenceSchema, setReferenceSchema] = useState10();
3497
- useEffect9(() => {
3498
- setReferenceSchema(() => schemas.find((schema) => Type4.getTypename(schema) === props?.referenceSchema));
3499
- }, [
3500
- schemas,
3501
- props?.referenceSchema
3502
- ]);
3503
- const [{ fieldSchema }, setFieldSchema] = useState10({
3504
- fieldSchema: getFormatSchema(props?.format)
3505
- });
3506
- const fieldMap = useMemo13(() => ({
3507
- ["format"]: (props2) => /* @__PURE__ */ React26.createElement(SelectField, {
3508
- ...props2,
3509
- options: FormatEnums.filter((value3) => value3 !== Format6.TypeFormat.None).map((value3) => ({
3510
- value: value3,
3511
- label: t(`format.${value3}.label`)
3512
- }))
3513
- }),
3514
- ["referenceSchema"]: (props2) => /* @__PURE__ */ React26.createElement(SelectField, {
3515
- ...props2,
3516
- options: schemas.map((schema) => Type4.getTypename(schema)).filter((typename) => typename != null).map((typename) => ({
3517
- value: typename
3518
- }))
3519
- }),
3520
- ["referencePath"]: (props2) => /* @__PURE__ */ React26.createElement(SelectField, {
3521
- ...props2,
3522
- options: referenceSchema ? getFormProperties(Type4.getSchema(referenceSchema).ast).sort((a, b) => a.name.toString().localeCompare(b.name.toString())).map((p) => ({
3523
- value: p.name.toString()
3524
- })) : []
3525
- }),
3526
- ["options"]: (props2) => /* @__PURE__ */ React26.createElement(SelectOptionField, props2)
3527
- }), [
3528
- t,
3529
- schemas,
3530
- referenceSchema
3531
- ]);
3532
- const propIsNotType = useCallback18((props2) => props2.filter((prop) => prop.name !== "type"), []);
3533
- const handleValuesChanged = useCallback18((_props) => {
3534
- setFieldSchema((prev) => {
3535
- const fieldSchema2 = getFormatSchema(_props.format);
3536
- if (prev.fieldSchema === fieldSchema2) {
3537
- return prev;
3538
- }
3539
- return {
3540
- fieldSchema: fieldSchema2
3541
- };
3542
- });
3543
- setReferenceSchema((prev) => {
3544
- if (_props.referenceSchema !== (prev ? Type4.getTypename(prev) : void 0)) {
3545
- const newSchema = schemas.find((schema) => Type4.getTypename(schema) === _props.referenceSchema);
3546
- if (newSchema) {
3547
- return newSchema;
3548
- }
3549
- }
3550
- return prev;
3551
- });
3552
- setProps((props2) => {
3553
- const type = formatToType[_props.format];
3554
- const additionalProps = formatToAdditionalPropertyAttributes[_props.format];
3555
- if (props2.type !== type) {
3556
- return {
3557
- ...props2,
3558
- ..._props,
3559
- ...additionalProps,
3560
- type
3561
- };
3562
- }
3563
- return _props;
3564
- });
3565
- }, [
3566
- schemas,
3567
- props.format,
3568
- props.referenceSchema
3569
- ]);
3570
- const handleValidate = useCallback18(({ property }) => {
3571
- if (property && projection.getFields().find((f) => f.path === property && f.path !== field.path)) {
3572
- return [
3573
- {
3574
- path: "property",
3575
- message: `property is not unique: '${property}'`
3576
- }
3577
- ];
3578
- }
3579
- }, [
3580
- projection.getFields(),
3581
- field
3582
- ]);
3583
- const handleSave = useCallback18((props2) => {
3584
- if (view) {
3585
- Obj3.update(view, () => {
3586
- projection.setFieldProjection({
3587
- field,
3588
- props: props2
3589
- });
3590
- });
3591
- } else {
3592
- projection.setFieldProjection({
3593
- field,
3594
- props: props2
3595
- });
3596
- }
3597
- onSave();
3598
- }, [
3599
- projection,
3600
- field,
3601
- view,
3602
- onSave
3603
- ]);
3604
- const handleCancel = useCallback18(() => {
3605
- onSave();
3606
- requestAnimationFrame(() => onCancel?.());
3607
- }, [
3608
- onSave
3609
- ]);
3610
- if (!fieldSchema) {
3611
- log3.warn("invalid format", {
3612
- props
3613
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 159, S: void 0 });
3614
- return null;
3615
- }
3616
- return /* @__PURE__ */ React26.createElement(Form.Root, {
3617
- key: field.id,
3618
- fieldMap,
3619
- autoFocus: true,
3620
- readonly,
3621
- schema: fieldSchema,
3622
- values: props,
3623
- filter: propIsNotType,
3624
- sort: [
3625
- "property",
3626
- "format"
3627
- ],
3628
- onValuesChanged: handleValuesChanged,
3629
- onValidate: handleValidate,
3630
- onSave: handleSave,
3631
- onCancel: handleCancel
3632
- }, /* @__PURE__ */ React26.createElement(Form.Content, null, /* @__PURE__ */ React26.createElement(Form.FieldSet, null), /* @__PURE__ */ React26.createElement(Form.Actions, null)));
3633
- };
3634
-
3635
- // src/components/ObjectForm/ObjectForm.tsx
3636
- import React27, { useCallback as useCallback19, useMemo as useMemo14 } from "react";
3637
- import { Obj as Obj4, Ref as Ref7, Tag as Tag3, Type as Type5 } from "@dxos/echo";
3638
- import { SchemaEx as SchemaEx11 } from "@dxos/effect";
3639
- import { invariant as invariant2 } from "@dxos/invariant";
3640
- import { HuePicker as HuePicker2 } from "@dxos/react-ui-pickers";
3641
- import { translationKey as translationKey12 } from "#translations";
3642
- var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ObjectForm/ObjectForm.tsx";
3643
- var ObjectForm = ({ object, type }) => {
3644
- const db = Obj4.getDatabase(object);
3645
- const meta = Obj4.getMeta(object);
3646
- const tags = [
3647
- ...meta.tags
3648
- ];
3649
- const values = useMemo14(() => ({
3650
- [META_TAGS_KEY]: tags,
3651
- ...object
3652
- }), [
3653
- object,
3654
- tags
3655
- ]);
3656
- const formSchema = useMemo14(() => withMetaTags(Type5.getSchema(type)), [
3657
- type
3658
- ]);
3659
- const handleCreate = useCallback19((type2, values2) => {
3660
- invariant2(db, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 29, S: void 0, A: ["db", ""] });
3661
- invariant2(Type5.isObject(type2), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 30, S: void 0, A: ["Type.isObject(type)", ""] });
3662
- const newObject = db.add(Obj4.make(type2, values2));
3663
- if (Obj4.instanceOf(Tag3.Tag, newObject)) {
3664
- Obj4.update(object, (object2) => {
3665
- Obj4.getMeta(object2).tags = [
3666
- ...Obj4.getMeta(object2).tags,
3667
- Ref7.make(newObject)
3668
- ];
3669
- });
3670
- }
3671
- }, []);
3672
- const handleChange = useCallback19(({ [META_TAGS_KEY]: metaTags, ...values2 }, { isValid, changed }) => {
3673
- if (!isValid) {
3674
- return;
3675
- }
3676
- const changedPaths = Object.keys(changed).filter((path) => changed[path]);
3677
- const hasTagsChange = changedPaths.some((path) => SchemaEx11.splitJsonPath(path)[0] === META_TAGS_KEY);
3678
- if (hasTagsChange) {
3679
- Obj4.update(object, (object2) => {
3680
- Obj4.getMeta(object2).tags = Array.isArray(metaTags) ? [
3681
- ...metaTags
3682
- ] : [];
3683
- });
3684
- }
3685
- const nonTagPaths = changedPaths.filter((path) => SchemaEx11.splitJsonPath(path)[0] !== META_TAGS_KEY);
3686
- if (nonTagPaths.length > 0) {
3687
- Obj4.update(object, () => {
3688
- for (const path of nonTagPaths) {
3689
- const parts = SchemaEx11.splitJsonPath(path);
3690
- const value3 = Obj4.getValue(values2, parts);
3691
- Obj4.setValue(object, parts, value3);
3692
- }
3693
- });
3694
- }
3695
- }, [
3696
- object
3697
- ]);
3698
- return /* @__PURE__ */ React27.createElement(Form.Root, {
3699
- schema: formSchema,
3700
- defaultValues: values,
3701
- createTypename: Type5.getTypename(Tag3.Tag),
3702
- createOptionIcon: "ph--plus--regular",
3703
- createOptionLabel: [
3704
- "add-tag.label",
3705
- {
3706
- ns: translationKey12
3707
- }
3708
- ],
3709
- createInitialValuePath: "label",
3710
- createFieldMap,
3711
- db,
3712
- onValuesChanged: handleChange,
3713
- onCreate: handleCreate
3714
- }, /* @__PURE__ */ React27.createElement(Form.Viewport, null, /* @__PURE__ */ React27.createElement(Form.Content, null, /* @__PURE__ */ React27.createElement(Form.FieldSet, null))));
3715
- };
3716
- var createFieldMap = {
3717
- hue: ({ type, label, presentation, getValue, onValueChange }) => {
3718
- const handleChange = useCallback19((nextHue) => onValueChange(type, nextHue), [
3719
- onValueChange,
3720
- type
3721
- ]);
3722
- const handleReset = useCallback19(() => onValueChange(type, void 0), [
3723
- onValueChange,
3724
- type
3725
- ]);
3726
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, presentation !== "inline" && /* @__PURE__ */ React27.createElement(Form.Label, {
3727
- label
3728
- }), /* @__PURE__ */ React27.createElement(HuePicker2, {
3729
- value: getValue(),
3730
- onChange: handleChange,
3731
- onReset: handleReset
3732
- }));
3733
- }
3734
- };
3735
-
3736
- // src/components/ObjectProperties/ObjectProperties.tsx
3737
- import * as Function from "effect/Function";
3738
- import * as Option8 from "effect/Option";
3739
- import React28, { useCallback as useCallback20, useMemo as useMemo15 } from "react";
3740
- import { Obj as Obj5, Tag as Tag4, Type as Type6 } from "@dxos/echo";
3741
- import { useType } from "@dxos/echo-react";
3742
- import { SchemaEx as SchemaEx12 } from "@dxos/effect";
3743
- import { invariant as invariant3 } from "@dxos/invariant";
3744
- import { composable as composable2, composableProps as composableProps2 } from "@dxos/react-ui";
3745
- import { HuePicker as HuePicker3 } from "@dxos/react-ui-pickers";
3746
- import { FactoryAnnotation } from "@dxos/schema";
3747
- import { translationKey as translationKey13 } from "#translations";
3748
- var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ObjectProperties/ObjectProperties.tsx";
3749
- var ObjectProperties = composable2(({ children, object, getCreateDefaults, resolveCreateEntry, ...props }, forwardedRef) => {
3750
- const db = Obj5.getDatabase(object);
3751
- const meta = Obj5.getMeta(object);
3752
- const tags = [
3753
- ...meta.tags
3754
- ];
3755
- const values = useMemo15(() => ({
3756
- [META_TAGS_KEY]: tags,
3757
- ...object
3758
- }), [
3759
- object,
3760
- tags
3761
- ]);
3762
- const typeUri = Obj5.getTypeURI(object);
3763
- const typeFromRegistry = useType(db, typeUri);
3764
- const type = Obj5.getType(object) ?? typeFromRegistry;
3765
- const formSchema = useMemo15(() => {
3766
- return Function.pipe(type, Option8.fromNullable, Option8.map((type2) => Type6.getSchema(type2)), Option8.map((schema) => withMetaTags(schema)), Option8.getOrUndefined);
3767
- }, [
3768
- type
3769
- ]);
3770
- const handleCreate = useCallback20((type2, values2) => {
3771
- invariant3(db, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 52, S: void 0, A: ["db", ""] });
3772
- invariant3(Type6.isObject(type2), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 53, S: void 0, A: ["Type.isObject(type)", ""] });
3773
- const factory = Option8.getOrUndefined(FactoryAnnotation.get(Type6.getSchema(type2)));
3774
- const newObject = factory ? factory(values2) : Obj5.make(type2, values2);
3775
- return db.add(newObject);
3776
- }, [
3777
- db
3778
- ]);
3779
- const handleChange = useCallback20(({ [META_TAGS_KEY]: metaTags, ...values2 }, { isValid, changed }) => {
3780
- if (!isValid) {
3781
- return;
3782
- }
3783
- const changedPaths = Object.keys(changed).filter((path) => changed[path]);
3784
- const hasTagsChange = changedPaths.some((path) => SchemaEx12.splitJsonPath(path)[0] === META_TAGS_KEY);
3785
- if (hasTagsChange) {
3786
- Obj5.update(object, (object2) => {
3787
- Obj5.getMeta(object2).tags = Array.isArray(metaTags) ? [
3788
- ...metaTags
3789
- ] : [];
3790
- });
3791
- }
3792
- const nonTagPaths = changedPaths.filter((path) => SchemaEx12.splitJsonPath(path)[0] !== META_TAGS_KEY);
3793
- if (nonTagPaths.length > 0) {
3794
- Obj5.update(object, () => {
3795
- for (const path of nonTagPaths) {
3796
- const parts = SchemaEx12.splitJsonPath(path);
3797
- const value3 = Obj5.getValue(values2, parts);
3798
- Obj5.setValue(object, parts, value3);
3799
- }
3800
- });
3801
- }
3802
- }, [
3803
- object
3804
- ]);
3805
- if (!formSchema) {
3806
- return null;
3807
- }
3808
- return /* @__PURE__ */ React28.createElement(Form.Root, {
3809
- db,
3810
- schema: formSchema,
3811
- defaultValues: values,
3812
- createTypename: Type6.getTypename(Tag4.Tag),
3813
- createOptionIcon: "ph--plus--regular",
3814
- createOptionLabel: [
3815
- "add-tag.label",
3816
- {
3817
- ns: translationKey13
3818
- }
3819
- ],
3820
- createInitialValuePath: "label",
3821
- createFieldMap: createFieldMap2,
3822
- onValuesChanged: handleChange,
3823
- onCreate: handleCreate,
3824
- getCreateDefaults,
3825
- resolveCreateEntry
3826
- }, /* @__PURE__ */ React28.createElement(Form.Viewport, {
3827
- ...composableProps2(props),
3828
- scroll: true,
3829
- ref: forwardedRef
3830
- }, /* @__PURE__ */ React28.createElement(Form.Content, null, /* @__PURE__ */ React28.createElement(Form.FieldSet, null), /* @__PURE__ */ React28.createElement(Form.Section, null, children))));
3831
- });
3832
- var createFieldMap2 = {
3833
- hue: ({ type, label, presentation, getValue, onValueChange }) => {
3834
- const handleChange = useCallback20((nextHue) => onValueChange(type, nextHue), [
3835
- onValueChange,
3836
- type
3837
- ]);
3838
- const handleReset = useCallback20(() => onValueChange(type, void 0), [
3839
- onValueChange,
3840
- type
3841
- ]);
3842
- return /* @__PURE__ */ React28.createElement(React28.Fragment, null, presentation !== "inline" && /* @__PURE__ */ React28.createElement(Form.Label, {
3843
- label
3844
- }), /* @__PURE__ */ React28.createElement(HuePicker3, {
3845
- value: getValue(),
3846
- onChange: handleChange,
3847
- onReset: handleReset
3848
- }));
3849
- }
3850
- };
3851
-
3852
- // src/components/ObjectTree/ObjectTree.tsx
3853
- import * as SchemaAST8 from "effect/SchemaAST";
3854
- import React29 from "react";
3855
- import { composable as composable3, composableProps as composableProps3 } from "@dxos/react-ui";
3856
- import { mx as mx7 } from "@dxos/ui-theme";
3857
- var ObjectTreeImpl = composable3(({ schema, value: value3, ...props }, forwardedRef) => /* @__PURE__ */ React29.createElement("div", {
3858
- ...composableProps3(props, {
3859
- role: "table",
3860
- classNames: "font-mono text-sm w-full"
3861
- }),
3862
- ref: forwardedRef
3863
- }, /* @__PURE__ */ React29.createElement(Node, {
3864
- ast: schema.ast,
3865
- value: value3,
3866
- label: null,
3867
- depth: 0
3868
- })));
3869
- ObjectTreeImpl.displayName = "ObjectTree";
3870
- var ObjectTree = ObjectTreeImpl;
3871
- var unwrap = (ast) => {
3872
- if (SchemaAST8.isRefinement(ast)) {
3873
- return unwrap(ast.from);
3874
- }
3875
- if (SchemaAST8.isSuspend(ast)) {
3876
- return unwrap(ast.f());
3877
- }
3878
- if (SchemaAST8.isTransformation(ast)) {
3879
- return unwrap(ast.from);
3880
- }
3881
- return ast;
3882
- };
3883
- var stripOptional = (ast) => {
3884
- if (!SchemaAST8.isUnion(ast)) {
3885
- return ast;
3886
- }
3887
- const nonUndef = ast.types.filter((t) => !SchemaAST8.isUndefinedKeyword(t));
3888
- return nonUndef.length === 1 ? nonUndef[0] : ast;
3889
- };
3890
- var Node = ({ ast, value: value3, label, depth }) => {
3891
- const inner = unwrap(stripOptional(ast));
3892
- if (SchemaAST8.isTypeLiteral(inner) && isPlainObject(value3)) {
3893
- const childDepth = label === null ? depth : depth + 1;
3894
- return /* @__PURE__ */ React29.createElement(React29.Fragment, null, label !== null && /* @__PURE__ */ React29.createElement(Row, {
3895
- label,
3896
- value: null,
3897
- depth,
3898
- kind: "group"
3899
- }), inner.propertySignatures.map((prop) => /* @__PURE__ */ React29.createElement(Node, {
3900
- key: String(prop.name),
3901
- ast: prop.type,
3902
- value: value3[String(prop.name)],
3903
- label: String(prop.name),
3904
- depth: childDepth
3905
- })));
3906
- }
3907
- if (SchemaAST8.isTupleType(inner) && Array.isArray(value3)) {
3908
- const elemType = inner.rest[0]?.type;
3909
- return /* @__PURE__ */ React29.createElement(React29.Fragment, null, label !== null && /* @__PURE__ */ React29.createElement(Row, {
3910
- label,
3911
- value: `Array(${value3.length})`,
3912
- depth,
3913
- kind: "group"
3914
- }), value3.map((item, index) => /* @__PURE__ */ React29.createElement(Node, {
3915
- key: index,
3916
- ast: elemType ?? SchemaAST8.unknownKeyword,
3917
- value: item,
3918
- label: `[${index}]`,
3919
- depth: depth + 1
3920
- })));
3921
- }
3922
- return /* @__PURE__ */ React29.createElement(Row, {
3923
- label: label ?? "",
3924
- value: formatLeaf(value3),
3925
- depth,
3926
- kind: "leaf"
3927
- });
3928
- };
3929
- var Row = ({ label, value: value3, depth }) => /* @__PURE__ */ React29.createElement("div", {
3930
- role: "row",
3931
- className: mx7("flex items-baseline gap-3 py-1"),
3932
- style: {
3933
- paddingInlineStart: 8 + depth * 16
3934
- }
3935
- }, /* @__PURE__ */ React29.createElement("div", {
3936
- role: "cell",
3937
- className: "text-subdued"
3938
- }, label), /* @__PURE__ */ React29.createElement("div", {
3939
- role: "cell",
3940
- className: mx7("break-all text-base-fg")
3941
- }, value3));
3942
- var isPlainObject = (value3) => typeof value3 === "object" && value3 !== null && !Array.isArray(value3);
3943
- var formatLeaf = (value3) => {
3944
- if (value3 === void 0) {
3945
- return null;
3946
- }
3947
- if (value3 === null) {
3948
- return /* @__PURE__ */ React29.createElement("span", {
3949
- className: "text-description"
3950
- }, "null");
3951
- }
3952
- switch (typeof value3) {
3953
- case "string":
3954
- return /* @__PURE__ */ React29.createElement("span", {
3955
- className: "text-blue-text"
3956
- }, JSON.stringify(value3));
3957
- case "number":
3958
- case "bigint":
3959
- return /* @__PURE__ */ React29.createElement("span", {
3960
- className: "text-green-text"
3961
- }, String(value3));
3962
- case "boolean":
3963
- return /* @__PURE__ */ React29.createElement("span", {
3964
- className: "text-purple-text"
3965
- }, String(value3));
3966
- default:
3967
- try {
3968
- return JSON.stringify(value3);
3969
- } catch {
3970
- return String(value3);
3971
- }
3972
- }
3973
- };
3974
-
3975
- // src/components/RefEditor/RefEditor.tsx
3976
- import { Prec } from "@codemirror/state";
3977
- import React30, { forwardRef as forwardRef2, useCallback as useCallback21, useEffect as useEffect10, useMemo as useMemo16, useRef as useRef8 } from "react";
3978
- import { Filter as Filter3, Obj as Obj6 } from "@dxos/echo";
3979
- import { useQuery as useQuery2 } from "@dxos/react-client/echo";
3980
- import { useThemeContext, useTranslation as useTranslation12 } from "@dxos/react-ui";
3981
- import { Editor as Editor2 } from "@dxos/react-ui-editor";
3982
- import { createBasicExtensions, createThemeExtensions, insertAtCursor, keymap } from "@dxos/ui-editor";
3983
- import { getHashHue as getHashHue2 } from "@dxos/ui-theme";
3984
- import { translationKey as translationKey14 } from "#translations";
3985
-
3986
- // src/components/RefEditor/ref-editor-extension.ts
3987
- import { RangeSetBuilder, StateEffect, StateField } from "@codemirror/state";
3988
- import { Decoration, EditorView, WidgetType } from "@codemirror/view";
3989
- import { Domino } from "@dxos/ui";
3990
- import { getHashHue, getStyles as getStyles2, mx as mx8 } from "@dxos/ui-theme";
3991
- var REF_REGEX = /^@([A-Za-z0-9]+)$/;
3992
- var EMAIL_REGEX = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
3993
- var NAME_ADDR_REGEX = /^(.*?)\s*<([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})>$/;
3994
- var formatMailbox = (name, email) => name ? `${name} <${email}>` : email;
3995
- var refEditorRedecorate = StateEffect.define();
3996
- var refEditor = ({ mode = "ref", getRef, match: match2 } = {}) => {
3997
- const buildRefDecorations = (state) => {
3998
- const deco = new RangeSetBuilder();
3999
- const text = state.doc.toString();
4000
- const tokenRegex = /\S+/g;
4001
- let tokenMatch;
4002
- while (tokenMatch = tokenRegex.exec(text)) {
4003
- const token = tokenMatch[0];
4004
- const from = tokenMatch.index;
4005
- const to = from + token.length;
4006
- const refMatch = token.match(REF_REGEX);
4007
- if (refMatch) {
4008
- const ref = getRef?.(refMatch[1]);
4009
- if (ref) {
4010
- deco.add(from, to, Decoration.widget({
4011
- widget: new RefWidget(ref.label, ref.hue ?? getHashHue(refMatch[1])),
4012
- atomic: true
4013
- }));
4014
- }
4015
- continue;
4016
- }
4017
- if (match2?.test(token)) {
4018
- deco.add(from, to, Decoration.widget({
4019
- widget: new TokenWidget(token),
4020
- atomic: true
4021
- }));
4022
- }
4023
- }
4024
- return deco.finish();
4025
- };
4026
- const buildEmailDecorations = (state) => {
4027
- const addrRegex = match2 ?? EMAIL_REGEX;
4028
- const deco = new RangeSetBuilder();
4029
- const text = state.doc.toString();
4030
- const segmentRegex = /[^,]+(?:,\s*)?|,\s*/g;
4031
- let segmentMatch;
4032
- while (segmentMatch = segmentRegex.exec(text)) {
4033
- const segment = segmentMatch[0];
4034
- const commaIndex = segment.indexOf(",");
4035
- const content = (commaIndex === -1 ? segment : segment.slice(0, commaIndex)).trim();
4036
- if (!content) {
4037
- continue;
4038
- }
4039
- const leading = segment.length - segment.trimStart().length;
4040
- const from = segmentMatch.index + leading;
4041
- const to = segmentMatch.index + segment.length;
4042
- const nameAddr = content.match(NAME_ADDR_REGEX);
4043
- if (nameAddr) {
4044
- deco.add(from, to, Decoration.widget({
4045
- widget: new RefWidget(nameAddr[1] || nameAddr[2], getHashHue(nameAddr[2])),
4046
- atomic: true
4047
- }));
4048
- } else if (addrRegex.test(content)) {
4049
- deco.add(from, to, Decoration.widget({
4050
- widget: new TokenWidget(content),
4051
- atomic: true
4052
- }));
4053
- }
4054
- }
4055
- return deco.finish();
4056
- };
4057
- const buildDecorations = mode === "email" ? buildEmailDecorations : buildRefDecorations;
4058
- return [
4059
- styles2,
4060
- StateField.define({
4061
- create: (state) => buildDecorations(state),
4062
- update: (deco, tr) => {
4063
- if (tr.docChanged || tr.effects.some((effect) => effect.is(refEditorRedecorate))) {
4064
- return buildDecorations(tr.state);
4065
- }
4066
- return deco;
4067
- },
4068
- provide: (field) => [
4069
- EditorView.decorations.from(field),
4070
- EditorView.atomicRanges.of((view) => {
4071
- const builder = new RangeSetBuilder();
4072
- const cursor = view.state.field(field).iter();
4073
- while (cursor.value) {
4074
- if (cursor.value.spec.atomic) {
4075
- builder.add(cursor.from, cursor.to, cursor.value);
4076
- }
4077
- cursor.next();
4078
- }
4079
- return builder.finish();
4080
- })
4081
- ]
4082
- })
4083
- ];
4084
- };
4085
- var lineHeight = "30px";
4086
- var container = (classNames, ...children) => {
4087
- const inner = Domino.of("span").classNames(mx8("inline-flex h-[26px] border rounded-xs", classNames)).append(...children);
4088
- return Domino.of("span").classNames("inline-flex h-[28px] align-middle").append(inner).root;
4089
- };
4090
- var RefWidget = class extends WidgetType {
4091
- _label;
4092
- _hue;
4093
- constructor(_label, _hue) {
4094
- super(), this._label = _label, this._hue = _hue;
4095
- }
4096
- eq(other) {
4097
- return this._label === other._label && this._hue === other._hue;
4098
- }
4099
- toDOM() {
4100
- const { bg: fill, border, surface } = getStyles2(this._hue);
4101
- return container(border, Domino.of("span").classNames(mx8("flex items-center px-1 text-black text-xs", fill)).text("@"), Domino.of("span").classNames(mx8("flex items-center px-1 text-subdued text-sm rounded-r-[3px]", surface)).text(this._label));
4102
- }
4103
- };
4104
- var TokenWidget = class extends WidgetType {
4105
- _token;
4106
- constructor(_token) {
4107
- super(), this._token = _token;
4108
- }
4109
- eq(other) {
4110
- return this._token === other._token;
4111
- }
4112
- toDOM() {
4113
- return container("border-separator", Domino.of("span").classNames("flex items-center px-1 text-subdued text-sm").text(this._token));
4114
- }
4115
- };
4116
- var styles2 = EditorView.theme({
4117
- ".cm-line": {
4118
- lineHeight
4119
- },
4120
- // Match the standard Input block size (md density): 2.5rem, 2rem on pointer-fine devices.
4121
- ".cm-scroller": {
4122
- alignItems: "center",
4123
- minHeight: "2.5rem"
4124
- },
4125
- "@media (pointer: fine)": {
4126
- ".cm-scroller": {
4127
- minHeight: "2rem"
4128
- }
4129
- }
4130
- });
4131
-
4132
- // src/components/RefEditor/RefEditor.tsx
4133
- var RefEditor = /* @__PURE__ */ forwardRef2(({ db, type, value: value3, readonly, mode = "ref", match: match2, getLabel, getValues, icon = "ph--link--regular", placeholder: placeholderProp, activateOnTyping, numItems = 8, ...props }, forwardedRef) => {
4134
- const { t } = useTranslation12(translationKey14);
4135
- const { themeMode } = useThemeContext();
4136
- const getObjectLabel = useCallback21((object) => getLabel?.(object) ?? Obj6.getLabel(object) ?? object.id, [
4137
- getLabel
4138
- ]);
4139
- const filter = useMemo16(() => Filter3.type(type), [
4140
- type
4141
- ]);
4142
- const objects = useQuery2(db, filter);
4143
- const refs = useMemo16(() => new Map(objects.map((object) => [
4144
- object.id,
4145
- {
4146
- label: getObjectLabel(object),
4147
- hue: getHashHue2(object.id)
4148
- }
4149
- ])), [
4150
- objects,
4151
- getObjectLabel
4152
- ]);
4153
- const refsRef = useRef8(refs);
4154
- refsRef.current = refs;
4155
- const controllerRef = useRef8(null);
4156
- const composedRef = useCallback21((controller) => {
4157
- controllerRef.current = controller;
4158
- if (typeof forwardedRef === "function") {
4159
- forwardedRef(controller);
4160
- } else if (forwardedRef) {
4161
- forwardedRef.current = controller;
4162
- }
4163
- }, [
4164
- forwardedRef
4165
- ]);
4166
- useEffect10(() => {
4167
- controllerRef.current?.view?.dispatch({
4168
- effects: refEditorRedecorate.of(null)
4169
- });
4170
- }, [
4171
- refs
4172
- ]);
4173
- const getMenu = useCallback21(async ({ text }) => {
4174
- const query = (text ?? "").replace(/^@/, "").toLowerCase();
4175
- const matchesQuery = (object) => !query || getObjectLabel(object).toLowerCase().includes(query) || (getValues?.(object) ?? []).some((entry) => entry.toLowerCase().includes(query));
4176
- if (mode === "email") {
4177
- const mailboxes = objects.flatMap((object) => (getValues?.(object) ?? []).map((entry) => ({
4178
- object,
4179
- entry
4180
- }))).filter(({ object, entry }) => !query || getObjectLabel(object).toLowerCase().includes(query) || entry.toLowerCase().includes(query));
4181
- return [
4182
- {
4183
- id: "mailboxes",
4184
- items: mailboxes.slice(0, numItems * 2).map(({ object, entry }) => ({
4185
- id: `${object.id}-${entry}`,
4186
- label: `${getObjectLabel(object)} (${entry})`,
4187
- icon,
4188
- onSelect: ({ view, head }) => insertMailbox(view, head, formatMailbox(getObjectLabel(object), entry))
4189
- }))
4190
- }
4191
- ];
4192
- }
4193
- return [
4194
- {
4195
- id: "refs",
4196
- items: objects.filter(matchesQuery).slice(0, numItems * 2).map((object) => {
4197
- const label = getObjectLabel(object);
4198
- const detail = getValues?.(object)?.[0];
4199
- return {
4200
- id: object.id,
4201
- label: detail && detail !== label ? `${label} (${detail})` : label,
4202
- icon,
4203
- onSelect: ({ view, head }) => insertAtCursor(view, head, `@${object.id} `)
4204
- };
4205
- })
4206
- }
4207
- ];
4208
- }, [
4209
- objects,
4210
- numItems,
4211
- mode,
4212
- icon,
4213
- getObjectLabel,
4214
- getValues
4215
- ]);
4216
- const placeholder = useMemo16(() => ({
4217
- content: placeholderProp ?? t(activateOnTyping ? "ref-editor-auto.placeholder" : "ref-editor.placeholder"),
4218
- focusOnly: false
4219
- }), [
4220
- t,
4221
- placeholderProp,
4222
- activateOnTyping
4223
- ]);
4224
- const extensions = useMemo16(() => [
4225
- createBasicExtensions({
4226
- readOnly: readonly,
4227
- lineWrapping: false
4228
- }),
4229
- createThemeExtensions({
4230
- themeMode,
4231
- slots: {
4232
- scroller: {
4233
- className: "scrollbar-none"
4234
- }
4235
- }
4236
- }),
4237
- refEditor({
4238
- mode,
4239
- match: match2,
4240
- getRef: (id) => refsRef.current.get(id)
4241
- }),
4242
- Prec.highest(keymap.of([
4243
- {
4244
- key: "Enter",
4245
- // Commit the in-progress token when it matches (insert the mode's separator, the
4246
- // same gesture as typing it); always consume the key (single-line input).
4247
- run: (view) => {
4248
- const { head } = view.state.selection.main;
4249
- const line = view.state.doc.lineAt(head);
4250
- const before = line.text.slice(0, head - line.from);
4251
- if (mode === "email") {
4252
- const segment = before.slice(before.lastIndexOf(",") + 1).trim();
4253
- if (NAME_ADDR_REGEX.test(segment) || (match2 ?? EMAIL_REGEX).test(segment)) {
4254
- insertAtCursor(view, head, ", ");
4255
- }
4256
- } else {
4257
- const token = before.slice(before.search(/\S+$/));
4258
- if (token && match2?.test(token)) {
4259
- insertAtCursor(view, head, " ");
4260
- }
4261
- }
4262
- return true;
4263
- }
4264
- }
4265
- ]))
4266
- ], [
4267
- readonly,
4268
- themeMode,
4269
- mode,
4270
- match2
4271
- ]);
4272
- return /* @__PURE__ */ React30.createElement(Editor2.Root, {
4273
- extensions,
4274
- numItems,
4275
- trigger: "@",
4276
- activateOnTyping,
4277
- placeholder,
4278
- // The menu owns its own matching (labels AND values); the built-in label filter would
4279
- // drop value-matched objects whose label doesn't start with the query.
4280
- filter: false,
4281
- getMenu,
4282
- ref: composedRef
4283
- }, /* @__PURE__ */ React30.createElement(Editor2.View, {
4284
- // TOOD(burdon): Use same style as react-ui Input.
4285
- classNames: "border border-input-separator rounded-xs px-2",
4286
- ...props,
4287
- initialValue: value3,
4288
- selectionEnd: true
4289
- }));
4290
- });
4291
- RefEditor.displayName = "RefEditor";
4292
- var insertMailbox = (view, head, mailbox) => {
4293
- const line = view.state.doc.lineAt(head);
4294
- const before = line.text.slice(0, head - line.from);
4295
- const lastComma = before.lastIndexOf(",");
4296
- const from = line.from + (lastComma === -1 ? 0 : lastComma + 1);
4297
- const insert = `${lastComma === -1 ? "" : " "}${mailbox}, `;
4298
- view.dispatch({
4299
- changes: {
4300
- from,
4301
- to: head,
4302
- insert
4303
- },
4304
- selection: {
4305
- anchor: from + insert.length,
4306
- head: from + insert.length
4307
- }
4308
- });
4309
- };
4310
-
4311
- // src/components/ViewEditor/ViewEditor.tsx
4312
- import { RegistryContext } from "@effect-atom/atom-react";
4313
- import * as Match2 from "effect/Match";
4314
- import * as Option9 from "effect/Option";
4315
- import * as Schema5 from "effect/Schema";
4316
- import React31, { forwardRef as forwardRef3, useCallback as useCallback22, useContext, useImperativeHandle, useMemo as useMemo17, useState as useState11 } from "react";
4317
- import { DXN as DXN4, EID as EID2, Entity as Entity2, Feed, Filter as Filter4, Format as Format7, Obj as Obj7, Query as Query2, QueryAST, Ref as Ref8, Type as Type7, View } from "@dxos/echo";
4318
- import { invariant as invariant4 } from "@dxos/invariant";
4319
- import { useObject, useQuery as useQuery3 } from "@dxos/react-client/echo";
4320
- import { Input as Input15, Message, ToggleIconButton as ToggleIconButton3, useTranslation as useTranslation13 } from "@dxos/react-ui";
4321
- import { QueryForm } from "@dxos/react-ui-components";
4322
- import { OrderedList as OrderedList3 } from "@dxos/react-ui-list";
4323
- import { ParentLabelAnnotation, ProjectionModel, VIEW_FIELD_LIMIT, createEchoChangeCallback, getTypeURIFromQuery } from "@dxos/schema";
4324
- import { mx as mx9 } from "@dxos/ui-theme";
4325
- import { translationKey as translationKey15 } from "#translations";
4326
- var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx";
4327
- var ViewEditor = /* @__PURE__ */ forwardRef3(({ classNames, type, view, mode = "schema", registry, db, readonly, showHeading = false, types, tags, onQueryChanged, onDelete }, forwardedRef) => {
4328
- const atomRegistry = useContext(RegistryContext);
4329
- const schemaReadonly = type == null || Type7.getDatabase(type) == null;
4330
- const { t } = useTranslation13(translationKey15);
4331
- const projectionModel = useMemo17(() => {
4332
- if (!type) {
4333
- return null;
4334
- }
4335
- const jsonSchema = type.jsonSchema;
4336
- const change = createEchoChangeCallback(view, Type7.getDatabase(type) != null ? type : void 0);
4337
- const model = new ProjectionModel({
4338
- registry: atomRegistry,
4339
- view,
4340
- baseSchema: jsonSchema,
4341
- change
4342
- });
4343
- return model;
4344
- }, [
4345
- atomRegistry,
4346
- type,
4347
- view
4348
- ]);
4349
- useImperativeHandle(forwardedRef, () => projectionModel, [
4350
- projectionModel
4351
- ]);
4352
- const queueTarget = Match2.value(view.query.ast).pipe(Match2.when({
4353
- type: "from"
4354
- }, ({ from }) => {
4355
- if (from._tag !== "scope") {
4356
- return void 0;
4357
- }
4358
- const feedScope = from.scopes.find((s) => s._tag === "feed");
4359
- return Option9.fromNullable(feedScope).pipe(Option9.map((s) => s.feedUri), Option9.getOrUndefined);
4360
- }), Match2.orElse(() => void 0));
4361
- const feeds = useQuery3(db, Filter4.type(Feed.Feed));
4362
- const targetRef = useMemo17(() => {
4363
- if (!queueTarget) {
4364
- return void 0;
4365
- }
4366
- const targetEid = EID2.tryParse(queueTarget);
4367
- const feed = feeds.find((feed2) => {
4368
- const feedEid = Feed.getQueueUri(feed2);
4369
- return feedEid != null && targetEid != null && EID2.equals(feedEid, targetEid);
4370
- });
4371
- return feed ? Ref8.fromURI(Entity2.getURI(feed)) : void 0;
4372
- }, [
4373
- queueTarget,
4374
- feeds
4375
- ]);
4376
- const viewSchema = useMemo17(() => {
4377
- const base = Schema5.Struct({
4378
- query: mode === "schema" ? Format7.URL.annotations({
4379
- title: "Record type"
4380
- }) : QueryAST.Query.annotations({
4381
- title: "Query"
4382
- })
4383
- });
4384
- if (mode === "tag") {
4385
- return Schema5.Struct({
4386
- ...base.fields,
4387
- // TODO(wittjosiah): Replace Type.Feed with Dataset.Dataset when Ref.Ref supports unions.
4388
- target: Ref8.Ref(Feed.Feed).pipe(Schema5.annotations({
4389
- title: "Target Feed"
4390
- }), ParentLabelAnnotation.set(true), Schema5.optional)
4391
- }).pipe(Schema5.mutable);
4392
- }
4393
- return base.pipe(Schema5.mutable);
4394
- }, [
4395
- mode
4396
- ]);
4397
- const viewValues = useMemo17(() => {
4398
- const typeUri = getTypeURIFromQuery(view.query.ast);
4399
- const typeDxn = typeUri ? DXN4.tryMake(typeUri) : void 0;
4400
- return {
4401
- query: mode === "schema" ? typeDxn ? DXN4.getName(typeDxn) : typeUri ?? "" : view.query.ast,
4402
- target: targetRef
4403
- };
4404
- }, [
4405
- mode,
4406
- view.query.ast,
4407
- targetRef
4408
- ]);
4409
- const fieldMap = useMemo17(() => mode === "tag" ? customFields({
4410
- types,
4411
- tags
4412
- }) : void 0, [
4413
- mode,
4414
- types,
4415
- tags
4416
- ]);
4417
- const handleUpdate = useCallback22((values) => {
4418
- const targetValue = values.target;
4419
- let queueDxn;
4420
- if (Ref8.isRef(targetValue)) {
4421
- const feedUri = targetValue.uri;
4422
- const feed = feeds.find((feed2) => Obj7.getURI(feed2) === feedUri);
4423
- if (feed) {
4424
- queueDxn = Feed.getQueueUri(feed);
4425
- }
4426
- }
4427
- const query = mode === "schema" ? Query2.select(Filter4.type(EID2.isEID(values.query) ? values.query : DXN4.make(values.query))).ast : JSON.parse(JSON.stringify(values.query));
4428
- onQueryChanged?.(query, queueDxn);
4429
- }, [
4430
- onQueryChanged,
4431
- mode,
4432
- feeds
4433
- ]);
4434
- const handleDelete = useCallback22((fieldId) => {
4435
- invariant4(!readonly, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 138, S: void 0, A: ["!readonly", ""] });
4436
- onDelete?.(fieldId);
4437
- }, [
4438
- onDelete,
4439
- readonly
4440
- ]);
4441
- return /* @__PURE__ */ React31.createElement("div", {
4442
- className: mx9(classNames)
4443
- }, /* @__PURE__ */ React31.createElement(Form.Root, {
4444
- schema: viewSchema,
4445
- values: viewValues,
4446
- fieldMap,
4447
- db,
4448
- onValuesChanged: handleUpdate
4449
- }, /* @__PURE__ */ React31.createElement(Form.Viewport, null, /* @__PURE__ */ React31.createElement(Form.Content, null, schemaReadonly && !readonly && /* @__PURE__ */ React31.createElement(Message.Root, {
4450
- valence: "info",
4451
- classNames: "my-form-padding"
4452
- }, /* @__PURE__ */ React31.createElement(Message.Title, null, t("system-schema.description"))), /* @__PURE__ */ React31.createElement(Form.FieldSet, null), type && /* @__PURE__ */ React31.createElement(FieldList, {
4453
- type,
4454
- view,
4455
- registry,
4456
- readonly,
4457
- showHeading,
4458
- onDelete: handleDelete
4459
- })))));
4460
- });
4461
- var customFields = ({ types, tags }) => ({
4462
- query: ({ type, readonly, label, getValue, onValueChange }) => {
4463
- const handleChange = useCallback22((query) => onValueChange(type, query.ast), [
4464
- onValueChange
4465
- ]);
4466
- return /* @__PURE__ */ React31.createElement(Input15.Root, null, /* @__PURE__ */ React31.createElement(FormFieldLabel, {
4467
- readonly,
4468
- label
4469
- }), /* @__PURE__ */ React31.createElement(QueryForm, {
4470
- initialQuery: getValue(),
4471
- types,
4472
- tags,
4473
- onChange: handleChange
4474
- }));
4475
- }
4476
- });
4477
- var FieldList = ({ type, view, registry, readonly, showHeading = false, onDelete }) => {
4478
- const atomRegistry = useContext(RegistryContext);
4479
- const schemaReadonly = Type7.getDatabase(type) == null;
4480
- const { t } = useTranslation13(translationKey15);
4481
- const [viewSnapshot] = useObject(view);
4482
- const projectionModel = useMemo17(() => {
4483
- const jsonSchema = type.jsonSchema;
4484
- const change = createEchoChangeCallback(view, Type7.getDatabase(type) != null ? type : void 0);
4485
- return new ProjectionModel({
4486
- registry: atomRegistry,
4487
- view,
4488
- baseSchema: jsonSchema,
4489
- change
4490
- });
4491
- }, [
4492
- atomRegistry,
4493
- type,
4494
- view
4495
- ]);
4496
- const [expandedField, setExpandedField] = useState11();
4497
- const handleAdd = useCallback22(() => {
4498
- invariant4(!readonly, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 204, S: void 0, A: ["!readonly", ""] });
4499
- const field = projectionModel.createFieldProjection();
4500
- setExpandedField(field.id);
4501
- }, [
4502
- projectionModel,
4503
- readonly
4504
- ]);
4505
- const handleDelete = useCallback22((fieldId) => {
4506
- invariant4(!readonly, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 212, S: void 0, A: ["!readonly", ""] });
4507
- if (fieldId === expandedField) {
4508
- setExpandedField(void 0);
4509
- }
4510
- onDelete?.(fieldId);
4511
- }, [
4512
- expandedField,
4513
- onDelete,
4514
- readonly
4515
- ]);
4516
- const handleMove = useCallback22((fromIndex, toIndex) => {
4517
- invariant4(!readonly, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 223, S: void 0, A: ["!readonly", ""] });
4518
- Obj7.update(view, (view2) => {
4519
- const fields = [
4520
- ...view2.projection.fields
4521
- ];
4522
- const [moved] = fields.splice(fromIndex, 1);
4523
- fields.splice(toIndex, 0, moved);
4524
- view2.projection.fields = fields;
4525
- });
4526
- }, [
4527
- view,
4528
- readonly
4529
- ]);
4530
- const handleClose = useCallback22(() => setExpandedField(void 0), []);
4531
- const handleHide = useCallback22((fieldId) => {
4532
- setExpandedField(void 0);
4533
- projectionModel.hideFieldProjection(fieldId);
4534
- }, [
4535
- projectionModel
4536
- ]);
4537
- const handleShow = useCallback22((property) => {
4538
- setExpandedField(void 0);
4539
- projectionModel.showFieldProjection(property);
4540
- }, [
4541
- projectionModel
4542
- ]);
4543
- if (!viewSnapshot) {
4544
- return null;
4545
- }
4546
- return /* @__PURE__ */ React31.createElement(OrderedList3.Root, {
4547
- items: viewSnapshot.projection.fields,
4548
- isItem: Schema5.is(View.FieldSchema),
4549
- getId: (field) => field.id,
4550
- onMove: readonly ? void 0 : handleMove,
4551
- readonly,
4552
- expandedId: expandedField,
4553
- onExpandedChange: setExpandedField
4554
- }, ({ items: fields }) => /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(FormFieldHeader, {
4555
- label: t("fields.label"),
4556
- readonly,
4557
- add: {
4558
- label: t("add-property-button.label"),
4559
- disabled: viewSnapshot.projection.fields.length >= VIEW_FIELD_LIMIT,
4560
- onClick: handleAdd
4561
- }
4562
- }), showHeading && /* @__PURE__ */ React31.createElement(FormFieldLabel, {
4563
- standalone: true,
4564
- classNames: "py-1",
4565
- label: t("field-path.label")
4566
- }), /* @__PURE__ */ React31.createElement(OrderedList3.Content, null, fields.map((field) => {
4567
- const hidden = field.visible === false;
4568
- return /* @__PURE__ */ React31.createElement(OrderedList3.DetailItem, {
4569
- key: field.id,
4570
- id: field.id,
4571
- item: field,
4572
- canDrag: !readonly && !schemaReadonly,
4573
- expandable: !readonly,
4574
- title: field.path,
4575
- titleClassNames: hidden ? "text-subdued" : void 0,
4576
- actions: /* @__PURE__ */ React31.createElement(ToggleIconButton3, {
4577
- iconOnly: true,
4578
- variant: "ghost",
4579
- active: hidden,
4580
- icon: "ph--eye--regular",
4581
- activeIcon: "ph--eye-closed--regular",
4582
- label: t(hidden ? "show-field.label" : "hide-field.label"),
4583
- "data-testid": hidden ? "show-field-button" : "hide-field-button",
4584
- disabled: readonly || !hidden && projectionModel.getFields().length <= 1,
4585
- onClick: () => hidden ? handleShow(field.path) : handleHide(field.id)
4586
- }),
4587
- trailing: !readonly && /* @__PURE__ */ React31.createElement(CompactIconButton, {
4588
- icon: "ph--x--regular",
4589
- label: t("delete-field.label"),
4590
- disabled: schemaReadonly || viewSnapshot.projection.fields.length <= 1,
4591
- onClick: () => handleDelete(field.id),
4592
- "data-testid": "field.delete"
4593
- })
4594
- }, /* @__PURE__ */ React31.createElement(FieldEditor, {
4595
- readonly: readonly || schemaReadonly,
4596
- registry,
4597
- projection: projectionModel,
4598
- field,
4599
- onSave: handleClose
4600
- }));
4601
- }))));
4602
- };
4603
- export {
4604
- ArrayField,
4605
- AsyncSelectField,
4606
- AutofillAnnotation,
4607
- AutofillAnnotationId,
4608
- AutofillField,
4609
- BooleanField,
4610
- ComboboxField,
4611
- CompactIconButton,
4612
- DateField,
4613
- EMAIL_REGEX,
4614
- FieldEditor,
4615
- Form,
4616
- FormContextProvider,
4617
- FormField,
4618
- FormFieldErrorBoundary,
4619
- FormFieldHeader,
4620
- FormFieldLabel,
4621
- FormFieldSet,
4622
- FormFieldSetContainer,
4623
- FormRow,
4624
- GeoPointField,
4625
- InlineRefField,
4626
- META_TAGS_KEY,
4627
- MarkdownField,
4628
- NAME_ADDR_REGEX,
4629
- NumberField,
4630
- ObjectForm,
4631
- ObjectPicker,
4632
- ObjectProperties,
4633
- ObjectTree,
4634
- OptionsLookupAnnotation,
4635
- OptionsLookupAnnotationId,
4636
- PasswordField,
4637
- REF_REGEX,
4638
- RefEditor,
4639
- RefField,
4640
- SelectField,
4641
- SelectOptionField,
4642
- TextAreaField,
4643
- TextField,
4644
- TupleField,
4645
- ViewEditor,
4646
- autofill,
4647
- cellClassesForFieldType,
4648
- createSelectField,
4649
- findRefOption,
4650
- formatForDisplay,
4651
- formatForEditing,
4652
- formatMailbox,
4653
- getDefaultValue,
4654
- getFormProperties,
4655
- getNumericConstraints,
4656
- getRefProps,
4657
- getRootFormProperties,
4658
- omitHiddenFormFields,
4659
- omitId,
4660
- optionsLookup,
4661
- parseValue,
4662
- pathNotUniqueError,
4663
- pickValues,
4664
- presentationFor,
4665
- refEditor,
4666
- refEditorRedecorate,
4667
- useAsyncFieldEffect,
4668
- useFormContext,
4669
- useFormFieldState,
4670
- useFormHandler,
4671
- useFormValues,
4672
- useKeyHandler,
4673
- withMetaTags
4674
- };
4675
- //# sourceMappingURL=index.mjs.map