@qoretechnologies/reqraft 0.10.53 → 0.10.54-pr.115.ge2d364c

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 (215) hide show
  1. package/.claude/CLAUDE.md +25 -0
  2. package/.tasks/INDEX.md +1 -0
  3. package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
  4. package/README.md +1 -1
  5. package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
  6. package/design/RENDER_EXPRESSION_TRANSPORT.md +18 -0
  7. package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
  8. package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
  9. package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
  10. package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
  11. package/dist/components/dpqlEditor/index.d.ts +4 -0
  12. package/dist/components/dpqlEditor/index.d.ts.map +1 -1
  13. package/dist/components/dpqlEditor/index.js +5 -1
  14. package/dist/components/dpqlEditor/index.js.map +1 -1
  15. package/dist/components/dpqlEditor/types.d.ts +29 -2
  16. package/dist/components/dpqlEditor/types.d.ts.map +1 -1
  17. package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
  18. package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
  19. package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
  20. package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
  21. package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
  22. package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
  23. package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
  24. package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
  25. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  26. package/dist/components/form/engine/CompactRow.js +186 -21
  27. package/dist/components/form/engine/CompactRow.js.map +1 -1
  28. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  29. package/dist/components/form/engine/CompactToolbar.js +5 -2
  30. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  31. package/dist/components/form/engine/FormEngine.d.ts +159 -11
  32. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  33. package/dist/components/form/engine/FormEngine.js +376 -75
  34. package/dist/components/form/engine/FormEngine.js.map +1 -1
  35. package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
  36. package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
  37. package/dist/components/form/engine/FormFieldsSkeleton.js +38 -0
  38. package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
  39. package/dist/components/form/engine/OptionFieldMessages.d.ts +17 -2
  40. package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
  41. package/dist/components/form/engine/OptionFieldMessages.js +61 -11
  42. package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
  43. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  44. package/dist/components/form/engine/_structuredData/SchemaDataView.js +7 -1
  45. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  46. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  47. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  48. package/dist/components/form/engine/readFirst.d.ts +42 -1
  49. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  50. package/dist/components/form/engine/readFirst.js +74 -7
  51. package/dist/components/form/engine/readFirst.js.map +1 -1
  52. package/dist/components/form/engine/rendererTypes.d.ts +13 -0
  53. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
  54. package/dist/components/form/engine/rendererTypes.js.map +1 -1
  55. package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
  56. package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
  57. package/dist/components/form/engine/rowMenuContext.js +103 -0
  58. package/dist/components/form/engine/rowMenuContext.js.map +1 -0
  59. package/dist/components/form/engine/typeChoices.d.ts +37 -0
  60. package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
  61. package/dist/components/form/engine/typeChoices.js +25 -0
  62. package/dist/components/form/engine/typeChoices.js.map +1 -0
  63. package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
  64. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  65. package/dist/components/form/expressions/ExpressionField.js +181 -33
  66. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  67. package/dist/components/form/expressions/argumentPresence.d.ts +97 -0
  68. package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
  69. package/dist/components/form/expressions/argumentPresence.js +203 -0
  70. package/dist/components/form/expressions/argumentPresence.js.map +1 -0
  71. package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
  72. package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
  73. package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
  74. package/dist/components/form/expressions/builder/index.d.ts +15 -2
  75. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  76. package/dist/components/form/expressions/builder/index.js +19 -57
  77. package/dist/components/form/expressions/builder/index.js.map +1 -1
  78. package/dist/components/form/expressions/dpqlMockLsp.d.ts +17 -0
  79. package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +1 -1
  80. package/dist/components/form/expressions/dpqlMockLsp.js +75 -9
  81. package/dist/components/form/expressions/dpqlMockLsp.js.map +1 -1
  82. package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
  83. package/dist/components/form/expressions/renderExpressionToText.js +94 -19
  84. package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
  85. package/dist/components/form/fields/Field.d.ts.map +1 -1
  86. package/dist/components/form/fields/Field.js +3 -1
  87. package/dist/components/form/fields/Field.js.map +1 -1
  88. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
  89. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  90. package/dist/components/form/fields/allowed-values/AllowedValues.js +14 -2
  91. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  92. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  93. package/dist/components/form/fields/auto/AutoFormField.js +13 -2
  94. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  95. package/dist/components/form/fields/rich-text/RichText.d.ts +11 -0
  96. package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
  97. package/dist/components/form/fields/rich-text/RichText.js +84 -5
  98. package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
  99. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
  100. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  101. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
  102. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  103. package/dist/components/form/fields/template/TemplateField.d.ts +31 -1
  104. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  105. package/dist/components/form/fields/template/TemplateField.js +507 -40
  106. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  107. package/dist/components/form/index.d.ts +1 -0
  108. package/dist/components/form/index.d.ts.map +1 -1
  109. package/dist/components/form/index.js +1 -0
  110. package/dist/components/form/index.js.map +1 -1
  111. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +1 -0
  112. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +1 -1
  113. package/dist/components/smartEditor/__fixtures__/mockLspServer.js +6 -1
  114. package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +1 -1
  115. package/dist/helpers/dpqlDetection.d.ts +58 -0
  116. package/dist/helpers/dpqlDetection.d.ts.map +1 -0
  117. package/dist/helpers/dpqlDetection.js +164 -0
  118. package/dist/helpers/dpqlDetection.js.map +1 -0
  119. package/dist/helpers/optionUiTypes.d.ts +1 -1
  120. package/dist/helpers/options.d.ts +37 -0
  121. package/dist/helpers/options.d.ts.map +1 -1
  122. package/dist/helpers/options.js +78 -1
  123. package/dist/helpers/options.js.map +1 -1
  124. package/dist/helpers/templateItems.d.ts +47 -0
  125. package/dist/helpers/templateItems.d.ts.map +1 -0
  126. package/dist/helpers/templateItems.js +88 -0
  127. package/dist/helpers/templateItems.js.map +1 -0
  128. package/dist/helpers/templates.d.ts +21 -0
  129. package/dist/helpers/templates.d.ts.map +1 -1
  130. package/dist/helpers/templates.js +49 -3
  131. package/dist/helpers/templates.js.map +1 -1
  132. package/dist/helpers/validations.d.ts.map +1 -1
  133. package/dist/helpers/validations.js +56 -9
  134. package/dist/helpers/validations.js.map +1 -1
  135. package/dist/hooks/useArgSchema.d.ts +2 -1
  136. package/dist/hooks/useArgSchema.d.ts.map +1 -1
  137. package/dist/hooks/useFetch/useFetch.d.ts +3 -1
  138. package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
  139. package/dist/hooks/useFetch/useFetch.js +47 -9
  140. package/dist/hooks/useFetch/useFetch.js.map +1 -1
  141. package/dist/hooks/useTemplates.d.ts +20 -0
  142. package/dist/hooks/useTemplates.d.ts.map +1 -1
  143. package/dist/hooks/useTemplates.js +34 -2
  144. package/dist/hooks/useTemplates.js.map +1 -1
  145. package/dist/index.d.ts +2 -1
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +5 -1
  148. package/dist/index.js.map +1 -1
  149. package/dist/stories/storyNetwork.d.ts +36 -0
  150. package/dist/stories/storyNetwork.d.ts.map +1 -0
  151. package/dist/stories/storyNetwork.js +82 -0
  152. package/dist/stories/storyNetwork.js.map +1 -0
  153. package/dist/utils/fetch.d.ts +151 -1
  154. package/dist/utils/fetch.d.ts.map +1 -1
  155. package/dist/utils/fetch.js +256 -44
  156. package/dist/utils/fetch.js.map +1 -1
  157. package/dist/utils/lspClient.d.ts +6 -0
  158. package/dist/utils/lspClient.d.ts.map +1 -1
  159. package/dist/utils/lspClient.js +62 -11
  160. package/dist/utils/lspClient.js.map +1 -1
  161. package/dist/utils/websocket.d.ts +6 -0
  162. package/dist/utils/websocket.d.ts.map +1 -1
  163. package/dist/utils/websocket.js +15 -7
  164. package/dist/utils/websocket.js.map +1 -1
  165. package/package.json +2 -2
  166. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +0 -2
  167. package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
  168. package/src/components/dpqlEditor/index.ts +4 -0
  169. package/src/components/dpqlEditor/types.ts +26 -2
  170. package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
  171. package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
  172. package/src/components/form/engine/CompactRow.tsx +231 -11
  173. package/src/components/form/engine/CompactToolbar.tsx +10 -3
  174. package/src/components/form/engine/FormEngine.stories.tsx +331 -30
  175. package/src/components/form/engine/FormEngine.tsx +504 -87
  176. package/src/components/form/engine/FormFieldsSkeleton.tsx +76 -0
  177. package/src/components/form/engine/OptionFieldMessages.tsx +89 -11
  178. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +11 -0
  179. package/src/components/form/engine/compactToolbarContext.ts +1 -1
  180. package/src/components/form/engine/readFirst.ts +86 -7
  181. package/src/components/form/engine/rendererTypes.ts +12 -0
  182. package/src/components/form/engine/rowMenuContext.ts +159 -0
  183. package/src/components/form/engine/typeChoices.ts +55 -0
  184. package/src/components/form/expressions/ExpressionField.stories.tsx +828 -31
  185. package/src/components/form/expressions/ExpressionField.tsx +234 -29
  186. package/src/components/form/expressions/argumentPresence.ts +200 -0
  187. package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
  188. package/src/components/form/expressions/builder/index.tsx +31 -75
  189. package/src/components/form/expressions/dpqlMockLsp.ts +81 -1
  190. package/src/components/form/expressions/renderExpressionToText.ts +106 -17
  191. package/src/components/form/fields/Field.tsx +3 -2
  192. package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -2
  193. package/src/components/form/fields/auto/AutoFormField.tsx +12 -2
  194. package/src/components/form/fields/rich-text/RichText.tsx +112 -19
  195. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
  196. package/src/components/form/fields/template/TemplateField.tsx +667 -95
  197. package/src/components/form/index.tsx +1 -0
  198. package/src/components/log/Log.stories.tsx +2 -1
  199. package/src/components/smartEditor/__fixtures__/mockLspServer.ts +6 -1
  200. package/src/helpers/dpqlDetection.ts +209 -0
  201. package/src/helpers/options.ts +70 -0
  202. package/src/helpers/templateItems.ts +81 -0
  203. package/src/helpers/templates.ts +51 -2
  204. package/src/helpers/validations.ts +59 -9
  205. package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
  206. package/src/hooks/useFetch/useFetch.tsx +53 -7
  207. package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
  208. package/src/hooks/useTemplates.ts +41 -0
  209. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
  210. package/src/index.tsx +14 -1
  211. package/src/stores/currentUser/currentUser.stories.tsx +2 -1
  212. package/src/stories/storyNetwork.ts +92 -0
  213. package/src/utils/fetch.ts +311 -21
  214. package/src/utils/lspClient.ts +68 -11
  215. package/src/utils/websocket.ts +20 -9
@@ -44,11 +44,20 @@ var __rest = (this && this.__rest) || function (s, e) {
44
44
  }
45
45
  return t;
46
46
  };
47
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
48
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
49
+ if (ar || !(i in from)) {
50
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
51
+ ar[i] = from[i];
52
+ }
53
+ }
54
+ return to.concat(ar || Array.prototype.slice.call(from));
55
+ };
47
56
  var __importDefault = (this && this.__importDefault) || function (mod) {
48
57
  return (mod && mod.__esModule) ? mod : { "default": mod };
49
58
  };
50
59
  Object.defineProperty(exports, "__esModule", { value: true });
51
- exports.TemplateField = exports.TemplateDropdownSelector = exports.CustomMenuItems = exports.ComponentMap = exports.TemplatesListProps = exports.isValueTemplate = exports.getTemplateValue = exports.getTemplateKey = void 0;
60
+ exports.TemplateField = exports.isTemplateAwareUiType = exports.BuiltInTemplateAwareUiTypes = exports.TemplateDropdownSelector = exports.CustomMenuItems = exports.ComponentMap = exports.TemplatesListProps = exports.isValueTemplate = exports.getTemplateValue = exports.getTemplateKey = void 0;
52
61
  var react_1 = require("react");
53
62
  var jsx_runtime_1 = require("react/jsx-runtime");
54
63
  // Verbatim port of qorus-ide `src/components/Field/template.tsx` (774 LOC,
@@ -65,7 +74,11 @@ var templates_1 = require("../../../../helpers/templates");
65
74
  Object.defineProperty(exports, "getTemplateKey", { enumerable: true, get: function () { return templates_1.getTemplateKey; } });
66
75
  Object.defineProperty(exports, "getTemplateValue", { enumerable: true, get: function () { return templates_1.getTemplateValue; } });
67
76
  Object.defineProperty(exports, "isValueTemplate", { enumerable: true, get: function () { return templates_1.isValueTemplate; } });
77
+ var dpqlDetection_1 = require("../../../../helpers/dpqlDetection");
78
+ var templateItems_1 = require("../../../../helpers/templateItems");
79
+ var markdownRendererContext_1 = require("../../../Description/markdownRendererContext");
68
80
  var validations_1 = require("../../../../helpers/validations");
81
+ var useDpqlProbe_1 = require("../../../dpqlEditor/useDpqlProbe");
69
82
  var useQorusTypes_1 = require("../../../../hooks/useQorusTypes");
70
83
  var useWhyDidYouUpdate_1 = require("../../../../hooks/useWhyDidYouUpdate");
71
84
  var builder_1 = require("../../expressions/builder");
@@ -81,6 +94,15 @@ var LongString_1 = __importDefault(require("../long-string/LongString"));
81
94
  var Number_1 = __importDefault(require("../number/Number"));
82
95
  var ReadOnlyTemplateTag_1 = require("./ReadOnlyTemplateTag");
83
96
  var RichText_1 = require("../rich-text/RichText");
97
+ var common_1 = require("../../../../helpers/common");
98
+ var rowMenuContext_1 = require("../../engine/rowMenuContext");
99
+ /**
100
+ * How long the author must pause before typed text is sent to the DPQL
101
+ * probe. Long enough that a probe is not sent for every prefix of what is
102
+ * being typed, short enough that the offer appears while they are still
103
+ * looking at the field.
104
+ */
105
+ var DPQL_DETECT_DEBOUNCE_MS = 400;
84
106
  exports.TemplatesListProps = {
85
107
  useTargetWidth: true,
86
108
  handler: 'focus',
@@ -165,18 +187,25 @@ var MenuActionsSection = (0, react_2.memo)(function (_a) {
165
187
  exports.CustomMenuItems = (0, react_2.memo)(function (_a) {
166
188
  var items = _a.items, closePopover = _a.closePopover, setIsTemplate = _a.setIsTemplate, setTemplateValue = _a.setTemplateValue, rest = __rest(_a, ["items", "closePopover", "setIsTemplate", "setTemplateValue"]);
167
189
  return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMenuSection, __assign({ label: 'Set Custom Value', isCollapsed: true, transparent: true, icon: 'Text' }, rest, { children: items.map(function (menuItem, index) {
168
- return 'isDivider' in menuItem ? ((0, jsx_runtime_1.jsx)(divider_1.default, __assign({}, menuItem), index)) : ((0, react_1.createElement)(reqore_1.ReqoreButton, __assign({}, rest, menuItem, { key: index, onClick: function (e) {
169
- var _a, _b;
170
- (_b = (_a = menuItem).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, e, function () {
171
- setIsTemplate(false);
172
- setTemplateValue(null);
173
- closePopover === null || closePopover === void 0 ? void 0 : closePopover();
174
- });
175
- } })));
190
+ return 'isDivider' in menuItem ?
191
+ (0, jsx_runtime_1.jsx)(divider_1.default, __assign({}, menuItem), index)
192
+ : (0, react_1.createElement)(reqore_1.ReqoreButton, __assign({}, rest, menuItem, { key: index, onClick: function (e) {
193
+ var _a, _b;
194
+ (_b = (_a = menuItem).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, e, function () {
195
+ setIsTemplate(false);
196
+ setTemplateValue(null);
197
+ closePopover === null || closePopover === void 0 ? void 0 : closePopover();
198
+ });
199
+ } }));
176
200
  }) })));
177
201
  });
178
202
  exports.TemplateDropdownSelector = (0, react_2.memo)(function (_a) {
179
203
  var onItemSelect = _a.onItemSelect, onRemoveClick = _a.onRemoveClick, items = _a.items, templates = _a.templates, allowCustomValues = _a.allowCustomValues, hasOnlyAllowedValues = _a.hasOnlyAllowedValues, value = _a.value, size = _a.size, rest = __rest(_a, ["onItemSelect", "onRemoveClick", "items", "templates", "allowCustomValues", "hasOnlyAllowedValues", "value", "size"]);
204
+ /* The LAST hop before Reqore, which is the only place a drawn description
205
+ may be made: `filteredTemplates` upstream is a `JSON.stringify` memo key,
206
+ and an element in it throws on its own circular owner. */
207
+ var renderMarkdown = (0, markdownRendererContext_1.useMarkdownRenderer)();
208
+ var shownItems = (0, react_2.useMemo)(function () { return (0, templateItems_1.renderTemplateItemDescriptions)((0, templateItems_1.templateItemsToShow)(items), renderMarkdown); }, [items, renderMarkdown]);
180
209
  // One resolver for every surface that names a reference — the picker chip
181
210
  // here, the read-only tag, and the compact row's expression summary. The
182
211
  // row and this editor must agree: a reference that reads as its path when
@@ -194,13 +223,33 @@ exports.TemplateDropdownSelector = (0, react_2.memo)(function (_a) {
194
223
  // SEAM (reqraft): the IDE resolves the template's app/action via
195
224
  // `useGetAppActionData` and renders the action's display name as a badge
196
225
  // here — the app catalogue is IDE-only, so the badge is dropped.
197
- return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { vertical: true, fluid: true, children: [hasOnlyAllowedValues && ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMessage, { intent: 'warning', size: 'small', opaque: false, children: "This field has pre-defined allowed values, make sure the template you select is compatible with those" })), (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { stack: true, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, __assign({ className: 'template-selector', customTheme: exports.TemplatesListProps.listCustomTheme, minimal: true, compact: true, onItemSelect: onItemSelect, items: items, label: label, leftIconProps: leftIconProps, caretPosition: 'right', filterable: true, size: size }, exports.TemplatesListProps)), allowCustomValues || value ? ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { customTheme: exports.TemplatesListProps.listCustomTheme, fixed: true, icon: 'CloseLine', tooltip: 'Remove template value', minimal: true, className: 'template-remove', compact: true, size: size, onClick: onRemoveClick })) : null] })] }));
226
+ return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { vertical: true, fluid: true, children: [hasOnlyAllowedValues && ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMessage, { intent: 'warning', size: 'small', opaque: false, children: "This field has pre-defined allowed values, make sure the template you select is compatible with those" })), (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { stack: true, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, __assign({ className: 'template-selector', customTheme: exports.TemplatesListProps.listCustomTheme, minimal: true, compact: true, onItemSelect: onItemSelect, items: shownItems, label: label, leftIconProps: leftIconProps, caretPosition: 'right', filterable: true, size: size }, exports.TemplatesListProps)), allowCustomValues || value ?
227
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { customTheme: exports.TemplatesListProps.listCustomTheme, fixed: true, icon: 'CloseLine', tooltip: 'Remove template value', minimal: true, className: 'template-remove', compact: true, size: size, onClick: onRemoveClick })
228
+ : null] })] }));
198
229
  });
199
- exports.TemplateField = (0, react_2.memo)(function (_a) {
230
+ /**
231
+ * `ui_type`s whose OWN editor renders templates as chips inline.
232
+ *
233
+ * For these the template SELECTOR must not take over: it replaces a control the
234
+ * author can type in with one they can only pick from. A Qorus test assertion's
235
+ * `Value` is the case this was written for — the IDE's own `auto.tsx` already
236
+ * renders it as template rich text (type freely, references become chips), and
237
+ * the same field reached through this form engine offered a dropdown and no way
238
+ * to type at all.
239
+ *
240
+ * `richtext` is reqraft's own; a consumer adds its types through the
241
+ * `templateAwareUiTypes` prop, exactly as it adds `rendererOnlyUiTypes`.
242
+ */
243
+ exports.BuiltInTemplateAwareUiTypes = ['richtext'];
244
+ var isTemplateAwareUiType = function (uiType, extra) {
245
+ return !!uiType && __spreadArray(__spreadArray([], exports.BuiltInTemplateAwareUiTypes, true), (extra !== null && extra !== void 0 ? extra : []), true).includes(uiType);
246
+ };
247
+ exports.isTemplateAwareUiType = isTemplateAwareUiType;
248
+ var TemplateFieldImpl = (0, react_2.memo)(function (_a) {
200
249
  var _b, _c, _d;
201
- var value = _a.value, name = _a.name, onChange = _a.onChange, _e = _a.component, Comp = _e === void 0 ? AutoFormField_1.AutoFormField : _e, templates = _a.templates, interfaceContext = _a.interfaceContext, // eslint-disable-line @typescript-eslint/no-unused-vars
250
+ var rowMenu = _a.rowMenu, value = _a.value, name = _a.name, onChange = _a.onChange, _e = _a.component, Comp = _e === void 0 ? AutoFormField_1.AutoFormField : _e, templates = _a.templates, interfaceContext = _a.interfaceContext, // eslint-disable-line @typescript-eslint/no-unused-vars
202
251
  _f = _a.allowTemplates, // eslint-disable-line @typescript-eslint/no-unused-vars
203
- allowTemplates = _f === void 0 ? true : _f, allowFunctions = _a.allowFunctions, allowTextExpressions = _a.allowTextExpressions, extraActions = _a.extraActions, _g = _a.allowCustomValues, allowCustomValues = _g === void 0 ? true : _g, _h = _a.filterTemplatesByType, filterTemplatesByType = _h === void 0 ? true : _h, filterTemplatesFunc = _a.filterTemplatesFunc, componentFromType = _a.componentFromType, isFunction = _a.isFunction, isDefaultFunction = _a.isDefaultFunction, isDefaultTemplate = _a.isDefaultTemplate, returnType = _a.returnType, level = _a.level, className = _a.className, menuItems = _a.menuItems, menuActions = _a.menuActions, reorder = _a.reorder, label = _a.label, rest = __rest(_a, ["value", "name", "onChange", "component", "templates", "interfaceContext", "allowTemplates", "allowFunctions", "allowTextExpressions", "extraActions", "allowCustomValues", "filterTemplatesByType", "filterTemplatesFunc", "componentFromType", "isFunction", "isDefaultFunction", "isDefaultTemplate", "returnType", "level", "className", "menuItems", "menuActions", "reorder", "label"]);
252
+ allowTemplates = _f === void 0 ? true : _f, allowFunctions = _a.allowFunctions, allowTextExpressions = _a.allowTextExpressions, extraActions = _a.extraActions, componentOverrides = _a.componentOverrides, _g = _a.allowCustomValues, allowCustomValues = _g === void 0 ? true : _g, _h = _a.filterTemplatesByType, filterTemplatesByType = _h === void 0 ? true : _h, filterTemplatesFunc = _a.filterTemplatesFunc, componentFromType = _a.componentFromType, isFunction = _a.isFunction, isDefaultFunction = _a.isDefaultFunction, isDefaultTemplate = _a.isDefaultTemplate, returnType = _a.returnType, level = _a.level, className = _a.className, menuItems = _a.menuItems, menuActions = _a.menuActions, reorder = _a.reorder, label = _a.label, rest = __rest(_a, ["rowMenu", "value", "name", "onChange", "component", "templates", "interfaceContext", "allowTemplates", "allowFunctions", "allowTextExpressions", "extraActions", "componentOverrides", "allowCustomValues", "filterTemplatesByType", "filterTemplatesFunc", "componentFromType", "isFunction", "isDefaultFunction", "isDefaultTemplate", "returnType", "level", "className", "menuItems", "menuActions", "reorder", "label"]);
204
253
  var qorusTypes = (0, useQorusTypes_1.useQorusTypes)();
205
254
  var functions = (0, useExpressions_1.useExpressions)({
206
255
  allow: !!allowFunctions,
@@ -208,6 +257,19 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
208
257
  extraExpressions: rest.expressions,
209
258
  });
210
259
  var type = rest.ui_type || rest.type || rest.defaultType;
260
+ /* The type an EXPRESSION must return, and is stored with, is a DATA type.
261
+ `type` above deliberately prefers `ui_type`, because that is what decides
262
+ which editor to render — but a ui_type says how a value is EDITED, not
263
+ what it is. The IDE's reference fields carry `ui_type: 'test-reference'`
264
+ (a rich-text editor that turns `$.` paths into chips), and feeding that
265
+ to the expression builder asked it for an expression RETURNING
266
+ `test-reference`: no expression returns one, so every choice was refused
267
+ with *"the expected return type is test-reference"*, and the ui_type was
268
+ then stored on the saved expression as its declared type.
269
+
270
+ The declared data type is the constraint; the ui_type is only a fallback
271
+ for a field that declares no type at all. */
272
+ var expressionDataType = rest.type || rest.defaultType || rest.ui_type;
211
273
  var filteredTemplates = (0, react_2.useMemo)(function () {
212
274
  if (!allowTemplates) {
213
275
  return undefined;
@@ -219,7 +281,20 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
219
281
  if (filterTemplatesFunc) {
220
282
  result = filterTemplatesFunc(result);
221
283
  }
222
- return result;
284
+ /* A lone category is opened, once, HERE — where this control resolves the
285
+ templates every one of its pickers then reads: the "Select Template"
286
+ dropdown, the in-editor `$` list, the numeric field's focus dropdown and
287
+ the expression builder's argument picker. Applying it per picker meant
288
+ each new one had to remember, and the ones that forgot made the author
289
+ click through a header naming the only category on offer to reach the
290
+ only values on offer.
291
+
292
+ LAST, after both filters. `filterTemplatesFunc` is given the grouped
293
+ shape on purpose — the Qog expression builder filters top-level items by
294
+ `metadata.dataRole`, which is a property of the CATEGORY — so flattening
295
+ before it would hand it values and quietly change what it keeps. This
296
+ only changes what is finally shown. */
297
+ return __assign(__assign({}, result), { items: (0, templateItems_1.templateItemsToShow)(result === null || result === void 0 ? void 0 : result.items) });
223
298
  }, [
224
299
  JSON.stringify(templates),
225
300
  type,
@@ -247,15 +322,49 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
247
322
  // templates on offer would otherwise open on an EMPTY picker, which is a
248
323
  // worse place to start than the type picker it replaced.
249
324
  var typeIsAnyLike = type === 'any' || type === 'auto';
250
- var isEmptyValue = value === undefined || value === null || value === '';
325
+ /* An EMPTY field, for the purpose of offering the template selector.
326
+
327
+ `null` is deliberately not empty. It is the DPQL null literal — a value
328
+ an author writes to say "this is nothing", and the only way to assert
329
+ that a call returned no value. Counted as emptiness here, an argument
330
+ holding it was treated as a field the author had just cleared: with a
331
+ type of `any` and templates on offer, the selector-restore effect below
332
+ flipped the field into template mode with a null template value, and
333
+ that reported the field as cleared — so writing `null` in the Text tab
334
+ and switching to Visual silently erased it, leaving `{type: "any"}` in
335
+ the draft and the message "Value for argument 1 ("any") is invalid:
336
+ Value is empty".
337
+
338
+ `undefined` is the cleared state; `null` is a value. Same rule as
339
+ `expressions/argumentPresence`, which states it canonically. */
340
+ var isEmptyValue = value === undefined || value === '';
251
341
  var hasTemplatesOnOffer = !!(0, lodash_1.size)(filteredTemplates === null || filteredTemplates === void 0 ? void 0 : filteredTemplates.items);
342
+ /* The field's own editor already renders templates inline, so the selector
343
+ is not merely unnecessary here — it is a downgrade, swapping a typable
344
+ control for a pick-only one. Derived rather than folded into the state
345
+ below so every `setIsTemplate` path keeps working untouched; they simply
346
+ stop having anything to say for these fields. */
347
+ var editorHandlesTemplates = !!allowTemplates && (0, exports.isTemplateAwareUiType)(type, rest.templateAwareUiTypes);
252
348
  var _j = (0, react_2.useState)((isDefaultTemplate ||
253
349
  (0, templates_1.isValueTemplate)(value) ||
254
350
  !allowCustomValues ||
255
351
  (typeIsAnyLike && isEmptyValue && hasTemplatesOnOffer)) &&
256
- allowTemplates), isTemplate = _j[0], setIsTemplate = _j[1];
352
+ allowTemplates), isTemplateState = _j[0], setIsTemplate = _j[1];
353
+ var isTemplate = editorHandlesTemplates ? false : isTemplateState;
257
354
  var _k = (0, react_2.useState)(!!isDefaultFunction && !!allowFunctions), internalIsFunction = _k[0], setInternalIsFunction = _k[1];
258
355
  var _l = (0, react_2.useState)(value), templateValue = _l[0], setTemplateValue = _l[1];
356
+ // Typed text that turned out to be an expression the author may accept.
357
+ var _m = (0, react_2.useState)(null), dpqlOffer = _m[0], setDpqlOffer = _m[1];
358
+ // The literal this field held before typed text switched it into
359
+ // expression mode — kept so the switch is undoable, and so the editor
360
+ // opens on the Text view the author was already writing in.
361
+ var _o = (0, react_2.useState)(null), expressionFromText = _o[0], setExpressionFromText = _o[1];
362
+ // Texts the author has said no to. Without this, dismissing an offer (or
363
+ // undoing a switch) would re-detect the same text on the next render and
364
+ // ask again forever.
365
+ var declinedTexts = (0, react_2.useRef)(new Set());
366
+ var detectTimer = (0, react_2.useRef)(null);
367
+ var probeDpql = (0, useDpqlProbe_1.useDpqlProbe)();
259
368
  var effectiveIsFunction = isFunction || internalIsFunction;
260
369
  (0, useWhyDidYouUpdate_1.useWhyDidYouUpdate)("Template field ".concat(name), __assign({ name: name, onChange: onChange, value: value }, rest));
261
370
  (0, react_2.useEffect)(function () {
@@ -263,6 +372,60 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
263
372
  setTemplateValue(value);
264
373
  }
265
374
  }, [JSON.stringify(value)]);
375
+ /* Clearing an untyped field returns it to the template selector, which is
376
+ where mounting it empty already lands.
377
+
378
+ The rule above that opens an `any`-like field on the template selector is
379
+ a `useState` INITIALISER, so it only ever ran on mount. Clearing a value
380
+ in place does not remount anything, so the field fell through to the TYPE
381
+ picker and demanded `string`/`int`/`hash` before it would let the author
382
+ name a value they had already captured -- the very question that rule
383
+ exists to stop asking. Reloading the page fixed it, which is the tell:
384
+ same field, same empty value, different landing, because only one of the
385
+ two paths ran the rule.
386
+
387
+ Watched as a TRANSITION from a value to no value, not as "the value is
388
+ empty": an author who picks `Set Custom Value` on an empty field is
389
+ asking for the type picker, and re-asserting the template view on every
390
+ render while the field sat empty would take it away again immediately. */
391
+ var hadValue = (0, react_2.useRef)(!isEmptyValue);
392
+ /* A clear is REMEMBERED rather than acted on in the same render.
393
+
394
+ While the field holds a value its resolved type is that VALUE's type — a
395
+ reference reads as `test-reference`, not `auto` — and clearing reverts it
396
+ to the schema's `auto` one render LATER than the value empties. Acting
397
+ only on the transition therefore ran while `typeIsAnyLike` was still
398
+ false and did nothing, and by the time the type settled the transition
399
+ had passed. Measured in the live IDE at the moment of the clear:
400
+ `{ wasCleared: true, typeIsAnyLike: false, type: "test-reference",
401
+ hasTemplatesOnOffer: true }`, then `typeIsAnyLike: true` on the very next
402
+ render with `wasCleared` already false. */
403
+ var restoreSelectorWhenSettled = (0, react_2.useRef)(false);
404
+ /** Set by the template control's own `×`, which is an author saying "not a
405
+ * template" — that clear must not be answered with the selector again. */
406
+ var suppressSelectorRestore = (0, react_2.useRef)(false);
407
+ (0, react_2.useEffect)(function () {
408
+ if (hadValue.current && isEmptyValue) {
409
+ restoreSelectorWhenSettled.current = !suppressSelectorRestore.current;
410
+ suppressSelectorRestore.current = false;
411
+ }
412
+ if (!isEmptyValue) {
413
+ // A value arrived: whatever the author did, they are not sitting on an
414
+ // empty field waiting to be offered the list.
415
+ restoreSelectorWhenSettled.current = false;
416
+ }
417
+ hadValue.current = !isEmptyValue;
418
+ if (restoreSelectorWhenSettled.current &&
419
+ isEmptyValue &&
420
+ typeIsAnyLike &&
421
+ hasTemplatesOnOffer &&
422
+ allowTemplates &&
423
+ !effectiveIsFunction) {
424
+ restoreSelectorWhenSettled.current = false;
425
+ setTemplateValue(null);
426
+ setIsTemplate(true);
427
+ }
428
+ }, [isEmptyValue, typeIsAnyLike, hasTemplatesOnOffer, allowTemplates, effectiveIsFunction]);
266
429
  (0, react_2.useEffect)(function () {
267
430
  if (allowCustomValues && isTemplate && value && !(0, templates_1.isValueTemplate)(value)) {
268
431
  setIsTemplate(false);
@@ -279,6 +442,16 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
279
442
  !(0, templates_1.isCompleteTemplateToken)(value)) {
280
443
  return;
281
444
  }
445
+ // A template reference with an OPERATOR on it (`$local:count + 1`) is
446
+ // an expression, not a template. The loose check above passes it —
447
+ // it starts with `$` and has a colon — so without this every such
448
+ // string was swallowed into template mode, where the arithmetic is
449
+ // dead text and no affordance can reach it. A token standing alone
450
+ // (`$local:count`, `$data:{1.field}`) has no operator and still
451
+ // flips, which is the case template mode is for.
452
+ if (!(0, templates_1.isCompleteTemplateToken)(value) && (0, dpqlDetection_1.mightBeDpqlExpression)(value)) {
453
+ return;
454
+ }
282
455
  setIsTemplate(true);
283
456
  setTemplateValue(value);
284
457
  }
@@ -299,7 +472,22 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
299
472
  // keep the string editor too; the chip-in-editor treatment arrives with
300
473
  // the rich-text string mode.
301
474
  var templateValueIsBracedToken = (0, templates_1.isBracedTemplateToken)(templateValue);
302
- var templateSupportsCustomValues = allowCustomValues && type === 'string' && !hasOnlyAllowedValues;
475
+ /* An UNTYPED field can be typed into as well as picked from.
476
+
477
+ Template mode has a typable editor whenever the field's type can hold
478
+ arbitrary text, and `any`/`auto` can — it is the type that has not been
479
+ narrowed yet, not a type that excludes text. Restricting this to
480
+ `string` meant an empty untyped field opened on template mode with no
481
+ editor to offer, so it fell through to the pick-only "Select Template"
482
+ dropdown: a control that can only choose from a list, on exactly the
483
+ fields where an author most often needs to write something the list
484
+ cannot hold — a deeper walk, a literal, or an expression.
485
+
486
+ Reported against a Qorus assertion's Expected Value, and it was never
487
+ specific to that field: every empty untyped field with templates on
488
+ offer got the same downgrade. The editor still offers the same templates
489
+ on focus, so nothing is lost by being able to type as well. */
490
+ var templateSupportsCustomValues = allowCustomValues && (type === 'string' || typeIsAnyLike) && !hasOnlyAllowedValues;
303
491
  var showTemplatesDropdown = allowTemplates && (!allowCustomValues || (isTemplate && !templateSupportsCustomValues));
304
492
  var hasOnlyExpressions = !allowCustomValues && !allowTemplates && allowFunctions;
305
493
  // True when some input control renders besides the ⋮ menu. When nothing
@@ -330,11 +518,20 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
330
518
  // If the field has allowed values and supports templates, we do not want to overwrite the field type with the template type
331
519
  function (item) {
332
520
  // If the template type is richtext, we need to wrap the template value in the richtext template format
333
- var value = item.badge === 'richtext'
334
- ? [
521
+ var value = item.badge === 'richtext' ?
522
+ [
335
523
  {
336
524
  type: 'paragraph',
525
+ /* The empty text nodes on either side are load-bearing. A
526
+ chip is an inline VOID: it holds no text of its own, so the
527
+ only places a cursor can go are the text nodes AROUND it.
528
+ Slate treats that as an invariant and repairs it during
529
+ normalisation, which runs on edits but NOT on a document
530
+ handed in whole as a controlled value — which is what this
531
+ is. Without them the picked template renders as a chip that
532
+ cannot be typed after, so it can never be extended by hand. */
337
533
  children: [
534
+ { text: '' },
338
535
  {
339
536
  children: [{ text: '' }],
340
537
  label: item.label,
@@ -342,6 +539,7 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
342
539
  value: item.value,
343
540
  metadata: item.metadata,
344
541
  },
542
+ { text: '' },
345
543
  ],
346
544
  },
347
545
  ]
@@ -352,7 +550,15 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
352
550
  // `SaveValueButton` in the controls menu — the saved-values storage is
353
551
  // IDE-only, so the menu item is dropped (`allowSaving` is inert).
354
552
  var handleRemoveTemplateClick = (0, react_2.useCallback)(function () {
553
+ /* This is the `×` ON the template control, and it means "I do not want a
554
+ template here" — on a field whose menu offers no `Set Custom Value` it
555
+ is the ONLY way to reach a literal. So it still drops to the custom
556
+ value editor, and it tells the empty-field rule below to keep its hands
557
+ off this particular clear: that rule watches the value going away, and
558
+ this handler clears the value too, so without the flag it would send
559
+ the author straight back to the selector they just dismissed. */
355
560
  if (allowCustomValues) {
561
+ suppressSelectorRestore.current = true;
356
562
  setIsTemplate(false);
357
563
  }
358
564
  setTemplateValue(null);
@@ -389,22 +595,229 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
389
595
  var handleExpressionChange = (0, react_2.useCallback)(function (expressionValue, remove) {
390
596
  if (remove) {
391
597
  setInternalIsFunction(false);
598
+ // The expression is gone, so there is no longer a switch to undo.
599
+ setExpressionFromText(null);
600
+ }
601
+ onChange(name, expressionValue === null || expressionValue === void 0 ? void 0 : expressionValue.value, expressionDataType, !remove);
602
+ }, [name, onChange, expressionDataType, value]);
603
+ // ─── Text typed into a plain field that is really a DPQL expression ───
604
+ //
605
+ // A field that accepts expressions still gets DPQL TYPED into it, in the
606
+ // ordinary editor, by an author who never opened the expression view.
607
+ // What happens then depends on the field's own type, and the asymmetry is
608
+ // deliberate: text that could stand as a literal here is only ever
609
+ // OFFERED, because silently rewriting the string `a + b` into a
610
+ // concatenation would destroy a value the author meant. Text that could
611
+ // NOT be a literal here was already an error the moment it was typed, so
612
+ // switching costs nothing and explains the error.
613
+ //
614
+ // The server decides whether the text is an expression at all — see
615
+ // `helpers/dpqlDetection` for why a successful parse alone means nothing.
616
+ var validationField = (0, react_2.useMemo)(function () { return ({
617
+ validation_regex: rest.validation_regex,
618
+ has_to_be_valid_identifier: rest.has_to_be_valid_identifier,
619
+ has_to_have_value: rest.has_to_have_value,
620
+ rules: rest.rules,
621
+ arg_schema: rest.arg_schema,
622
+ }); }, [
623
+ rest.validation_regex,
624
+ rest.has_to_be_valid_identifier,
625
+ rest.has_to_have_value,
626
+ JSON.stringify(rest.rules),
627
+ JSON.stringify(rest.arg_schema),
628
+ ]);
629
+ var enterExpressionFromText = (0, react_2.useCallback)(function (text, expression) {
630
+ var _a;
631
+ setDpqlOffer(null);
632
+ setExpressionFromText(text);
633
+ setInternalIsFunction(true);
634
+ setIsTemplate(false);
635
+ setTemplateValue(null);
636
+ // `dpql/parse` answers with the field-ready envelope
637
+ // (`{is_expression, value}`); this field stores the inner AST and
638
+ // signals the flag through `onChange`'s fourth argument, exactly as
639
+ // `handleExpressionChange` does.
640
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, (_a = expression === null || expression === void 0 ? void 0 : expression.value) !== null && _a !== void 0 ? _a : expression, expressionDataType, true);
641
+ }, [name, onChange, expressionDataType]);
642
+ var undoExpressionFromText = (0, react_2.useCallback)(function () {
643
+ if (expressionFromText === null) {
644
+ return;
645
+ }
646
+ declinedTexts.current.add(expressionFromText);
647
+ setInternalIsFunction(false);
648
+ setExpressionFromText(null);
649
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, expressionFromText, type, false);
650
+ }, [expressionFromText, name, onChange, type]);
651
+ var declineDpqlOffer = (0, react_2.useCallback)(function () {
652
+ if (dpqlOffer) {
653
+ declinedTexts.current.add(dpqlOffer.text);
654
+ }
655
+ setDpqlOffer(null);
656
+ }, [dpqlOffer]);
657
+ var acceptDpqlOffer = (0, react_2.useCallback)(function () {
658
+ if (dpqlOffer) {
659
+ enterExpressionFromText(dpqlOffer.text, dpqlOffer.expression);
660
+ }
661
+ }, [dpqlOffer, enterExpressionFromText]);
662
+ // Held in a ref so the debounce below does not depend on it. `onChange`
663
+ // comes from the host form and can be a fresh function on any render;
664
+ // with the callback in the effect's deps, a re-render caused by a
665
+ // DIFFERENT field would restart this field's timer, and a form the
666
+ // author is typing in re-renders constantly.
667
+ var enterExpressionRef = (0, react_2.useRef)(enterExpressionFromText);
668
+ (0, react_2.useEffect)(function () {
669
+ enterExpressionRef.current = enterExpressionFromText;
670
+ }, [enterExpressionFromText]);
671
+ // Detection only runs on a field that could hold an expression and is
672
+ // currently showing a plain editor — never on a read-only field, a
673
+ // fixed-value list, or one already in template or expression mode.
674
+ var canDetectDpql = !!allowFunctions &&
675
+ !!allowTextExpressions &&
676
+ !effectiveIsFunction &&
677
+ !isTemplate &&
678
+ !hasOnlyAllowedValues &&
679
+ !rest.readonly &&
680
+ !rest.readOnly &&
681
+ !rest.disabled;
682
+ (0, react_2.useEffect)(function () {
683
+ if (detectTimer.current) {
684
+ clearTimeout(detectTimer.current);
685
+ detectTimer.current = null;
392
686
  }
393
- onChange(name, expressionValue === null || expressionValue === void 0 ? void 0 : expressionValue.value, type, !remove);
394
- }, [name, onChange, type, value]);
687
+ /* A richtext field is still text. It hands its value back as a Slate
688
+ DOCUMENT rather than a flattened string — deliberately, so a chosen
689
+ reference cannot fuse with text typed beside it — and reading only
690
+ `typeof value === 'string'` here silently switched detection off for
691
+ every one of them the moment the field changed shape. A test
692
+ assertion's Value is exactly that field, and `1 + 2` typed into it
693
+ stopped being offered as an expression.
694
+
695
+ `richtextToString` flattens a tag to its raw value, so a document
696
+ holding only a chosen reference reads as `$.result` — no operator, so
697
+ `mightBeDpqlExpression` rejects it below and the server is never
698
+ asked. */
699
+ var text = typeof value === 'string' ? value
700
+ : Array.isArray(value) ? (0, common_1.richtextToString)(value)
701
+ : undefined;
702
+ if (!canDetectDpql ||
703
+ !text ||
704
+ declinedTexts.current.has(text) ||
705
+ !(0, dpqlDetection_1.mightBeDpqlExpression)(text)) {
706
+ setDpqlOffer(null);
707
+ return undefined;
708
+ }
709
+ var cancelled = false;
710
+ detectTimer.current = setTimeout(function () {
711
+ detectTimer.current = null;
712
+ void probeDpql(text)
713
+ .then(function (parsed) {
714
+ if (cancelled) {
715
+ return;
716
+ }
717
+ var outcome = (0, dpqlDetection_1.classifyTypedText)({ text: text, type: type, field: validationField, parsed: parsed });
718
+ if (outcome === 'switch') {
719
+ enterExpressionRef.current(text, parsed.expression);
720
+ }
721
+ else if (outcome === 'offer') {
722
+ setDpqlOffer({ text: text, expression: parsed.expression });
723
+ }
724
+ else {
725
+ setDpqlOffer(null);
726
+ }
727
+ })
728
+ .catch(function () {
729
+ // Detection is an offer of help, never a reason for a field to
730
+ // break: an unreachable instance or a classifier that threw leaves
731
+ // the author's text exactly where they put it.
732
+ if (!cancelled) {
733
+ setDpqlOffer(null);
734
+ }
735
+ });
736
+ }, DPQL_DETECT_DEBOUNCE_MS);
737
+ return function () {
738
+ cancelled = true;
739
+ if (detectTimer.current) {
740
+ clearTimeout(detectTimer.current);
741
+ detectTimer.current = null;
742
+ }
743
+ };
744
+ }, [value, canDetectDpql, type, validationField, probeDpql]);
745
+ var canOfferExpression = allowFunctions && !hasOnlyAllowedValues && !rest.readonly && !internalIsFunction;
746
+ var canOfferTemplate = showTemplateToggle && !isTemplate;
747
+ var publishedItems = (0, react_2.useMemo)(function () { return __spreadArray(__spreadArray(__spreadArray([], (canOfferExpression && !functions.loading
748
+ ? [
749
+ {
750
+ label: 'Use Expression',
751
+ icon: 'Functions',
752
+ tooltip: 'Run a function on this value',
753
+ onClick: handleSelectFunctionChange,
754
+ },
755
+ ]
756
+ : []), true), (canOfferTemplate
757
+ ? [
758
+ {
759
+ label: 'Use Template',
760
+ icon: 'MoneyDollarCircleLine',
761
+ tooltip: 'Use a template',
762
+ onClick: handleTemplateToggleClick,
763
+ },
764
+ ]
765
+ : []), true), (menuItems !== null && menuItems !== void 0 ? menuItems : [])
766
+ .filter(function (item) { return !('isDivider' in item); })
767
+ .map(function (item) { return ({
768
+ label: item.label,
769
+ description: item.description,
770
+ onClick: function () {
771
+ var _a;
772
+ return (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, undefined, function () {
773
+ setIsTemplate(false);
774
+ setTemplateValue(null);
775
+ });
776
+ },
777
+ }); }), true); }, [
778
+ canOfferExpression,
779
+ functions.loading,
780
+ canOfferTemplate,
781
+ menuItems,
782
+ handleSelectFunctionChange,
783
+ handleTemplateToggleClick,
784
+ ]);
785
+ /* Keyed on WHICH items are offered, never on the items: they carry
786
+ handlers, so they are a new array every render and a row comparing them
787
+ by value would loop. */
788
+ var publishedKey = [
789
+ canOfferExpression && !functions.loading ? 'expression' : '',
790
+ canOfferTemplate ? 'template' : '',
791
+ "custom:".concat((menuItems !== null && menuItems !== void 0 ? menuItems : []).map(function (item) { var _a; return String((_a = item.label) !== null && _a !== void 0 ? _a : ''); }).join('|')),
792
+ ].join(',');
793
+ (0, rowMenuContext_1.useRowMenuPublisher)(rowMenu, publishedKey, publishedItems);
395
794
  var renderControls = (0, react_2.useCallback)(function () {
396
795
  var showFunctionsDropdown = allowFunctions && !hasOnlyAllowedValues && !rest.readonly && !internalIsFunction;
397
796
  var showTemplatesButton = showTemplateToggle && !isTemplate;
398
797
  // The "Set value" label promises a way to set one — reorder rows alone don't.
399
798
  var hasValueRows = showFunctionsDropdown || showTemplatesButton || (0, lodash_1.size)(menuItems) > 0;
799
+ /* ONE menu per control. Where this field sits in a form ROW, the row
800
+ already renders a ⋮ of its own, and drawing a second one beside it
801
+ put two menus of slightly different widths on the same control —
802
+ with the type choices an `any` field offers hidden in the narrower
803
+ one. The items are published to the row instead (see
804
+ `rowMenuContext`); outside a row there is nothing to publish into,
805
+ so the field goes on drawing its own. */
806
+ if (rowMenu) {
807
+ return null;
808
+ }
400
809
  if (hasOnlyExpressions) {
401
- return showFunctionsDropdown ? (functions.loading ? ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })) : ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, minimal: true, label: 'Create New Expression', className: 'function-selector', icon: 'Functions', tooltip: 'This field only accepts expressions', onClick: function () {
402
- setIsTemplate(false);
403
- setTemplateValue(null);
404
- onChange === null || onChange === void 0 ? void 0 : onChange(name, {
405
- args: [],
406
- }, undefined, true);
407
- } }))) : null;
810
+ return (showFunctionsDropdown ?
811
+ functions.loading ?
812
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })
813
+ : (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, minimal: true, label: 'Create New Expression', className: 'function-selector', icon: 'Functions', tooltip: 'This field only accepts expressions', onClick: function () {
814
+ setIsTemplate(false);
815
+ setTemplateValue(null);
816
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, {
817
+ args: [],
818
+ }, undefined, true);
819
+ } })
820
+ : null);
408
821
  }
409
822
  if (hasValueRows || (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0) {
410
823
  return ((0, jsx_runtime_1.jsx)(reqore_1.ReqorePopover, { component: reqore_1.ReqoreButton, closeOnTargetClick: true, closeOnInsideClick: false, isReqoreComponent: true, noWrapper: true, noArrow: true, placement: 'bottom-end', componentProps: {
@@ -414,10 +827,18 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
414
827
  transparent: true,
415
828
  size: rest.size,
416
829
  fixed: true,
417
- // Centre the trailing menu in its flex line so it lines up with
418
- // sibling action buttons (reqore alignSelf; replaces a reqraft
419
- // `align-self !important` override of this button).
420
- alignSelf: 'center',
830
+ /* Centre the trailing menu in its flex line so it lines up
831
+ with sibling action buttons (reqore alignSelf; replaces a
832
+ reqraft `align-self !important` override of this button).
833
+
834
+ Centring is only right beside a ONE-LINE editor. The
835
+ expression shell is a toolbar with an editor, a type message
836
+ and a preview stacked under it, so centring put this menu
837
+ somewhere down the side of that block — level with nothing,
838
+ over the editor, and a long way from the Undo it belongs
839
+ beside. There it goes to the top, which is where the shell's
840
+ own toolbar is. */
841
+ alignSelf: effectiveIsFunction ? 'flex-start' : 'center',
421
842
  label: hasInputAffordance || !hasValueRows ? undefined : 'Set value',
422
843
  style: hasInputAffordance ?
423
844
  {
@@ -437,7 +858,17 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
437
858
  },
438
859
  },
439
860
  },
440
- }, handler: 'click', content: (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreMenu, { size: rest.size, maxHeight: '400px', style: { overflow: 'auto' }, children: [showFunctionsDropdown ? (functions.loading ? ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })) : ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, transparent: true, label: 'Use Expression', className: 'function-selector', icon: 'Functions', tooltip: 'Run a function on this value', onClick: handleSelectFunctionChange }))) : null, showTemplatesButton ? ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreButton, { transparent: true, icon: 'MoneyDollarCircleLine', className: 'template-toggle', tooltip: 'Use a template', compact: true, size: rest.size, onClick: handleTemplateToggleClick, children: [' ', "Use Template", ' '] })) : null, (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0 ? ((0, jsx_runtime_1.jsx)(MenuActionsSection, { actions: menuActions, size: rest.size })) : null, (0, lodash_1.size)(menuItems) > 0 ? ((0, jsx_runtime_1.jsx)(exports.CustomMenuItems, { items: menuItems, setIsTemplate: setIsTemplate, setTemplateValue: setTemplateValue })) : null] }) }));
861
+ }, handler: 'click', content: (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreMenu, { size: rest.size, maxHeight: '400px', style: { overflow: 'auto' }, children: [showFunctionsDropdown ?
862
+ functions.loading ?
863
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })
864
+ : (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, transparent: true, label: 'Use Expression', className: 'function-selector', icon: 'Functions', tooltip: 'Run a function on this value', onClick: handleSelectFunctionChange })
865
+ : null, showTemplatesButton ?
866
+ (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreButton, { transparent: true, icon: 'MoneyDollarCircleLine', className: 'template-toggle', tooltip: 'Use a template', compact: true, size: rest.size, onClick: handleTemplateToggleClick, children: [' ', "Use Template", ' '] })
867
+ : null, (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0 ?
868
+ (0, jsx_runtime_1.jsx)(MenuActionsSection, { actions: menuActions, size: rest.size })
869
+ : null, (0, lodash_1.size)(menuItems) > 0 ?
870
+ (0, jsx_runtime_1.jsx)(exports.CustomMenuItems, { items: menuItems, setIsTemplate: setIsTemplate, setTemplateValue: setTemplateValue })
871
+ : null] }) }));
441
872
  }
442
873
  return null;
443
874
  }, [
@@ -456,15 +887,18 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
456
887
  menuItems,
457
888
  menuActions,
458
889
  internalIsFunction,
890
+ effectiveIsFunction,
459
891
  hasInputAffordance,
892
+ // the row decides whether this field draws a menu at all
893
+ rowMenu,
460
894
  ]);
461
895
  // When the type is a list, and it has an element type - that element type is different
462
896
  // from the field type, so we need to send down the full templates object
463
897
  // and the actual rendered field will filter it's own templates
464
898
  // This is a special case only for lists with element types
465
899
  var componentTemplates = (0, react_2.useMemo)(function () {
466
- return type === 'list' && (rest.ui_element_type || rest.element_type)
467
- ? templates
900
+ return type === 'list' && (rest.ui_element_type || rest.element_type) ?
901
+ templates
468
902
  : __assign(__assign({}, filteredTemplates), exports.TemplatesListProps);
469
903
  }, [JSON.stringify(filteredTemplates), rest.ui_element_type, rest.element_type, type]);
470
904
  if (effectiveIsFunction && !hasOnlyAllowedValues) {
@@ -476,12 +910,26 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
476
910
  return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreErrorBoundary, { children: (0, jsx_runtime_1.jsx)(ExpressionField_1.ExpressionField, { value: {
477
911
  is_expression: true,
478
912
  value: value,
479
- }, localTemplates: templates, type: type, returnType: (returnType || type), onChange: handleExpressionChange, readOnly: rest.readOnly || rest.disabled, expressions: rest.expressions, expressionsUrl: rest.expressions_url, serverHandled: rest.server_expression_handling, extraActions: extraActions, size: rest.size, reorder: reorder }) }), renderControls()] }));
913
+ },
914
+ // The host's per-ui_type editors reach this field through the
915
+ // rest-spread; the expression shell needs them explicitly or the
916
+ // builder's operands render "Unknown type!" for any consumer
917
+ // ui_type (an assertion's `test-reference` Value, for one).
918
+ componentOverrides: componentOverrides, localTemplates: templates, type: type, returnType: (returnType || expressionDataType), onChange: handleExpressionChange, readOnly: rest.readOnly || rest.disabled, expressions: rest.expressions, expressionsUrl: rest.expressions_url, serverHandled: rest.server_expression_handling, extraActions: extraActions, size: rest.size,
919
+ // An author who typed the expression as text is already
920
+ // writing in that language — dropping them into the visual
921
+ // builder would make them find their own sentence again.
922
+ defaultMode: expressionFromText !== null ? 'text' : 'visual', reorder: reorder }) }), expressionFromText !== null ?
923
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { fixed: true, compact: true, minimal: true, icon: 'ArrowGoBackLine',
924
+ // No `intent` with `minimal`: a Reqore intent is a FILL, and
925
+ // `muted` as TEXT is 1.3:1 on the app surface.
926
+ size: rest.size, className: 'dpql-detected-undo', tooltip: "Keep \"".concat(expressionFromText, "\" as plain text instead"), onClick: undoExpressionFromText, children: "Undo" })
927
+ : null, renderControls()] }));
480
928
  }
481
929
  return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreErrorBoundary, { children: (0, jsx_runtime_1.jsx)(builder_1.ExpressionBuilder, { value: {
482
930
  is_expression: true,
483
931
  value: value,
484
- }, localTemplates: templates, level: level, type: type, returnType: (returnType || type), onChange: handleExpressionChange, readOnly: rest.readOnly || rest.disabled, expressions: rest.expressions, expressionsUrl: rest.expressions_url, serverHandled: rest.server_expression_handling, extraActions: extraActions, reorder: reorder }) }), renderControls()] }));
932
+ }, componentOverrides: componentOverrides, localTemplates: templates, level: level, type: type, returnType: (returnType || expressionDataType), onChange: handleExpressionChange, readOnly: rest.readOnly || rest.disabled, expressions: rest.expressions, expressionsUrl: rest.expressions_url, serverHandled: rest.server_expression_handling, extraActions: extraActions, reorder: reorder }) }), renderControls()] }));
485
933
  }
486
934
  if (rest.disabled) {
487
935
  if (isTemplate) {
@@ -491,9 +939,28 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
491
939
  // IDE's intent scheme. Shared with the compact read-first row.
492
940
  return (0, jsx_runtime_1.jsx)(ReadOnlyTemplateTag_1.ReadOnlyTemplateTag, { value: templateValue, templates: templates, size: rest.size });
493
941
  }
494
- return (0, jsx_runtime_1.jsx)(Comp, __assign({ value: value, onChange: onChange, name: name }, rest));
942
+ return ((0, jsx_runtime_1.jsx)(Comp, __assign({ value: value, onChange: onChange, name: name }, rest, { componentOverrides: componentOverrides })));
495
943
  }
496
- return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, __assign({ fluid: rest.fluid, fixed: rest.fixed, size: rest.size }, rest, { stack: false, verticalAlign: 'flex-start', children: [!isTemplate && allowCustomValues ? ((0, jsx_runtime_1.jsx)(Component, __assign({ value: value, allowTemplates: allowTemplates, onChange: onChange, name: name, level: level }, rest, componentTypeProp, { "aria-label": fieldAriaLabel, className: "".concat(className, " template-selector"), templates: componentTemplates }))) : null, isTemplate && templateSupportsCustomValues && !templateValueIsBracedToken ? ((0, jsx_runtime_1.jsx)(LongStringField, __assign({ className: 'template-selector', type: 'string', name: 'templateVal', level: level, value: templateValue, templates: __assign(__assign({}, filteredTemplates), exports.TemplatesListProps), onChange: handleTemplateFieldChange }, rest, { "aria-label": fieldAriaLabel }))) : null, showTemplatesDropdown ||
497
- (isTemplate && templateSupportsCustomValues && templateValueIsBracedToken) ? ((0, jsx_runtime_1.jsx)(exports.TemplateDropdownSelector, { allowCustomValues: allowCustomValues, templates: templates, value: templateValue, items: filteredTemplates === null || filteredTemplates === void 0 ? void 0 : filteredTemplates.items, onItemSelect: handleSelectTemplateFromList, onRemoveClick: handleRemoveTemplateClick, size: rest.size, label: label, hasOnlyAllowedValues: hasOnlyAllowedValues })) : null, renderControls()] })));
944
+ return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, __assign({ fluid: rest.fluid, fixed: rest.fixed, size: rest.size }, rest, { stack: false, verticalAlign: 'flex-start', children: [!isTemplate && allowCustomValues ?
945
+ (0, jsx_runtime_1.jsx)(Component, __assign({ value: value, allowTemplates: allowTemplates, onChange: onChange, name: name, level: level }, rest, { componentOverrides: componentOverrides }, componentTypeProp, { "aria-label": fieldAriaLabel, className: "".concat(className, " template-selector"), templates: componentTemplates }))
946
+ : null, isTemplate && templateSupportsCustomValues && !templateValueIsBracedToken ?
947
+ (0, jsx_runtime_1.jsx)(LongStringField, __assign({ className: 'template-selector', type: 'string', name: 'templateVal', level: level, value: templateValue, templates: __assign(__assign({}, filteredTemplates), exports.TemplatesListProps), onChange: handleTemplateFieldChange }, rest, { "aria-label": fieldAriaLabel }))
948
+ : null, dpqlOffer ?
949
+ (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { fixed: true, stack: true, size: rest.size, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, icon: 'Functions', intent: 'info', className: 'dpql-detected-offer', tooltip: "\"".concat(dpqlOffer.text, "\" reads as an expression. It is also valid text here, so nothing has been changed \u2014 use it as an expression?"), onClick: acceptDpqlOffer, children: "Use as expression" }), (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { compact: true, icon: 'CloseLine', intent: 'info', className: 'dpql-detected-dismiss', tooltip: 'Keep it as plain text', onClick: declineDpqlOffer })] })
950
+ : null, (showTemplatesDropdown ||
951
+ (isTemplate && templateSupportsCustomValues && templateValueIsBracedToken)) ?
952
+ (0, jsx_runtime_1.jsx)(exports.TemplateDropdownSelector, { allowCustomValues: allowCustomValues, templates: templates, value: templateValue, items: filteredTemplates === null || filteredTemplates === void 0 ? void 0 : filteredTemplates.items, onItemSelect: handleSelectTemplateFromList, onRemoveClick: handleRemoveTemplateClick, size: rest.size, label: label, hasOnlyAllowedValues: hasOnlyAllowedValues })
953
+ : null, renderControls()] })));
954
+ });
955
+ /**
956
+ * The row channel belongs to the row's OWN editor: this field. It is read here
957
+ * and hidden from everything this field renders, so a field nested inside it —
958
+ * an expression operand, a list item — keeps drawing its own menu instead of
959
+ * publishing actions into a row menu that cannot say which value they act on.
960
+ * `undefined` outside a row, where this field draws its own menu too.
961
+ */
962
+ exports.TemplateField = (0, react_2.memo)(function (props) {
963
+ var rowMenu = (0, rowMenuContext_1.useRowMenu)();
964
+ return ((0, jsx_runtime_1.jsx)(rowMenuContext_1.RowMenuContext.Provider, { value: undefined, children: (0, jsx_runtime_1.jsx)(TemplateFieldImpl, __assign({}, props, { rowMenu: rowMenu })) }));
498
965
  });
499
966
  //# sourceMappingURL=TemplateField.js.map