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

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