@qoretechnologies/reqraft 0.10.55-pr.124.g77dd26c → 0.10.55

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 (375) 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_COMPACT.md +34 -0
  6. package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
  7. package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
  8. package/design/IDE_INTEGRATION.md +13 -0
  9. package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
  10. package/design/SELECT_DIALOG_FLOOR.md +86 -0
  11. package/design/SMART_EDITOR_UX.md +21 -0
  12. package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
  13. package/dist/components/composer/Composer.d.ts +8 -3
  14. package/dist/components/composer/Composer.d.ts.map +1 -1
  15. package/dist/components/composer/Composer.js.map +1 -1
  16. package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
  17. package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
  18. package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
  19. package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
  20. package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
  21. package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
  22. package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
  23. package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
  24. package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
  25. package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
  26. package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
  27. package/dist/components/dpqlEditor/dpqlTags.d.ts +8 -0
  28. package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
  29. package/dist/components/dpqlEditor/dpqlTags.js +5 -1
  30. package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
  31. package/dist/components/dpqlEditor/index.d.ts +4 -0
  32. package/dist/components/dpqlEditor/index.d.ts.map +1 -1
  33. package/dist/components/dpqlEditor/index.js +5 -1
  34. package/dist/components/dpqlEditor/index.js.map +1 -1
  35. package/dist/components/dpqlEditor/types.d.ts +42 -2
  36. package/dist/components/dpqlEditor/types.d.ts.map +1 -1
  37. package/dist/components/dpqlEditor/types.js +0 -4
  38. package/dist/components/dpqlEditor/types.js.map +1 -1
  39. package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
  40. package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
  41. package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
  42. package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
  43. package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
  44. package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
  45. package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
  46. package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
  47. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  48. package/dist/components/form/engine/CompactRow.js +414 -128
  49. package/dist/components/form/engine/CompactRow.js.map +1 -1
  50. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  51. package/dist/components/form/engine/CompactToolbar.js +5 -2
  52. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  53. package/dist/components/form/engine/FormEngine.d.ts +273 -22
  54. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  55. package/dist/components/form/engine/FormEngine.js +669 -192
  56. package/dist/components/form/engine/FormEngine.js.map +1 -1
  57. package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
  58. package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
  59. package/dist/components/form/engine/FormFieldsSkeleton.js +41 -0
  60. package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
  61. package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
  62. package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
  63. package/dist/components/form/engine/OptionFieldMessages.js +203 -13
  64. package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
  65. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  66. package/dist/components/form/engine/_structuredData/SchemaDataView.js +68 -5
  67. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  68. package/dist/components/form/engine/compactRowContext.d.ts +15 -1
  69. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  70. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  71. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  72. package/dist/components/form/engine/compactRowStyles.js +55 -29
  73. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  74. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  75. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  76. package/dist/components/form/engine/optionActions.d.ts +37 -1
  77. package/dist/components/form/engine/optionActions.d.ts.map +1 -1
  78. package/dist/components/form/engine/optionActions.js +71 -1
  79. package/dist/components/form/engine/optionActions.js.map +1 -1
  80. package/dist/components/form/engine/optionsContext.d.ts +22 -0
  81. package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
  82. package/dist/components/form/engine/optionsContext.js +20 -0
  83. package/dist/components/form/engine/optionsContext.js.map +1 -0
  84. package/dist/components/form/engine/readFirst.d.ts +42 -1
  85. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  86. package/dist/components/form/engine/readFirst.js +76 -8
  87. package/dist/components/form/engine/readFirst.js.map +1 -1
  88. package/dist/components/form/engine/rendererTypes.d.ts +42 -3
  89. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
  90. package/dist/components/form/engine/rendererTypes.js +3 -1
  91. package/dist/components/form/engine/rendererTypes.js.map +1 -1
  92. package/dist/components/form/engine/rowFocus.d.ts +30 -0
  93. package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
  94. package/dist/components/form/engine/rowFocus.js +93 -0
  95. package/dist/components/form/engine/rowFocus.js.map +1 -0
  96. package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
  97. package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
  98. package/dist/components/form/engine/rowMenuContext.js +103 -0
  99. package/dist/components/form/engine/rowMenuContext.js.map +1 -0
  100. package/dist/components/form/engine/rowOpenPicker.d.ts +27 -0
  101. package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
  102. package/dist/components/form/engine/rowOpenPicker.js +30 -0
  103. package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
  104. package/dist/components/form/engine/typeChoices.d.ts +42 -0
  105. package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
  106. package/dist/components/form/engine/typeChoices.js +33 -0
  107. package/dist/components/form/engine/typeChoices.js.map +1 -0
  108. package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
  109. package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
  110. package/dist/components/form/expressions/DpqlRendering.js +53 -0
  111. package/dist/components/form/expressions/DpqlRendering.js.map +1 -0
  112. package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
  113. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  114. package/dist/components/form/expressions/ExpressionField.js +203 -45
  115. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  116. package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
  117. package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
  118. package/dist/components/form/expressions/argumentPresence.js +217 -0
  119. package/dist/components/form/expressions/argumentPresence.js.map +1 -0
  120. package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
  121. package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
  122. package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
  123. package/dist/components/form/expressions/builder/index.d.ts +15 -2
  124. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  125. package/dist/components/form/expressions/builder/index.js +21 -65
  126. package/dist/components/form/expressions/builder/index.js.map +1 -1
  127. package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
  128. package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
  129. package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
  130. package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
  131. package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
  132. package/dist/components/form/expressions/mockExpressions.js +8 -4
  133. package/dist/components/form/expressions/mockExpressions.js.map +1 -1
  134. package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
  135. package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
  136. package/dist/components/form/expressions/renderExpressionToText.js +95 -19
  137. package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
  138. package/dist/components/form/expressions/types.d.ts +17 -2
  139. package/dist/components/form/expressions/types.d.ts.map +1 -1
  140. package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
  141. package/dist/components/form/expressions/useExpressions.js +5 -2
  142. package/dist/components/form/expressions/useExpressions.js.map +1 -1
  143. package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
  144. package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
  145. package/dist/components/form/expressions/useRenderExpression.js +50 -126
  146. package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
  147. package/dist/components/form/fields/Field.d.ts +21 -0
  148. package/dist/components/form/fields/Field.d.ts.map +1 -1
  149. package/dist/components/form/fields/Field.js +54 -8
  150. package/dist/components/form/fields/Field.js.map +1 -1
  151. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
  152. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  153. package/dist/components/form/fields/allowed-values/AllowedValues.js +52 -8
  154. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  155. package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
  156. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  157. package/dist/components/form/fields/auto/AutoFormField.js +91 -28
  158. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  159. package/dist/components/form/fields/date/Date.d.ts +1 -1
  160. package/dist/components/form/fields/date/Date.d.ts.map +1 -1
  161. package/dist/components/form/fields/date/Date.js +16 -2
  162. package/dist/components/form/fields/date/Date.js.map +1 -1
  163. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  164. package/dist/components/form/fields/long-string/LongString.js +14 -2
  165. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  166. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  167. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
  168. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  169. package/dist/components/form/fields/number/Number.d.ts +12 -1
  170. package/dist/components/form/fields/number/Number.d.ts.map +1 -1
  171. package/dist/components/form/fields/number/Number.js +6 -3
  172. package/dist/components/form/fields/number/Number.js.map +1 -1
  173. package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
  174. package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
  175. package/dist/components/form/fields/rich-text/RichText.js +153 -12
  176. package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
  177. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  178. package/dist/components/form/fields/select/Select.js +120 -20
  179. package/dist/components/form/fields/select/Select.js.map +1 -1
  180. package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
  181. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  182. package/dist/components/form/fields/select/SelectCollection.js +200 -7
  183. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  184. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
  185. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  186. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
  187. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  188. package/dist/components/form/fields/template/TemplateField.d.ts +34 -2
  189. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  190. package/dist/components/form/fields/template/TemplateField.js +606 -58
  191. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  192. package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
  193. package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
  194. package/dist/components/form/fields/template/TemplateText.js +80 -0
  195. package/dist/components/form/fields/template/TemplateText.js.map +1 -0
  196. package/dist/components/form/index.d.ts +2 -0
  197. package/dist/components/form/index.d.ts.map +1 -1
  198. package/dist/components/form/index.js +4 -0
  199. package/dist/components/form/index.js.map +1 -1
  200. package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
  201. package/dist/components/smartEditor/SmartEditor.js +29 -8
  202. package/dist/components/smartEditor/SmartEditor.js.map +1 -1
  203. package/dist/components/smartEditor/useLspSession.d.ts +5 -1
  204. package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
  205. package/dist/components/smartEditor/useLspSession.js +16 -2
  206. package/dist/components/smartEditor/useLspSession.js.map +1 -1
  207. package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
  208. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
  209. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
  210. package/dist/helpers/common.d.ts.map +1 -1
  211. package/dist/helpers/common.js +7 -3
  212. package/dist/helpers/common.js.map +1 -1
  213. package/dist/helpers/dpqlDetection.d.ts +58 -0
  214. package/dist/helpers/dpqlDetection.d.ts.map +1 -0
  215. package/dist/helpers/dpqlDetection.js +164 -0
  216. package/dist/helpers/dpqlDetection.js.map +1 -0
  217. package/dist/helpers/optionUiTypes.d.ts +31 -3
  218. package/dist/helpers/optionUiTypes.d.ts.map +1 -1
  219. package/dist/helpers/optionUiTypes.js +36 -1
  220. package/dist/helpers/optionUiTypes.js.map +1 -1
  221. package/dist/helpers/options.d.ts +57 -0
  222. package/dist/helpers/options.d.ts.map +1 -1
  223. package/dist/helpers/options.js +97 -1
  224. package/dist/helpers/options.js.map +1 -1
  225. package/dist/helpers/templateItems.d.ts +47 -0
  226. package/dist/helpers/templateItems.d.ts.map +1 -0
  227. package/dist/helpers/templateItems.js +88 -0
  228. package/dist/helpers/templateItems.js.map +1 -0
  229. package/dist/helpers/templateText.d.ts +80 -0
  230. package/dist/helpers/templateText.d.ts.map +1 -0
  231. package/dist/helpers/templateText.js +291 -0
  232. package/dist/helpers/templateText.js.map +1 -0
  233. package/dist/helpers/templates.d.ts +30 -12
  234. package/dist/helpers/templates.d.ts.map +1 -1
  235. package/dist/helpers/templates.js +91 -33
  236. package/dist/helpers/templates.js.map +1 -1
  237. package/dist/helpers/validations.d.ts +35 -8
  238. package/dist/helpers/validations.d.ts.map +1 -1
  239. package/dist/helpers/validations.js +162 -25
  240. package/dist/helpers/validations.js.map +1 -1
  241. package/dist/hooks/useArgSchema.d.ts +2 -1
  242. package/dist/hooks/useArgSchema.d.ts.map +1 -1
  243. package/dist/hooks/useFetch/useFetch.d.ts +3 -1
  244. package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
  245. package/dist/hooks/useFetch/useFetch.js +47 -9
  246. package/dist/hooks/useFetch/useFetch.js.map +1 -1
  247. package/dist/hooks/useTemplates.d.ts +20 -0
  248. package/dist/hooks/useTemplates.d.ts.map +1 -1
  249. package/dist/hooks/useTemplates.js +34 -2
  250. package/dist/hooks/useTemplates.js.map +1 -1
  251. package/dist/index.d.ts +2 -1
  252. package/dist/index.d.ts.map +1 -1
  253. package/dist/index.js +5 -1
  254. package/dist/index.js.map +1 -1
  255. package/dist/utils/fetch.d.ts +151 -1
  256. package/dist/utils/fetch.d.ts.map +1 -1
  257. package/dist/utils/fetch.js +256 -44
  258. package/dist/utils/fetch.js.map +1 -1
  259. package/dist/utils/lspClient.d.ts +6 -0
  260. package/dist/utils/lspClient.d.ts.map +1 -1
  261. package/dist/utils/lspClient.js +62 -11
  262. package/dist/utils/lspClient.js.map +1 -1
  263. package/dist/utils/websocket.d.ts +6 -0
  264. package/dist/utils/websocket.d.ts.map +1 -1
  265. package/dist/utils/websocket.js +15 -7
  266. package/dist/utils/websocket.js.map +1 -1
  267. package/package.json +10 -6
  268. package/src/components/composer/Composer.tsx +7 -2
  269. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
  270. package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
  271. package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
  272. package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
  273. package/src/components/dpqlEditor/dpqlTags.ts +13 -0
  274. package/src/components/dpqlEditor/index.ts +4 -0
  275. package/src/components/dpqlEditor/types.ts +38 -2
  276. package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
  277. package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
  278. package/src/components/form/engine/CompactRow.tsx +487 -160
  279. package/src/components/form/engine/CompactToolbar.tsx +10 -3
  280. package/src/components/form/engine/FormEngine.stories.tsx +1269 -211
  281. package/src/components/form/engine/FormEngine.tsx +922 -270
  282. package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
  283. package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
  284. package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
  285. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
  286. package/src/components/form/engine/compactRowContext.ts +15 -6
  287. package/src/components/form/engine/compactRowStyles.ts +56 -2
  288. package/src/components/form/engine/compactToolbarContext.ts +1 -1
  289. package/src/components/form/engine/optionActions.ts +89 -1
  290. package/src/components/form/engine/optionsContext.ts +24 -0
  291. package/src/components/form/engine/readFirst.ts +88 -8
  292. package/src/components/form/engine/rendererTypes.ts +48 -4
  293. package/src/components/form/engine/rowFocus.ts +101 -0
  294. package/src/components/form/engine/rowMenuContext.ts +159 -0
  295. package/src/components/form/engine/rowOpenPicker.ts +27 -0
  296. package/src/components/form/engine/typeChoices.ts +63 -0
  297. package/src/components/form/expressions/DpqlRendering.tsx +85 -0
  298. package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
  299. package/src/components/form/expressions/ExpressionField.tsx +269 -55
  300. package/src/components/form/expressions/argumentPresence.ts +213 -0
  301. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
  302. package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
  303. package/src/components/form/expressions/builder/index.tsx +39 -81
  304. package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
  305. package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
  306. package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
  307. package/src/components/form/expressions/mockExpressions.ts +8 -4
  308. package/src/components/form/expressions/renderExpressionToText.ts +112 -22
  309. package/src/components/form/expressions/types.ts +17 -2
  310. package/src/components/form/expressions/useExpressions.ts +5 -1
  311. package/src/components/form/expressions/useRenderExpression.ts +43 -127
  312. package/src/components/form/fields/Field.tsx +102 -20
  313. package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
  314. package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
  315. package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
  316. package/src/components/form/fields/date/Date.tsx +17 -1
  317. package/src/components/form/fields/long-string/LongString.tsx +15 -3
  318. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
  319. package/src/components/form/fields/number/Number.tsx +25 -0
  320. package/src/components/form/fields/rich-text/RichText.tsx +248 -30
  321. package/src/components/form/fields/select/Select.stories.tsx +392 -0
  322. package/src/components/form/fields/select/Select.tsx +164 -27
  323. package/src/components/form/fields/select/SelectCollection.tsx +350 -6
  324. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
  325. package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
  326. package/src/components/form/fields/template/TemplateField.tsx +825 -131
  327. package/src/components/form/fields/template/TemplateText.tsx +115 -0
  328. package/src/components/form/index.tsx +4 -0
  329. package/src/components/log/Log.stories.tsx +2 -1
  330. package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
  331. package/src/components/smartEditor/SmartEditor.tsx +41 -9
  332. package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
  333. package/src/components/smartEditor/useLspSession.ts +21 -3
  334. package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
  335. package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
  336. package/src/helpers/common.ts +7 -3
  337. package/src/helpers/dpqlDetection.ts +209 -0
  338. package/src/helpers/optionUiTypes.ts +38 -0
  339. package/src/helpers/options.ts +96 -0
  340. package/src/helpers/templateItems.ts +81 -0
  341. package/src/helpers/templateText.ts +350 -0
  342. package/src/helpers/templates.ts +94 -41
  343. package/src/helpers/validations.ts +203 -51
  344. package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
  345. package/src/hooks/useFetch/useFetch.tsx +53 -7
  346. package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
  347. package/src/hooks/useTemplates.ts +41 -0
  348. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
  349. package/src/index.tsx +14 -1
  350. package/src/stores/currentUser/currentUser.stories.tsx +2 -1
  351. package/src/stories/storyNetwork.ts +92 -0
  352. package/src/utils/fetch.ts +311 -21
  353. package/src/utils/lspClient.ts +68 -11
  354. package/src/utils/websocket.ts +20 -9
  355. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
  356. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
  357. package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
  358. package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
  359. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
  360. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
  361. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
  362. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
  363. package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
  364. package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
  365. package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
  366. package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
  367. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
  368. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
  369. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
  370. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
  371. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
  372. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
  373. package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
  374. package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
  375. package/dist/stories/Data/fileFieldArgSchema.json +0 -86
@@ -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,17 @@ 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 singleLineString_1 = require("../../../../helpers/singleLineString");
99
+ var optionUiTypes_1 = require("../../../../helpers/optionUiTypes");
100
+ var rowMenuContext_1 = require("../../engine/rowMenuContext");
101
+ /**
102
+ * How long the author must pause before typed text is sent to the DPQL
103
+ * probe. Long enough that a probe is not sent for every prefix of what is
104
+ * being typed, short enough that the offer appears while they are still
105
+ * looking at the field.
106
+ */
107
+ var DPQL_DETECT_DEBOUNCE_MS = 400;
84
108
  exports.TemplatesListProps = {
85
109
  useTargetWidth: true,
86
110
  handler: 'focus',
@@ -172,24 +196,31 @@ var MenuTrailingItems = (0, react_2.memo)(function (_a) {
172
196
  // A direct child of `ReqoreMenu`, like `CustomMenuItems`, so the menu hands it
173
197
  // the popover's `closePopover` — a section does not pass it on to its rows.
174
198
  var MenuActionsSection = (0, react_2.memo)(function (_a) {
175
- var actions = _a.actions, closePopover = _a.closePopover, size = _a.size, rest = __rest(_a, ["actions", "closePopover", "size"]);
176
- return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMenuSection, __assign({ label: actions.label, icon: actions.icon, isCollapsed: true, transparent: true, className: 'template-menu-actions', size: size }, rest, { children: renderMenuActionRows(actions, closePopover, size) })));
199
+ var actions = _a.actions, closePopover = _a.closePopover, size = _a.size, startExpanded = _a.startExpanded, rest = __rest(_a, ["actions", "closePopover", "size", "startExpanded"]);
200
+ return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMenuSection, __assign({ label: actions.label, icon: actions.icon, isCollapsed: !startExpanded, transparent: true, className: 'template-menu-actions', size: size }, rest, { children: renderMenuActionRows(actions, closePopover, size) })));
177
201
  });
178
202
  exports.CustomMenuItems = (0, react_2.memo)(function (_a) {
179
- var items = _a.items, closePopover = _a.closePopover, setIsTemplate = _a.setIsTemplate, setTemplateValue = _a.setTemplateValue, rest = __rest(_a, ["items", "closePopover", "setIsTemplate", "setTemplateValue"]);
180
- 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) {
181
- 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) {
182
- var _a, _b;
183
- (_b = (_a = menuItem).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, e, function () {
184
- setIsTemplate(false);
185
- setTemplateValue(null);
186
- closePopover === null || closePopover === void 0 ? void 0 : closePopover();
187
- });
188
- } })));
203
+ var items = _a.items, closePopover = _a.closePopover, setIsTemplate = _a.setIsTemplate, setTemplateValue = _a.setTemplateValue, startExpanded = _a.startExpanded, rest = __rest(_a, ["items", "closePopover", "setIsTemplate", "setTemplateValue", "startExpanded"]);
204
+ return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMenuSection, __assign({ label: 'Set Custom Value', isCollapsed: !startExpanded, transparent: true, icon: 'Text' }, rest, { children: items.map(function (menuItem, index) {
205
+ return 'isDivider' in menuItem ?
206
+ (0, jsx_runtime_1.jsx)(divider_1.default, __assign({}, menuItem), index)
207
+ : (0, react_1.createElement)(reqore_1.ReqoreButton, __assign({}, rest, menuItem, { key: index, onClick: function (e) {
208
+ var _a, _b;
209
+ (_b = (_a = menuItem).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, e, function () {
210
+ setIsTemplate(false);
211
+ setTemplateValue(null);
212
+ closePopover === null || closePopover === void 0 ? void 0 : closePopover();
213
+ });
214
+ } }));
189
215
  }) })));
190
216
  });
191
217
  exports.TemplateDropdownSelector = (0, react_2.memo)(function (_a) {
192
218
  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"]);
219
+ /* The LAST hop before Reqore, which is the only place a drawn description
220
+ may be made: `filteredTemplates` upstream is a `JSON.stringify` memo key,
221
+ and an element in it throws on its own circular owner. */
222
+ var renderMarkdown = (0, markdownRendererContext_1.useMarkdownRenderer)();
223
+ var shownItems = (0, react_2.useMemo)(function () { return (0, templateItems_1.renderTemplateItemDescriptions)((0, templateItems_1.templateItemsToShow)(items), renderMarkdown); }, [items, renderMarkdown]);
193
224
  // One resolver for every surface that names a reference — the picker chip
194
225
  // here, the read-only tag, and the compact row's expression summary. The
195
226
  // row and this editor must agree: a reference that reads as its path when
@@ -207,13 +238,33 @@ exports.TemplateDropdownSelector = (0, react_2.memo)(function (_a) {
207
238
  // SEAM (reqraft): the IDE resolves the template's app/action via
208
239
  // `useGetAppActionData` and renders the action's display name as a badge
209
240
  // here — the app catalogue is IDE-only, so the badge is dropped.
210
- 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] })] }));
241
+ 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 ?
242
+ (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 })
243
+ : null] })] }));
211
244
  });
212
- exports.TemplateField = (0, react_2.memo)(function (_a) {
245
+ /**
246
+ * `ui_type`s whose OWN editor renders templates as chips inline.
247
+ *
248
+ * For these the template SELECTOR must not take over: it replaces a control the
249
+ * author can type in with one they can only pick from. A Qorus test assertion's
250
+ * `Value` is the case this was written for — the IDE's own `auto.tsx` already
251
+ * renders it as template rich text (type freely, references become chips), and
252
+ * the same field reached through this form engine offered a dropdown and no way
253
+ * to type at all.
254
+ *
255
+ * `richtext` is reqraft's own; a consumer adds its types through the
256
+ * `templateAwareUiTypes` prop, exactly as it adds `rendererOnlyUiTypes`.
257
+ */
258
+ exports.BuiltInTemplateAwareUiTypes = ['richtext'];
259
+ var isTemplateAwareUiType = function (uiType, extra) {
260
+ return !!uiType && __spreadArray(__spreadArray([], exports.BuiltInTemplateAwareUiTypes, true), (extra !== null && extra !== void 0 ? extra : []), true).includes(uiType);
261
+ };
262
+ exports.isTemplateAwareUiType = isTemplateAwareUiType;
263
+ var TemplateFieldImpl = (0, react_2.memo)(function (_a) {
213
264
  var _b, _c, _d;
214
- 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
265
+ 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
215
266
  _f = _a.allowTemplates, // eslint-disable-line @typescript-eslint/no-unused-vars
216
- 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, menuTrailingItems = _a.menuTrailingItems, 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", "menuTrailingItems", "reorder", "label"]);
267
+ 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, menuTrailingItems = _a.menuTrailingItems, 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", "menuTrailingItems", "reorder", "label"]);
217
268
  var qorusTypes = (0, useQorusTypes_1.useQorusTypes)();
218
269
  var functions = (0, useExpressions_1.useExpressions)({
219
270
  allow: !!allowFunctions,
@@ -221,6 +272,19 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
221
272
  extraExpressions: rest.expressions,
222
273
  });
223
274
  var type = rest.ui_type || rest.type || rest.defaultType;
275
+ /* The type an EXPRESSION must return, and is stored with, is a DATA type.
276
+ `type` above deliberately prefers `ui_type`, because that is what decides
277
+ which editor to render — but a ui_type says how a value is EDITED, not
278
+ what it is. The IDE's reference fields carry `ui_type: 'test-reference'`
279
+ (a rich-text editor that turns `$.` paths into chips), and feeding that
280
+ to the expression builder asked it for an expression RETURNING
281
+ `test-reference`: no expression returns one, so every choice was refused
282
+ with *"the expected return type is test-reference"*, and the ui_type was
283
+ then stored on the saved expression as its declared type.
284
+
285
+ The declared data type is the constraint; the ui_type is only a fallback
286
+ for a field that declares no type at all. */
287
+ var expressionDataType = rest.type || rest.defaultType || rest.ui_type;
224
288
  var filteredTemplates = (0, react_2.useMemo)(function () {
225
289
  if (!allowTemplates) {
226
290
  return undefined;
@@ -232,7 +296,20 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
232
296
  if (filterTemplatesFunc) {
233
297
  result = filterTemplatesFunc(result);
234
298
  }
235
- return result;
299
+ /* A lone category is opened, once, HERE — where this control resolves the
300
+ templates every one of its pickers then reads: the "Select Template"
301
+ dropdown, the in-editor `$` list, the numeric field's focus dropdown and
302
+ the expression builder's argument picker. Applying it per picker meant
303
+ each new one had to remember, and the ones that forgot made the author
304
+ click through a header naming the only category on offer to reach the
305
+ only values on offer.
306
+
307
+ LAST, after both filters. `filterTemplatesFunc` is given the grouped
308
+ shape on purpose — the Qog expression builder filters top-level items by
309
+ `metadata.dataRole`, which is a property of the CATEGORY — so flattening
310
+ before it would hand it values and quietly change what it keeps. This
311
+ only changes what is finally shown. */
312
+ return __assign(__assign({}, result), { items: (0, templateItems_1.templateItemsToShow)(result === null || result === void 0 ? void 0 : result.items) });
236
313
  }, [
237
314
  JSON.stringify(templates),
238
315
  type,
@@ -256,19 +333,59 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
256
333
  // Only while the field is EMPTY. A field already holding a literal must
257
334
  // open showing that literal — flipping to the template view would hide a
258
335
  // value the author put there and make it look lost.
259
- // ...and only when there is actually something to pick. A field with no
260
- // templates on offer would otherwise open on an EMPTY picker, which is a
261
- // worse place to start than the type picker it replaced.
262
- var typeIsAnyLike = type === 'any' || type === 'auto';
263
- var isEmptyValue = value === undefined || value === null || value === '';
336
+ //
337
+ // A field that may hold a custom value opens there whether or not templates
338
+ // are on offer, and whether or not they have ARRIVED: template mode gives it
339
+ // a typable editor, which offers the list once there is one. Waiting for the
340
+ // list decided the landing from data that is async — on a cold load the
341
+ // type-filtered list did not exist at mount, so the field fell to the type
342
+ // picker and stayed there. A pick-only field (no custom values) is in
343
+ // template mode regardless, by the `!allowCustomValues` term.
344
+ var typeIsAnyLike = (0, optionUiTypes_1.isUntypedOptionType)(type);
345
+ /* An EMPTY field, for the purpose of offering the template selector.
346
+
347
+ `null` is deliberately not empty. It is the DPQL null literal — a value
348
+ an author writes to say "this is nothing", and the only way to assert
349
+ that a call returned no value. Counted as emptiness here, an argument
350
+ holding it was treated as a field the author had just cleared: with a
351
+ type of `any` and templates on offer, the selector-restore effect below
352
+ flipped the field into template mode with a null template value, and
353
+ that reported the field as cleared — so writing `null` in the Text tab
354
+ and switching to Visual silently erased it, leaving `{type: "any"}` in
355
+ the draft and the message "Value for argument 1 ("any") is invalid:
356
+ Value is empty".
357
+
358
+ `undefined` is the cleared state; `null` is a value. Same rule as
359
+ `expressions/argumentPresence`, which states it canonically. */
360
+ var isEmptyValue = value === undefined || value === '';
264
361
  var hasTemplatesOnOffer = !!(0, lodash_1.size)(filteredTemplates === null || filteredTemplates === void 0 ? void 0 : filteredTemplates.items);
265
- var _j = (0, react_2.useState)((isDefaultTemplate ||
266
- (0, templates_1.isValueTemplate)(value) ||
267
- !allowCustomValues ||
268
- (typeIsAnyLike && isEmptyValue && hasTemplatesOnOffer)) &&
269
- allowTemplates), isTemplate = _j[0], setIsTemplate = _j[1];
362
+ /* The field's own editor already renders templates inline, so the selector
363
+ is not merely unnecessary here — it is a downgrade, swapping a typable
364
+ control for a pick-only one. Derived rather than folded into the state
365
+ below so every `setIsTemplate` path keeps working untouched; they simply
366
+ stop having anything to say for these fields. */
367
+ var editorHandlesTemplates = !!allowTemplates && (0, exports.isTemplateAwareUiType)(type, rest.templateAwareUiTypes);
368
+ var hasOnlyAllowedValues = (0, react_2.useMemo)(function () { return !!(0, lodash_1.size)(rest.allowed_values) && !rest.allowed_values_creatable; }, [rest.allowed_values, rest.allowed_values_creatable]);
369
+ /** Where this field lands when it is empty: an untyped one in template mode — see above. */
370
+ var emptyLandsOnTemplates = typeIsAnyLike && (hasTemplatesOnOffer || (!!allowCustomValues && !hasOnlyAllowedValues));
371
+ var opensOnTemplates = isEmptyValue && emptyLandsOnTemplates;
372
+ var _j = (0, react_2.useState)((isDefaultTemplate || (0, templates_1.isValueTemplate)(value) || !allowCustomValues || opensOnTemplates) &&
373
+ allowTemplates), isTemplateState = _j[0], setIsTemplate = _j[1];
374
+ var isTemplate = editorHandlesTemplates ? false : isTemplateState;
270
375
  var _k = (0, react_2.useState)(!!isDefaultFunction && !!allowFunctions), internalIsFunction = _k[0], setInternalIsFunction = _k[1];
271
376
  var _l = (0, react_2.useState)(value), templateValue = _l[0], setTemplateValue = _l[1];
377
+ // Typed text that turned out to be an expression the author may accept.
378
+ var _m = (0, react_2.useState)(null), dpqlOffer = _m[0], setDpqlOffer = _m[1];
379
+ // The literal this field held before typed text switched it into
380
+ // expression mode — kept so the switch is undoable, and so the editor
381
+ // opens on the Text view the author was already writing in.
382
+ var _o = (0, react_2.useState)(null), expressionFromText = _o[0], setExpressionFromText = _o[1];
383
+ // Texts the author has said no to. Without this, dismissing an offer (or
384
+ // undoing a switch) would re-detect the same text on the next render and
385
+ // ask again forever.
386
+ var declinedTexts = (0, react_2.useRef)(new Set());
387
+ var detectTimer = (0, react_2.useRef)(null);
388
+ var probeDpql = (0, useDpqlProbe_1.useDpqlProbe)();
272
389
  var effectiveIsFunction = isFunction || internalIsFunction;
273
390
  (0, useWhyDidYouUpdate_1.useWhyDidYouUpdate)("Template field ".concat(name), __assign({ name: name, onChange: onChange, value: value }, rest));
274
391
  (0, react_2.useEffect)(function () {
@@ -276,6 +393,58 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
276
393
  setTemplateValue(value);
277
394
  }
278
395
  }, [JSON.stringify(value)]);
396
+ /* Clearing an untyped field returns it to the template selector, which is
397
+ where mounting it empty already lands.
398
+
399
+ The rule above that opens an `any`-like field on the template selector is
400
+ a `useState` INITIALISER, so it only ever ran on mount. Clearing a value
401
+ in place does not remount anything, so the field fell through to the TYPE
402
+ picker and demanded `string`/`int`/`hash` before it would let the author
403
+ name a value they had already captured -- the very question that rule
404
+ exists to stop asking. Reloading the page fixed it, which is the tell:
405
+ same field, same empty value, different landing, because only one of the
406
+ two paths ran the rule.
407
+
408
+ Watched as a TRANSITION from a value to no value, not as "the value is
409
+ empty": an author who picks `Set Custom Value` on an empty field is
410
+ asking for the type picker, and re-asserting the template view on every
411
+ render while the field sat empty would take it away again immediately. */
412
+ var hadValue = (0, react_2.useRef)(!isEmptyValue);
413
+ /* A clear is REMEMBERED rather than acted on in the same render.
414
+
415
+ While the field holds a value its resolved type is that VALUE's type — a
416
+ reference reads as `test-reference`, not `auto` — and clearing reverts it
417
+ to the schema's `auto` one render LATER than the value empties. Acting
418
+ only on the transition therefore ran while `typeIsAnyLike` was still
419
+ false and did nothing, and by the time the type settled the transition
420
+ had passed. Measured in the live IDE at the moment of the clear:
421
+ `{ wasCleared: true, typeIsAnyLike: false, type: "test-reference",
422
+ hasTemplatesOnOffer: true }`, then `typeIsAnyLike: true` on the very next
423
+ render with `wasCleared` already false. */
424
+ var restoreSelectorWhenSettled = (0, react_2.useRef)(false);
425
+ /** Set by the template control's own `×`, which is an author saying "not a
426
+ * template" — that clear must not be answered with the selector again. */
427
+ var suppressSelectorRestore = (0, react_2.useRef)(false);
428
+ (0, react_2.useEffect)(function () {
429
+ if (hadValue.current && isEmptyValue) {
430
+ restoreSelectorWhenSettled.current = !suppressSelectorRestore.current;
431
+ suppressSelectorRestore.current = false;
432
+ }
433
+ if (!isEmptyValue) {
434
+ // A value arrived: whatever the author did, they are not sitting on an
435
+ // empty field waiting to be offered the list.
436
+ restoreSelectorWhenSettled.current = false;
437
+ }
438
+ hadValue.current = !isEmptyValue;
439
+ if (restoreSelectorWhenSettled.current &&
440
+ opensOnTemplates &&
441
+ allowTemplates &&
442
+ !effectiveIsFunction) {
443
+ restoreSelectorWhenSettled.current = false;
444
+ setTemplateValue(null);
445
+ setIsTemplate(true);
446
+ }
447
+ }, [isEmptyValue, opensOnTemplates, allowTemplates, effectiveIsFunction]);
279
448
  (0, react_2.useEffect)(function () {
280
449
  if (allowCustomValues && isTemplate && value && !(0, templates_1.isValueTemplate)(value)) {
281
450
  setIsTemplate(false);
@@ -292,6 +461,16 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
292
461
  !(0, templates_1.isCompleteTemplateToken)(value)) {
293
462
  return;
294
463
  }
464
+ // A template reference with an OPERATOR on it (`$local:count + 1`) is
465
+ // an expression, not a template. The loose check above passes it —
466
+ // it starts with `$` and has a colon — so without this every such
467
+ // string was swallowed into template mode, where the arithmetic is
468
+ // dead text and no affordance can reach it. A token standing alone
469
+ // (`$local:count`, `$data:{1.field}`) has no operator and still
470
+ // flips, which is the case template mode is for.
471
+ if (!(0, templates_1.isCompleteTemplateToken)(value) && (0, dpqlDetection_1.mightBeDpqlExpression)(value)) {
472
+ return;
473
+ }
295
474
  setIsTemplate(true);
296
475
  setTemplateValue(value);
297
476
  }
@@ -302,7 +481,6 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
302
481
  onChange === null || onChange === void 0 ? void 0 : onChange(name, templateValue, type, effectiveIsFunction);
303
482
  }
304
483
  }, [JSON.stringify(templateValue)]);
305
- var hasOnlyAllowedValues = (0, react_2.useMemo)(function () { return !!(0, lodash_1.size)(rest.allowed_values) && !rest.allowed_values_creatable; }, [rest.allowed_values, rest.allowed_values_creatable]);
306
484
  var showTemplateToggle = allowCustomValues && allowTemplates && !rest.arg_schema;
307
485
  // Only a BRACED context ref (`$data:{…}` — machine-written, nobody types
308
486
  // one) renders as the picker chip (named via `resolveTemplateLabel`)
@@ -312,7 +490,22 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
312
490
  // keep the string editor too; the chip-in-editor treatment arrives with
313
491
  // the rich-text string mode.
314
492
  var templateValueIsBracedToken = (0, templates_1.isBracedTemplateToken)(templateValue);
315
- var templateSupportsCustomValues = allowCustomValues && type === 'string' && !hasOnlyAllowedValues;
493
+ /* An UNTYPED field can be typed into as well as picked from.
494
+
495
+ Template mode has a typable editor whenever the field's type can hold
496
+ arbitrary text, and `any`/`auto` can — it is the type that has not been
497
+ narrowed yet, not a type that excludes text. Restricting this to
498
+ `string` meant an empty untyped field opened on template mode with no
499
+ editor to offer, so it fell through to the pick-only "Select Template"
500
+ dropdown: a control that can only choose from a list, on exactly the
501
+ fields where an author most often needs to write something the list
502
+ cannot hold — a deeper walk, a literal, or an expression.
503
+
504
+ Reported against a Qorus assertion's Expected Value, and it was never
505
+ specific to that field: every empty untyped field with templates on
506
+ offer got the same downgrade. The editor still offers the same templates
507
+ on focus, so nothing is lost by being able to type as well. */
508
+ var templateSupportsCustomValues = allowCustomValues && (type === 'string' || typeIsAnyLike) && !hasOnlyAllowedValues;
316
509
  var showTemplatesDropdown = allowTemplates && (!allowCustomValues || (isTemplate && !templateSupportsCustomValues));
317
510
  var hasOnlyExpressions = !allowCustomValues && !allowTemplates && allowFunctions;
318
511
  // True when some input control renders besides the ⋮ menu. When nothing
@@ -331,23 +524,39 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
331
524
  var fieldAriaLabel = (_d = (_c = (_b = rest['aria-label']) !== null && _b !== void 0 ? _b : label) !== null && _c !== void 0 ? _c : rest.display_name) !== null && _d !== void 0 ? _d : name;
332
525
  var handleTemplateFieldChange = (0, react_2.useCallback)(function (_name, val) {
333
526
  if (!val) {
334
- setIsTemplate(false);
527
+ /* Emptying the text leaves the field where an empty field lands. For
528
+ an untyped field that is THIS editor: switching to custom mode here
529
+ mounted a different editor in its place, so deleting the last
530
+ character lost the cursor. */
531
+ if (!(emptyLandsOnTemplates && allowTemplates)) {
532
+ setIsTemplate(false);
533
+ }
335
534
  setTemplateValue(null);
336
535
  onChange(name, undefined);
337
536
  }
338
537
  else {
339
538
  setTemplateValue(val);
340
539
  }
341
- }, [name, onChange]);
540
+ }, [name, onChange, emptyLandsOnTemplates, allowTemplates]);
541
+ var handleTemplateTextChange = (0, react_2.useCallback)(function (val) { return handleTemplateFieldChange(name, typeof val === 'string' ? val : ''); }, [handleTemplateFieldChange, name]);
342
542
  var handleSelectTemplateFromList = (0, react_2.useCallback)(
343
543
  // If the field has allowed values and supports templates, we do not want to overwrite the field type with the template type
344
544
  function (item) {
345
545
  // If the template type is richtext, we need to wrap the template value in the richtext template format
346
- var value = item.badge === 'richtext'
347
- ? [
546
+ var value = item.badge === 'richtext' ?
547
+ [
348
548
  {
349
549
  type: 'paragraph',
550
+ /* The empty text nodes on either side are load-bearing. A
551
+ chip is an inline VOID: it holds no text of its own, so the
552
+ only places a cursor can go are the text nodes AROUND it.
553
+ Slate treats that as an invariant and repairs it during
554
+ normalisation, which runs on edits but NOT on a document
555
+ handed in whole as a controlled value — which is what this
556
+ is. Without them the picked template renders as a chip that
557
+ cannot be typed after, so it can never be extended by hand. */
350
558
  children: [
559
+ { text: '' },
351
560
  {
352
561
  children: [{ text: '' }],
353
562
  label: item.label,
@@ -355,6 +564,7 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
355
564
  value: item.value,
356
565
  metadata: item.metadata,
357
566
  },
567
+ { text: '' },
358
568
  ],
359
569
  },
360
570
  ]
@@ -365,7 +575,15 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
365
575
  // `SaveValueButton` in the controls menu — the saved-values storage is
366
576
  // IDE-only, so the menu item is dropped (`allowSaving` is inert).
367
577
  var handleRemoveTemplateClick = (0, react_2.useCallback)(function () {
578
+ /* This is the `×` ON the template control, and it means "I do not want a
579
+ template here" — on a field whose menu offers no `Set Custom Value` it
580
+ is the ONLY way to reach a literal. So it still drops to the custom
581
+ value editor, and it tells the empty-field rule below to keep its hands
582
+ off this particular clear: that rule watches the value going away, and
583
+ this handler clears the value too, so without the flag it would send
584
+ the author straight back to the selector they just dismissed. */
368
585
  if (allowCustomValues) {
586
+ suppressSelectorRestore.current = true;
369
587
  setIsTemplate(false);
370
588
  }
371
589
  setTemplateValue(null);
@@ -393,6 +611,24 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
393
611
  type,
394
612
  value,
395
613
  ]);
614
+ /* Leaving template mode from the ⋮ — the counterpart of "Use Template".
615
+ Template mode's editor has no `×` of its own, so without this the author
616
+ who switched to a template had no way back and no menu to ask with. */
617
+ var handleUseCustomValueClick = (0, react_2.useCallback)(function () {
618
+ setIsTemplate(false);
619
+ /* A reference cannot stay in the custom editor: the field reads it as a
620
+ template again and switches straight back. Anything else is text the
621
+ author wrote, and it survives the switch. */
622
+ if ((0, templates_1.isValueTemplate)(value) || (0, templates_1.isCompleteTemplateToken)(templateValue)) {
623
+ /* Only where the value is actually cleared. The flag is read by the
624
+ "emptied" effect, which never runs when the text survives — setting
625
+ it unconditionally left it armed to swallow the NEXT genuine clear's
626
+ selector restore. */
627
+ suppressSelectorRestore.current = true;
628
+ setTemplateValue(null);
629
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, undefined);
630
+ }
631
+ }, [name, onChange, value, templateValue]);
396
632
  var handleTemplateToggleClick = (0, react_2.useCallback)(function () {
397
633
  setInternalIsFunction(false);
398
634
  onChange(name, undefined, undefined, false);
@@ -402,22 +638,262 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
402
638
  var handleExpressionChange = (0, react_2.useCallback)(function (expressionValue, remove) {
403
639
  if (remove) {
404
640
  setInternalIsFunction(false);
641
+ // The expression is gone, so there is no longer a switch to undo.
642
+ setExpressionFromText(null);
405
643
  }
406
- onChange(name, expressionValue === null || expressionValue === void 0 ? void 0 : expressionValue.value, type, !remove);
407
- }, [name, onChange, type, value]);
644
+ onChange(name, expressionValue === null || expressionValue === void 0 ? void 0 : expressionValue.value, expressionDataType, !remove);
645
+ }, [name, onChange, expressionDataType, value]);
646
+ // ─── Text typed into a plain field that is really a DPQL expression ───
647
+ //
648
+ // A field that accepts expressions still gets DPQL TYPED into it, in the
649
+ // ordinary editor, by an author who never opened the expression view.
650
+ // What happens then depends on the field's own type, and the asymmetry is
651
+ // deliberate: text that could stand as a literal here is only ever
652
+ // OFFERED, because silently rewriting the string `a + b` into a
653
+ // concatenation would destroy a value the author meant. Text that could
654
+ // NOT be a literal here was already an error the moment it was typed, so
655
+ // switching costs nothing and explains the error.
656
+ //
657
+ // The server decides whether the text is an expression at all — see
658
+ // `helpers/dpqlDetection` for why a successful parse alone means nothing.
659
+ var validationField = (0, react_2.useMemo)(function () { return ({
660
+ validation_regex: rest.validation_regex,
661
+ has_to_be_valid_identifier: rest.has_to_be_valid_identifier,
662
+ has_to_have_value: rest.has_to_have_value,
663
+ rules: rest.rules,
664
+ arg_schema: rest.arg_schema,
665
+ }); }, [
666
+ rest.validation_regex,
667
+ rest.has_to_be_valid_identifier,
668
+ rest.has_to_have_value,
669
+ JSON.stringify(rest.rules),
670
+ JSON.stringify(rest.arg_schema),
671
+ ]);
672
+ var enterExpressionFromText = (0, react_2.useCallback)(function (text, expression) {
673
+ var _a;
674
+ setDpqlOffer(null);
675
+ setExpressionFromText(text);
676
+ setInternalIsFunction(true);
677
+ setIsTemplate(false);
678
+ setTemplateValue(null);
679
+ // `dpql/parse` answers with the field-ready envelope
680
+ // (`{is_expression, value}`); this field stores the inner AST and
681
+ // signals the flag through `onChange`'s fourth argument, exactly as
682
+ // `handleExpressionChange` does.
683
+ 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);
684
+ }, [name, onChange, expressionDataType]);
685
+ var undoExpressionFromText = (0, react_2.useCallback)(function () {
686
+ if (expressionFromText === null) {
687
+ return;
688
+ }
689
+ declinedTexts.current.add(expressionFromText);
690
+ setInternalIsFunction(false);
691
+ setExpressionFromText(null);
692
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, expressionFromText, type, false);
693
+ }, [expressionFromText, name, onChange, type]);
694
+ var declineDpqlOffer = (0, react_2.useCallback)(function () {
695
+ if (dpqlOffer) {
696
+ declinedTexts.current.add(dpqlOffer.text);
697
+ }
698
+ setDpqlOffer(null);
699
+ }, [dpqlOffer]);
700
+ var acceptDpqlOffer = (0, react_2.useCallback)(function () {
701
+ if (dpqlOffer) {
702
+ enterExpressionFromText(dpqlOffer.text, dpqlOffer.expression);
703
+ }
704
+ }, [dpqlOffer, enterExpressionFromText]);
705
+ // Held in a ref so the debounce below does not depend on it. `onChange`
706
+ // comes from the host form and can be a fresh function on any render;
707
+ // with the callback in the effect's deps, a re-render caused by a
708
+ // DIFFERENT field would restart this field's timer, and a form the
709
+ // author is typing in re-renders constantly.
710
+ var enterExpressionRef = (0, react_2.useRef)(enterExpressionFromText);
711
+ (0, react_2.useEffect)(function () {
712
+ enterExpressionRef.current = enterExpressionFromText;
713
+ }, [enterExpressionFromText]);
714
+ // Detection only runs on a field that could hold an expression and is
715
+ // currently showing a plain editor — never on a read-only field, a
716
+ // fixed-value list, or one already in template or expression mode.
717
+ var canDetectDpql = !!allowFunctions &&
718
+ !!allowTextExpressions &&
719
+ !effectiveIsFunction &&
720
+ !isTemplate &&
721
+ !hasOnlyAllowedValues &&
722
+ !rest.readonly &&
723
+ !rest.readOnly &&
724
+ !rest.disabled;
725
+ (0, react_2.useEffect)(function () {
726
+ if (detectTimer.current) {
727
+ clearTimeout(detectTimer.current);
728
+ detectTimer.current = null;
729
+ }
730
+ /* A richtext field is still text. It hands its value back as a Slate
731
+ DOCUMENT rather than a flattened string — deliberately, so a chosen
732
+ reference cannot fuse with text typed beside it — and reading only
733
+ `typeof value === 'string'` here silently switched detection off for
734
+ every one of them the moment the field changed shape. A test
735
+ assertion's Value is exactly that field, and `1 + 2` typed into it
736
+ stopped being offered as an expression.
737
+
738
+ `richtextToString` flattens a tag to its raw value, so a document
739
+ holding only a chosen reference reads as `$.result` — no operator, so
740
+ `mightBeDpqlExpression` rejects it below and the server is never
741
+ asked. */
742
+ var text = typeof value === 'string' ? value
743
+ : Array.isArray(value) ? (0, common_1.richtextToString)(value)
744
+ : undefined;
745
+ if (!canDetectDpql ||
746
+ !text ||
747
+ declinedTexts.current.has(text) ||
748
+ !(0, dpqlDetection_1.mightBeDpqlExpression)(text)) {
749
+ setDpqlOffer(null);
750
+ return undefined;
751
+ }
752
+ var cancelled = false;
753
+ detectTimer.current = setTimeout(function () {
754
+ detectTimer.current = null;
755
+ void probeDpql(text)
756
+ .then(function (parsed) {
757
+ if (cancelled) {
758
+ return;
759
+ }
760
+ var outcome = (0, dpqlDetection_1.classifyTypedText)({ text: text, type: type, field: validationField, parsed: parsed });
761
+ if (outcome === 'switch') {
762
+ enterExpressionRef.current(text, parsed.expression);
763
+ }
764
+ else if (outcome === 'offer') {
765
+ setDpqlOffer({ text: text, expression: parsed.expression });
766
+ }
767
+ else {
768
+ setDpqlOffer(null);
769
+ }
770
+ })
771
+ .catch(function () {
772
+ // Detection is an offer of help, never a reason for a field to
773
+ // break: an unreachable instance or a classifier that threw leaves
774
+ // the author's text exactly where they put it.
775
+ if (!cancelled) {
776
+ setDpqlOffer(null);
777
+ }
778
+ });
779
+ }, DPQL_DETECT_DEBOUNCE_MS);
780
+ return function () {
781
+ cancelled = true;
782
+ if (detectTimer.current) {
783
+ clearTimeout(detectTimer.current);
784
+ detectTimer.current = null;
785
+ }
786
+ };
787
+ }, [value, canDetectDpql, type, validationField, probeDpql]);
788
+ var canOfferExpression = allowFunctions && !hasOnlyAllowedValues && !rest.readonly && !internalIsFunction;
789
+ var canOfferTemplate = showTemplateToggle && !isTemplate;
790
+ /* The way back out, published on the same terms the field's own ⋮ draws it.
791
+ A field inside a form ROW draws no menu of its own — it publishes into
792
+ the row's — so leaving this out of the published list meant template mode
793
+ was a one-way door on the surface most options are edited from. */
794
+ var canOfferCustomValue = showTemplateToggle && isTemplate && templateSupportsCustomValues;
795
+ var publishedItems = (0, react_2.useMemo)(function () { return __spreadArray(__spreadArray(__spreadArray(__spreadArray([], (canOfferExpression && !functions.loading
796
+ ? [
797
+ {
798
+ label: 'Use Expression',
799
+ icon: 'Functions',
800
+ tooltip: 'Run a function on this value',
801
+ onClick: handleSelectFunctionChange,
802
+ },
803
+ ]
804
+ : []), true), (canOfferTemplate
805
+ ? [
806
+ {
807
+ label: 'Use Template',
808
+ icon: 'MoneyDollarCircleLine',
809
+ tooltip: 'Use a template',
810
+ onClick: handleTemplateToggleClick,
811
+ },
812
+ ]
813
+ : []), true), (canOfferCustomValue
814
+ ? [
815
+ {
816
+ label: 'Use Custom Value',
817
+ icon: 'EditLine',
818
+ tooltip: 'Write the value here instead of choosing a template',
819
+ onClick: handleUseCustomValueClick,
820
+ },
821
+ ]
822
+ : []), true), (menuItems !== null && menuItems !== void 0 ? menuItems : [])
823
+ .filter(function (item) { return !('isDivider' in item); })
824
+ .map(function (item) { return ({
825
+ label: item.label,
826
+ description: item.description,
827
+ onClick: function () {
828
+ var _a;
829
+ return (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, undefined, function () {
830
+ setIsTemplate(false);
831
+ setTemplateValue(null);
832
+ });
833
+ },
834
+ }); }), true); }, [
835
+ canOfferExpression,
836
+ functions.loading,
837
+ canOfferTemplate,
838
+ canOfferCustomValue,
839
+ menuItems,
840
+ handleSelectFunctionChange,
841
+ handleTemplateToggleClick,
842
+ handleUseCustomValueClick,
843
+ ]);
844
+ /* Keyed on WHICH items are offered, never on the items: they carry
845
+ handlers, so they are a new array every render and a row comparing them
846
+ by value would loop. */
847
+ var publishedKey = [
848
+ canOfferExpression && !functions.loading ? 'expression' : '',
849
+ canOfferTemplate ? 'template' : '',
850
+ canOfferCustomValue ? 'custom-value' : '',
851
+ "custom:".concat((menuItems !== null && menuItems !== void 0 ? menuItems : []).map(function (item) { var _a; return String((_a = item.label) !== null && _a !== void 0 ? _a : ''); }).join('|')),
852
+ ].join(',');
853
+ (0, rowMenuContext_1.useRowMenuPublisher)(rowMenu, publishedKey, publishedItems);
408
854
  var renderControls = (0, react_2.useCallback)(function () {
409
855
  var showFunctionsDropdown = allowFunctions && !hasOnlyAllowedValues && !rest.readonly && !internalIsFunction;
410
856
  var showTemplatesButton = showTemplateToggle && !isTemplate;
857
+ // The way back out of template mode, where the editor draws no `×`.
858
+ var showCustomValueButton = showTemplateToggle && isTemplate && templateSupportsCustomValues;
411
859
  // The "Set value" label promises a way to set one — reorder rows alone don't.
412
- var hasValueRows = showFunctionsDropdown || showTemplatesButton || (0, lodash_1.size)(menuItems) > 0;
860
+ var hasValueRows = showFunctionsDropdown || showTemplatesButton || showCustomValueButton || (0, lodash_1.size)(menuItems) > 0;
861
+ /* A lone group opens itself. Two of the menu's groups are collapsed
862
+ sections, so a menu holding nothing but one of them asked for a click
863
+ to reach the only thing on offer — on an untyped field, "Set Custom
864
+ Value" hiding the type rows behind it. Expanded, it still names what
865
+ its rows are and can still be collapsed. The same rule the template
866
+ list follows for a lone category, and the row menu already publishes
867
+ these rows flat. */
868
+ var loneSectionStartsExpanded = [
869
+ showFunctionsDropdown,
870
+ showTemplatesButton,
871
+ showCustomValueButton,
872
+ (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0,
873
+ (0, lodash_1.size)(menuItems) > 0,
874
+ ].filter(Boolean).length === 1;
875
+ /* ONE menu per control. Where this field sits in a form ROW, the row
876
+ already renders a ⋮ of its own, and drawing a second one beside it
877
+ put two menus of slightly different widths on the same control —
878
+ with the type choices an `any` field offers hidden in the narrower
879
+ one. The items are published to the row instead (see
880
+ `rowMenuContext`); outside a row there is nothing to publish into,
881
+ so the field goes on drawing its own. */
882
+ if (rowMenu) {
883
+ return null;
884
+ }
413
885
  if (hasOnlyExpressions) {
414
- 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 () {
415
- setIsTemplate(false);
416
- setTemplateValue(null);
417
- onChange === null || onChange === void 0 ? void 0 : onChange(name, {
418
- args: [],
419
- }, undefined, true);
420
- } }))) : null;
886
+ return (showFunctionsDropdown ?
887
+ functions.loading ?
888
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })
889
+ : (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 () {
890
+ setIsTemplate(false);
891
+ setTemplateValue(null);
892
+ onChange === null || onChange === void 0 ? void 0 : onChange(name, {
893
+ args: [],
894
+ }, undefined, true);
895
+ } })
896
+ : null);
421
897
  }
422
898
  if (hasValueRows || (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0 || (0, lodash_1.size)(menuTrailingItems) > 0) {
423
899
  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: {
@@ -427,10 +903,18 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
427
903
  transparent: true,
428
904
  size: rest.size,
429
905
  fixed: true,
430
- // Centre the trailing menu in its flex line so it lines up with
431
- // sibling action buttons (reqore alignSelf; replaces a reqraft
432
- // `align-self !important` override of this button).
433
- alignSelf: 'center',
906
+ /* Centre the trailing menu in its flex line so it lines up
907
+ with sibling action buttons (reqore alignSelf; replaces a
908
+ reqraft `align-self !important` override of this button).
909
+
910
+ Centring is only right beside a ONE-LINE editor. The
911
+ expression shell is a toolbar with an editor, a type message
912
+ and a preview stacked under it, so centring put this menu
913
+ somewhere down the side of that block — level with nothing,
914
+ over the editor, and a long way from the Undo it belongs
915
+ beside. There it goes to the top, which is where the shell's
916
+ own toolbar is. */
917
+ alignSelf: effectiveIsFunction ? 'flex-start' : 'center',
434
918
  label: hasInputAffordance || !hasValueRows ? undefined : 'Set value',
435
919
  style: hasInputAffordance ?
436
920
  {
@@ -450,7 +934,21 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
450
934
  },
451
935
  },
452
936
  },
453
- }, 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, (0, lodash_1.size)(menuTrailingItems) > 0 ? ((0, jsx_runtime_1.jsx)(MenuTrailingItems, { items: menuTrailingItems, size: rest.size })) : null] }) }));
937
+ }, handler: 'click', content: (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreMenu, { size: rest.size, maxHeight: '400px', style: { overflow: 'auto' }, children: [showFunctionsDropdown ?
938
+ functions.loading ?
939
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreSkeleton, { size: rest.size })
940
+ : (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 })
941
+ : null, showTemplatesButton ?
942
+ (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", ' '] })
943
+ : null, showCustomValueButton ?
944
+ (0, jsx_runtime_1.jsxs)(reqore_1.ReqoreButton, { transparent: true, icon: 'EditLine', className: 'template-custom-value', tooltip: 'Write the value here instead of choosing a template', compact: true, size: rest.size, onClick: handleUseCustomValueClick, children: [' ', "Use Custom Value", ' '] })
945
+ : null, (0, lodash_1.size)(menuActions === null || menuActions === void 0 ? void 0 : menuActions.items) > 0 ?
946
+ (0, jsx_runtime_1.jsx)(MenuActionsSection, { actions: menuActions, size: rest.size, startExpanded: loneSectionStartsExpanded })
947
+ : null, (0, lodash_1.size)(menuItems) > 0 ?
948
+ (0, jsx_runtime_1.jsx)(exports.CustomMenuItems, { items: menuItems, setIsTemplate: setIsTemplate, setTemplateValue: setTemplateValue, startExpanded: loneSectionStartsExpanded })
949
+ : null, (0, lodash_1.size)(menuTrailingItems) > 0 ?
950
+ (0, jsx_runtime_1.jsx)(MenuTrailingItems, { items: menuTrailingItems, size: rest.size })
951
+ : null] }) }));
454
952
  }
455
953
  return null;
456
954
  }, [
@@ -458,11 +956,14 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
458
956
  functions.expressions,
459
957
  handleSelectFunctionChange,
460
958
  handleTemplateToggleClick,
959
+ handleUseCustomValueClick,
461
960
  hasOnlyAllowedValues,
462
961
  isTemplate,
463
962
  rest.readonly,
464
963
  rest.size,
465
964
  showTemplateToggle,
965
+ // reaches the menu through `templateSupportsCustomValues`
966
+ allowCustomValues,
466
967
  hasOnlyExpressions,
467
968
  type,
468
969
  value,
@@ -470,17 +971,31 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
470
971
  menuActions,
471
972
  menuTrailingItems,
472
973
  internalIsFunction,
974
+ effectiveIsFunction,
473
975
  hasInputAffordance,
976
+ // the row decides whether this field draws a menu at all
977
+ rowMenu,
474
978
  ]);
475
979
  // When the type is a list, and it has an element type - that element type is different
476
980
  // from the field type, so we need to send down the full templates object
477
981
  // and the actual rendered field will filter it's own templates
478
982
  // This is a special case only for lists with element types
983
+ /* `undefined` when nothing is on offer, rather than a list with no items:
984
+ an editor given a list draws the control that opens it, so a field whose
985
+ templates were all filtered out by type opened an EMPTY menu. */
479
986
  var componentTemplates = (0, react_2.useMemo)(function () {
480
- return type === 'list' && (rest.ui_element_type || rest.element_type)
481
- ? templates
482
- : __assign(__assign({}, filteredTemplates), exports.TemplatesListProps);
483
- }, [JSON.stringify(filteredTemplates), rest.ui_element_type, rest.element_type, type]);
987
+ if (type === 'list' && (rest.ui_element_type || rest.element_type)) {
988
+ return templates;
989
+ }
990
+ return (0, lodash_1.size)(filteredTemplates === null || filteredTemplates === void 0 ? void 0 : filteredTemplates.items) ? __assign(__assign({}, filteredTemplates), exports.TemplatesListProps) : undefined;
991
+ }, [
992
+ JSON.stringify(filteredTemplates),
993
+ // the list-with-element-type branch hands this back untouched
994
+ templates,
995
+ rest.ui_element_type,
996
+ rest.element_type,
997
+ type,
998
+ ]);
484
999
  if (effectiveIsFunction && !hasOnlyAllowedValues) {
485
1000
  // SEAM (reqraft): `allowTextExpressions` swaps the IDE's bare builder
486
1001
  // for the ExpressionField shell (Visual = the same builder, Text = the
@@ -490,12 +1005,26 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
490
1005
  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: {
491
1006
  is_expression: true,
492
1007
  value: value,
493
- }, 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()] }));
1008
+ },
1009
+ // The host's per-ui_type editors reach this field through the
1010
+ // rest-spread; the expression shell needs them explicitly or the
1011
+ // builder's operands render "Unknown type!" for any consumer
1012
+ // ui_type (an assertion's `test-reference` Value, for one).
1013
+ 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,
1014
+ // An author who typed the expression as text is already
1015
+ // writing in that language — dropping them into the visual
1016
+ // builder would make them find their own sentence again.
1017
+ defaultMode: expressionFromText !== null ? 'text' : 'visual', reorder: reorder }) }), expressionFromText !== null ?
1018
+ (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { fixed: true, compact: true, minimal: true, icon: 'ArrowGoBackLine',
1019
+ // No `intent` with `minimal`: a Reqore intent is a FILL, and
1020
+ // `muted` as TEXT is 1.3:1 on the app surface.
1021
+ size: rest.size, className: 'dpql-detected-undo', tooltip: "Keep \"".concat(expressionFromText, "\" as plain text instead"), onClick: undoExpressionFromText, children: "Undo" })
1022
+ : null, renderControls()] }));
494
1023
  }
495
1024
  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: {
496
1025
  is_expression: true,
497
1026
  value: value,
498
- }, 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()] }));
1027
+ }, 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()] }));
499
1028
  }
500
1029
  if (rest.disabled) {
501
1030
  if (isTemplate) {
@@ -505,9 +1034,28 @@ exports.TemplateField = (0, react_2.memo)(function (_a) {
505
1034
  // IDE's intent scheme. Shared with the compact read-first row.
506
1035
  return (0, jsx_runtime_1.jsx)(ReadOnlyTemplateTag_1.ReadOnlyTemplateTag, { value: templateValue, templates: templates, size: rest.size });
507
1036
  }
508
- return (0, jsx_runtime_1.jsx)(Comp, __assign({ value: value, onChange: onChange, name: name }, rest));
1037
+ return ((0, jsx_runtime_1.jsx)(Comp, __assign({ value: value, onChange: onChange, name: name }, rest, { componentOverrides: componentOverrides })));
509
1038
  }
510
- 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 ||
511
- (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()] })));
1039
+ 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 ?
1040
+ (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 }))
1041
+ : null, isTemplate && templateSupportsCustomValues && !templateValueIsBracedToken ?
1042
+ (0, jsx_runtime_1.jsx)(RichText_1.RichTextFormField, __assign({ className: 'template-selector', valueFormat: 'text', singleLine: (0, singleLineString_1.isSingleLineStringType)('string'), value: typeof templateValue === 'string' ? templateValue : '', templates: filteredTemplates, allowTemplates: true, onChange: handleTemplateTextChange }, rest, { "aria-label": fieldAriaLabel }))
1043
+ : null, dpqlOffer ?
1044
+ (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 })] })
1045
+ : null, (showTemplatesDropdown ||
1046
+ (isTemplate && templateSupportsCustomValues && templateValueIsBracedToken)) ?
1047
+ (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 })
1048
+ : null, renderControls()] })));
1049
+ });
1050
+ /**
1051
+ * The row channel belongs to the row's OWN editor: this field. It is read here
1052
+ * and hidden from everything this field renders, so a field nested inside it —
1053
+ * an expression operand, a list item — keeps drawing its own menu instead of
1054
+ * publishing actions into a row menu that cannot say which value they act on.
1055
+ * `undefined` outside a row, where this field draws its own menu too.
1056
+ */
1057
+ exports.TemplateField = (0, react_2.memo)(function (props) {
1058
+ var rowMenu = (0, rowMenuContext_1.useRowMenu)();
1059
+ return ((0, jsx_runtime_1.jsx)(rowMenuContext_1.RowMenuContext.Provider, { value: undefined, children: (0, jsx_runtime_1.jsx)(TemplateFieldImpl, __assign({}, props, { rowMenu: rowMenu })) }));
512
1060
  });
513
1061
  //# sourceMappingURL=TemplateField.js.map