@qoretechnologies/reqraft 0.10.55-pr.124.g43fa138 → 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 -201
  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
@@ -4,15 +4,115 @@ import {
4
4
  ReqoreControlGroup,
5
5
  ReqoreMessage,
6
6
  ReqoreModal,
7
+ ReqoreTag,
7
8
  } from '@qoretechnologies/reqore';
8
9
  import { IReqoreCollectionItemProps } from '@qoretechnologies/reqore/dist/components/Collection/item';
9
10
  import { TReqoreBadge } from '@qoretechnologies/reqore/dist/components/Button';
11
+ import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
10
12
  import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
13
+ import { IReqoreTooltip } from '@qoretechnologies/reqore/dist/types/global';
11
14
  import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
12
15
  import { capitalize, isEqual, size } from 'lodash';
13
16
  import { useMemo, useState } from 'react';
17
+ import styled from 'styled-components';
18
+ import {
19
+ UNAVAILABLE_VALUE_FALLBACK_REASON,
20
+ getRefusalMessage,
21
+ } from '../../../../helpers/options';
14
22
  import { Description } from '../../../Description';
15
23
 
24
+ /**
25
+ * The size the picker cannot be dragged below, measured on its own content.
26
+ *
27
+ * reqore floors a modal at what the DRAWER's chrome needs — its title and its
28
+ * close control — which is all reqore can answer for. The picker needs more
29
+ * than that, and said so the hard way: dragged to roughly 45px wide it still
30
+ * held a search box, a list and a close button, stacked in a column nobody
31
+ * could read.
32
+ *
33
+ * Measured on `components-form-select--item-actions-in-the-title-bar` at
34
+ * 1400x1000, sweeping the width and the height of the dialog box:
35
+ *
36
+ * - **320px wide.** The collection is laid out in 300px columns
37
+ * (`minColumnWidth`), so a row is 300px wide whatever the dialog does. At
38
+ * 300px the row overhangs the dialog by 13px and at 310px by 3px; 320px is
39
+ * the first round width that contains it (-7px), and from 340px the row
40
+ * grows with the box instead of overhanging it.
41
+ * - **180px tall.** The panel header is 55px and the search field 26px, and
42
+ * both are whole from 100px — but the list below them has to show one entry
43
+ * in full, or there is nothing choosable in the dialog. A plain row is 56px
44
+ * and is whole from 160px; a row carrying a description is 70px and needs
45
+ * 174px. The floor takes the taller of the two, because the same dialog
46
+ * serves both and a floor that clips the rows it is actually used with is
47
+ * the defect this constant exists to prevent — the assertion-kind picker in
48
+ * qorus-ide is the described-row case. 180px is the first round height that
49
+ * contains it.
50
+ */
51
+ export const SELECT_DIALOG_MIN_WIDTH = 320;
52
+ /** See {@link SELECT_DIALOG_MIN_WIDTH}. */
53
+ export const SELECT_DIALOG_MIN_HEIGHT = 180;
54
+
55
+ /**
56
+ * The hook a row wears while its value cannot be picked — for the stylesheet
57
+ * below, and for a host's own tests and styling.
58
+ */
59
+ export const UNAVAILABLE_ITEM_CLASS = 'reqraft-select-item-unavailable';
60
+
61
+ /**
62
+ * The picker's floor, expressed where it survives the reqore we pin.
63
+ *
64
+ * `className` reaches the element `re-resizable` sizes and drags, and a CSS
65
+ * `min-width` clamps THAT element's rendered box however far the drag goes —
66
+ * so this holds against reqore 0.74.1, where a modal's floor is a hardcoded
67
+ * 40px and `minSize` never reaches it, as well as against the `minWidth` /
68
+ * `minHeight` props reqore grew later.
69
+ *
70
+ * `!important` is load-bearing, not decoration: `re-resizable` writes its own
71
+ * `min-width` / `min-height` INLINE on this same element, and an inline
72
+ * declaration beats a normal one from a stylesheet.
73
+ *
74
+ * Clamping the box from outside `re-resizable` rather than through it costs
75
+ * one thing, and it is not the thing it looks like. The drag stays exact: each
76
+ * move sizes the box from where the drag STARTED plus the pointer's delta, not
77
+ * from the last size, so coming back out tracks the pointer with no dead zone
78
+ * (measured — past the floor the box holds 320px while the inline width reads
79
+ * 40px, and 300px back out it is 400px again). What is left behind is that
80
+ * divergence: an inline width smaller than the box anyone can see. Read this
81
+ * dialog's size from its rect, never from `style.width`. Once reqraft can pin
82
+ * a reqore that has them this becomes `minWidth` / `minHeight` on the modal,
83
+ * and the two agree again.
84
+ *
85
+ * Capped at the viewport so a phone gets a dialog it can see all of rather
86
+ * than a floor wider than its screen — reqore's own maximum is 90vw / 90vh.
87
+ */
88
+ const StyledSelectDialog = styled(ReqoreModal)`
89
+ min-width: min(${SELECT_DIALOG_MIN_WIDTH}px, 90vw) !important;
90
+ min-height: min(${SELECT_DIALOG_MIN_HEIGHT}px, 90vh) !important;
91
+
92
+ /*
93
+ * A value that cannot be picked, said in the row's own stylesheet.
94
+ *
95
+ * Reqore's "disabled" is NOT what marks it. That applies DisabledElement,
96
+ * which is "pointer-events: none" — and the two places the reason can be
97
+ * read, the row's tooltip and whatever control its title bar carries, both
98
+ * need the pointer. Dropping onClick is what actually makes the row inert:
99
+ * reqore derives "interactive" from the handlers a panel was given, so a row
100
+ * with none gets no pointer cursor and no hover lift while keeping every
101
+ * pointer event it had.
102
+ *
103
+ * The dimming lands on the TITLE rather than on the row, because "opacity"
104
+ * creates a stacking context: dimming the row would dim the reason inside it,
105
+ * and the reason is the one thing on an unavailable row that has to read.
106
+ */
107
+ .${UNAVAILABLE_ITEM_CLASS} {
108
+ cursor: not-allowed;
109
+ }
110
+
111
+ .${UNAVAILABLE_ITEM_CLASS} > .reqore-panel-title {
112
+ opacity: 0.55;
113
+ }
114
+ `;
115
+
16
116
  const PositiveColorEffect = {
17
117
  gradient: {
18
118
  colors: {
@@ -22,6 +122,48 @@ const PositiveColorEffect = {
22
122
  },
23
123
  };
24
124
 
125
+ /**
126
+ * One affordance on an offered item, as a DESCRIPTOR rather than a node.
127
+ *
128
+ * A caller's items routinely travel through a `JSON.stringify(items)` memo key
129
+ * (`FieldAllowedValues`, `MultiSelectFormField`), and a rendered React element
130
+ * is circular through its fibre — putting one on an item throws and takes the
131
+ * whole form down with it. So an item names the component and hands it props;
132
+ * `as` is a function or a memo object, which `JSON.stringify` simply omits.
133
+ */
134
+ export interface ISelectFieldCollectionItemAction {
135
+ /** The component to render. Defaults to {@link ReqoreButton}. */
136
+ as?: React.ElementType;
137
+ /** Props handed to it. Keep them JSON-serialisable. */
138
+ props?: Record<string, unknown>;
139
+ }
140
+
141
+ /**
142
+ * Content for a tooltip an item puts on its own row, under the same
143
+ * no-React-elements rule as {@link ISelectFieldCollectionItemAction}: a plain
144
+ * string, or a component named by `as` and given plain props.
145
+ */
146
+ export type TSelectFieldCollectionItemTooltipContent =
147
+ | string
148
+ | { as: React.ElementType; props?: Record<string, unknown> };
149
+
150
+ /**
151
+ * A per-item tooltip: a bare string, or reqore's tooltip options with the
152
+ * content given as a descriptor.
153
+ */
154
+ export type TSelectFieldCollectionItemTooltip =
155
+ | string
156
+ | (Omit<IReqoreTooltip, 'content'> & {
157
+ content?: TSelectFieldCollectionItemTooltipContent;
158
+ });
159
+
160
+ /** A note rendered in an item's body, in the same shape a schema sends one. */
161
+ export interface ISelectFieldCollectionItemMessage {
162
+ intent?: TReqoreIntent;
163
+ title?: string;
164
+ content: string;
165
+ }
166
+
25
167
  export interface ISelectFieldCollectionItem {
26
168
  value?: unknown;
27
169
  name?: unknown;
@@ -34,8 +176,54 @@ export interface ISelectFieldCollectionItem {
34
176
  intent?: TReqoreIntent;
35
177
  disabled?: boolean;
36
178
  groups?: string[];
37
- actions?: { as?: React.ElementType; props?: Record<string, unknown> }[];
38
- messages?: { intent?: TReqoreIntent; title?: string; content: string }[];
179
+ /**
180
+ * Affordances rendered in the row's BODY, stacked under the description.
181
+ * Each one costs the row a line of its own, which is right for something
182
+ * with a label and wrong for a single icon on a long list — see
183
+ * `title_actions`.
184
+ */
185
+ actions?: ISelectFieldCollectionItemAction[];
186
+ /**
187
+ * Affordances rendered in the row's TITLE BAR, inline with the name and to
188
+ * the right of it, where a row already reserves the height.
189
+ *
190
+ * Separate from `actions` rather than a relocation of it: an item's body
191
+ * actions have been rendered under the description since this collection
192
+ * existed, and callers size and lay them out on that basis. This is the
193
+ * opt-in for the other placement.
194
+ */
195
+ title_actions?: ISelectFieldCollectionItemAction[];
196
+ /**
197
+ * A tooltip on the ROW — the whole item is the hover target, not one control
198
+ * inside it. Content that is more than a sentence belongs here rather than
199
+ * in `content`, which every row pays for in height whether it is read or not.
200
+ */
201
+ tooltip?: TSelectFieldCollectionItemTooltip;
202
+ messages?: ISelectFieldCollectionItemMessage[];
203
+ /**
204
+ * Why the value cannot be picked, when it cannot.
205
+ *
206
+ * Rendered FIRST in the row — above the description, not under it, where a
207
+ * `messages` entry would land. A reader who has just been refused a choice is
208
+ * looking for the reason, and a row that answers with its description and
209
+ * makes them read on has answered a different question.
210
+ *
211
+ * Set it together with `disabled`, which is what drops the click handler.
212
+ * Carried as data rather than as a node for the reason in
213
+ * {@link ISelectFieldCollectionItemAction}.
214
+ */
215
+ unavailable?: ISelectFieldCollectionItemMessage;
216
+ /** Added to the row's own classes — see {@link UNAVAILABLE_ITEM_CLASS}. */
217
+ className?: string;
218
+ /**
219
+ * Extra words the list's search matches this row on.
220
+ *
221
+ * Reqore searches a row's label and the STRING of its content, and this
222
+ * collection's content is a React element — which stringifies to
223
+ * `[object Object]` and matches every query equally. Anything that has to be
224
+ * findable belongs here.
225
+ */
226
+ searchString?: string;
39
227
  [key: string]: unknown;
40
228
  }
41
229
 
@@ -109,6 +297,121 @@ export const getSelectItemDescriptionProps = (
109
297
  return { shortDescription };
110
298
  };
111
299
 
300
+ /**
301
+ * Why an item cannot be picked, in the words the row will print.
302
+ *
303
+ * Two shapes reach here and they mean the same thing. A caller that resolved
304
+ * the availability itself sends `unavailable`; a caller — or a server — that
305
+ * only marked the value `disabled` and explained it in `messages` gets the same
306
+ * treatment, because a refusal nobody worded is still a refusal, and a row that
307
+ * silently ignores a click is the defect this answers.
308
+ *
309
+ * Which message explains a refusal is decided in one place
310
+ * ({@link getRefusalMessage}), so the picker and the form's own resolver cannot
311
+ * word the same refusal differently.
312
+ */
313
+ export const getSelectItemUnavailability = (
314
+ item: Pick<ISelectFieldCollectionItem, 'unavailable' | 'disabled' | 'messages'>
315
+ ): ISelectFieldCollectionItemMessage | undefined => {
316
+ if (item.unavailable) {
317
+ return item.unavailable;
318
+ }
319
+
320
+ if (!item.disabled) {
321
+ return undefined;
322
+ }
323
+
324
+ const explanation = getRefusalMessage(item.messages);
325
+
326
+ return {
327
+ intent: explanation?.intent || 'warning',
328
+ title: explanation?.title,
329
+ content: explanation?.content || UNAVAILABLE_VALUE_FALLBACK_REASON,
330
+ };
331
+ };
332
+
333
+ /**
334
+ * An item's title-bar affordances as reqore panel actions.
335
+ *
336
+ * `as` is defaulted here rather than left to reqore: reqore renders an action
337
+ * with no `as` as a `ReqoreButton` built from the action's OWN keys and drops
338
+ * `props` on the floor, so a descriptor that named only props would render an
339
+ * empty button. Defaulting `as` keeps one descriptor shape for both
340
+ * placements.
341
+ *
342
+ * `size` is a default, not an override — the row is `tiny`, so a control in
343
+ * its title bar is too unless the caller says otherwise. The body renderer
344
+ * below forces `size` after the caller's props and cannot be given a
345
+ * different one; that is the older behaviour and it is left alone.
346
+ *
347
+ * An unavailable row leads with a padlock, before whatever the caller declared.
348
+ * It is a TAG, not a control: the words are already on the row and in its
349
+ * tooltip, and a second popover saying the same thing over the first is worse
350
+ * than one. It is also not the compact form's "Depends on" chip, whose whole
351
+ * value is clicking through to the blocking field — from inside a modal that
352
+ * covers that field, there is nowhere for such a click to go.
353
+ */
354
+ export const getSelectItemTitleActions = (
355
+ item: Pick<ISelectFieldCollectionItem, 'title_actions'>,
356
+ unavailable?: ISelectFieldCollectionItemMessage
357
+ ): IReqorePanelAction[] | undefined => {
358
+ const declared = (item.title_actions || []).map(
359
+ ({ as = ReqoreButton, props, ...rest }): IReqorePanelAction => ({
360
+ ...rest,
361
+ as,
362
+ props: { size: 'tiny', ...props },
363
+ })
364
+ );
365
+
366
+ const actions = unavailable ? [UNAVAILABLE_TITLE_ACTION, ...declared] : declared;
367
+
368
+ return actions.length ? actions : undefined;
369
+ };
370
+
371
+ /** The padlock an unavailable row wears — see {@link getSelectItemTitleActions}. */
372
+ const UNAVAILABLE_TITLE_ACTION: IReqorePanelAction = {
373
+ as: ReqoreTag,
374
+ props: {
375
+ size: 'tiny',
376
+ icon: 'LockLine',
377
+ minimal: true,
378
+ className: 'reqraft-select-item-lock',
379
+ },
380
+ };
381
+
382
+ /**
383
+ * An item's row tooltip as reqore's own tooltip prop.
384
+ *
385
+ * The descriptor is turned into an element HERE — after the caller's items
386
+ * have been through whatever memo key they travel under — which is the whole
387
+ * reason the item carries `{ as, props }` and not a node.
388
+ */
389
+ export const buildSelectItemTooltip = (
390
+ tooltip: ISelectFieldCollectionItem['tooltip']
391
+ ): string | IReqoreTooltip | undefined => {
392
+ if (!tooltip) {
393
+ return undefined;
394
+ }
395
+
396
+ if (typeof tooltip === 'string') {
397
+ return tooltip;
398
+ }
399
+
400
+ const { content, ...rest } = tooltip;
401
+
402
+ if (content === undefined) {
403
+ return rest;
404
+ }
405
+
406
+ if (typeof content === 'string') {
407
+ return { ...rest, content };
408
+ }
409
+
410
+ const { as: Component, props } = content;
411
+
412
+ return { ...rest, content: <Component {...props} /> };
413
+ };
414
+
112
415
  export const SelectFieldCollection = ({
113
416
  onClose,
114
417
  items,
@@ -131,7 +434,7 @@ export const SelectFieldCollection = ({
131
434
  }, [items, appliedFilters]);
132
435
 
133
436
  return (
134
- <ReqoreModal
437
+ <StyledSelectDialog
135
438
  isOpen
136
439
  // A library-owned hook for hosts and their tests to target the picker
137
440
  // (qorus-ide's helpers looked for its own `.q-select-dialog`, which
@@ -166,15 +469,53 @@ export const SelectFieldCollection = ({
166
469
  showLayoutSwitch={false}
167
470
  items={filteredItems.map((item): IReqoreCollectionItemProps => {
168
471
  const { longDescription, shortDescription } = getSelectItemDescriptionProps(item);
472
+ const unavailable = getSelectItemUnavailability(item);
473
+ /* One `messages` entry can be the refusal itself — a server that sent
474
+ `disabled` with its reason, resolved into `unavailable` upstream.
475
+ It is already printed above the description, so it is not printed
476
+ again underneath it. */
477
+ const messages = (item.messages || []).filter(
478
+ (message) => !unavailable || message.content !== unavailable.content
479
+ );
169
480
 
170
481
  return {
171
482
  label: item.display_name || item.value?.toString(),
172
483
  size: 'tiny',
173
484
  groups: item.groups,
485
+ className: [item.className, unavailable ? UNAVAILABLE_ITEM_CLASS : undefined]
486
+ .filter(Boolean)
487
+ .join(' '),
488
+ /* Inline with the name, in height the title bar already spends.
489
+ `actions` on the item stays where it has always been, in the
490
+ body under the description. */
491
+ actions: getSelectItemTitleActions(item, unavailable),
492
+ /* The row is the hover target, so an explanation costs the list
493
+ nothing until it is asked for. */
494
+ tooltip: buildSelectItemTooltip(item.tooltip ?? unavailable?.content),
495
+ /* Reqore searches a row's label and its stringified content, and
496
+ the content below is an element — so every row matches
497
+ `[object Object]` and nothing matches a word inside it. A reason
498
+ nobody can search for in a list of hundreds is a reason nobody
499
+ finds. */
500
+ searchString: [item.searchString, unavailable?.title, unavailable?.content]
501
+ .filter(Boolean)
502
+ .join(' '),
174
503
  customTheme: { main: '#08182d' },
175
504
  responsiveTitle: false,
176
505
  content: (
177
506
  <ReqoreControlGroup vertical fluid>
507
+ {unavailable ? (
508
+ <ReqoreMessage
509
+ flat
510
+ icon='LockLine'
511
+ intent={unavailable.intent || 'warning'}
512
+ title={unavailable.title}
513
+ size='small'
514
+ opaque={false}
515
+ >
516
+ {unavailable.content}
517
+ </ReqoreMessage>
518
+ ) : null}
178
519
  {longDescription || shortDescription ? (
179
520
  <Description
180
521
  longDescription={longDescription || ''}
@@ -192,7 +533,7 @@ export const SelectFieldCollection = ({
192
533
  size='tiny'
193
534
  />
194
535
  ))}
195
- {(item.messages || []).map(({ intent, title, content }, index) => (
536
+ {messages.map(({ intent, title, content }, index) => (
196
537
  <ReqoreMessage
197
538
  flat
198
539
  intent={intent}
@@ -220,7 +561,10 @@ export const SelectFieldCollection = ({
220
561
  size: '20px',
221
562
  rounded: true,
222
563
  },
223
- onClick: !item.disabled
564
+ /* No handler is what makes an unavailable row inert, and it is
565
+ deliberately NOT reqore's `disabled`: see the stylesheet on
566
+ `StyledSelectDialog`. */
567
+ onClick: !item.disabled && !unavailable
224
568
  ? (e) => {
225
569
  if (e.currentTarget.contains(e.target as Node)) {
226
570
  onItemSelect(item);
@@ -245,6 +589,6 @@ export const SelectFieldCollection = ({
245
589
  },
246
590
  }))}
247
591
  />
248
- </ReqoreModal>
592
+ </StyledSelectDialog>
249
593
  );
250
594
  };
@@ -5,6 +5,7 @@ import { memo } from 'react';
5
5
  import {
6
6
  getTemplateTagStyle,
7
7
  describeTemplateReference,
8
+ templateTooltip,
8
9
  TTemplateMeta,
9
10
  } from '../../../../helpers/templates';
10
11
 
@@ -18,7 +19,8 @@ export interface IReadOnlyTemplateTagProps {
18
19
 
19
20
  /**
20
21
  * The read-only representation of a selected template — a picker-shaped chip:
21
- * the `$`-dollar icon, the resolved display name (raw value in the tooltip), the
22
+ * the `$`-dollar icon, the resolved display name (and what the value IS in the
23
+ * tooltip, not the reference that names it), the
22
24
  * app image when present, coloured by qorus-ide's scheme (info / qorus purple /
23
25
  * success — see `getTemplateTagStyle`). Shared by `TemplateField`'s disabled
24
26
  * branch and the compact read-first row so a template reads identically wherever
@@ -34,7 +36,7 @@ export const ReadOnlyTemplateTag = memo(
34
36
  icon='ExchangeDollarLine'
35
37
  leftIconProps={{ image: metadata?.image }}
36
38
  label={label}
37
- tooltip={value}
39
+ tooltip={templateTooltip(templates, value)}
38
40
  {...getTemplateTagStyle(metadata)}
39
41
  />
40
42
  </ReqoreTagGroup>
@@ -264,10 +264,20 @@ export const AutoComponent: StoryObj<typeof meta> = {
264
264
  docs: {
265
265
  description: {
266
266
  story:
267
- 'Renders TemplateField over an untyped (`auto`) field that accepts templates. While the field is empty it opens on the template selector rather than the data-type picker, so the author can say which value they mean before answering how it is stored.',
267
+ 'Renders TemplateField over an untyped (`auto`) field that accepts templates. While the field is empty it opens on an editor to type into, which offers the templates — never on a data-type picker, so the author can write the value they mean without answering how it is stored.',
268
268
  },
269
269
  },
270
270
  },
271
+ play: async ({ canvasElement }) => {
272
+ /* The editor itself, not a class: TemplateField puts `.template-selector`
273
+ on every control it draws, the data-type picker included, so asserting
274
+ the class passed while the story showed "Please select data type". */
275
+ await waitFor(() => expect(canvasElement.querySelector('[contenteditable="true"]')).toBeTruthy(), {
276
+ timeout: 5000,
277
+ });
278
+ expect(canvasElement.textContent).not.toContain('Please select data type');
279
+ expect(canvasElement.textContent).not.toContain('Select Template');
280
+ },
271
281
  };
272
282
 
273
283
  export const AllowedValuesWithTemplate: StoryObj<typeof meta> = {
@@ -674,7 +684,7 @@ export const TemplateCanBeSelected: StoryObj<typeof meta> = {
674
684
  docs: {
675
685
  description: {
676
686
  story:
677
- 'Renders TemplateField for a string, opens the templates popover and clicks the Interface ID template — the field switches to the $local:id template value inside the template-offering input (plain word tokens stay typeable; only braced context refs chip).',
687
+ 'Renders TemplateField for a string, opens the templates popover and clicks the Interface ID template — the field switches to template mode, where the $local:id reference is a chip named Interface ID inside an editor that can still be typed into.',
678
688
  },
679
689
  },
680
690
  },
@@ -684,9 +694,16 @@ export const TemplateCanBeSelected: StoryObj<typeof meta> = {
684
694
  await ShowsTemplatesListForString.play({ canvasElement, ...rest });
685
695
  await _testsClickButton({ label: 'Interface ID' });
686
696
 
687
- await sleep(100);
688
-
689
- await expect(canvas.getByDisplayValue('$local:id')).toBeInTheDocument();
697
+ // The chosen reference reads as the name it was chosen by, in an editor
698
+ // that can still be typed into — not as its spelling.
699
+ await waitFor(
700
+ () => {
701
+ const chip = canvasElement.querySelector('.template-selector [contenteditable="true"] .reqore-tag');
702
+ expect(chip?.textContent).toContain('Interface ID');
703
+ },
704
+ { timeout: 5000 }
705
+ );
706
+ await expect(canvas.queryByDisplayValue('$local:id')).toBeNull();
690
707
  },
691
708
  };
692
709
 
@@ -991,14 +1008,73 @@ export const EmptyAnyOpensOnTemplates: StoryObj<typeof meta> = {
991
1008
  docs: {
992
1009
  description: {
993
1010
  story:
994
- 'An untyped, empty field that accepts templates opens showing the template selector rather than a data-type picker.',
1011
+ 'An untyped, empty field that accepts templates opens on an editor to type into, which offers the templates — never on a data-type picker.',
995
1012
  },
996
1013
  },
997
1014
  },
998
1015
  play: async ({ canvasElement }) => {
1016
+ /* The editor itself, not a class: TemplateField puts `.template-selector`
1017
+ on every control it draws, the data-type picker included, so asserting
1018
+ the class passed while the story showed "Please select data type". */
1019
+ await waitFor(() => expect(canvasElement.querySelector('[contenteditable="true"]')).toBeTruthy(), {
1020
+ timeout: 5000,
1021
+ });
1022
+ expect(canvasElement.textContent).not.toContain('Please select data type');
1023
+ expect(canvasElement.textContent).not.toContain('Select Template');
1024
+ },
1025
+ };
1026
+
1027
+ /** A string mixing text with template references, as template mode edits it. */
1028
+ const templateReferencesInText: StoryObj<typeof meta> = {
1029
+ args: {
1030
+ type: 'string',
1031
+ defaultType: 'string',
1032
+ defaultInternalType: 'string',
1033
+ value: 'Interface $local:id failed at $timestamp:now',
1034
+ },
1035
+ play: async ({ canvasElement }) => {
1036
+ const editor = await waitFor(
1037
+ () => {
1038
+ const element = canvasElement.querySelector<HTMLElement>('.template-selector [contenteditable="true"]');
1039
+ expect(element).toBeTruthy();
1040
+ return element!;
1041
+ },
1042
+ { timeout: 5000 }
1043
+ );
1044
+ // Each reference is its catalogue name; the text between them is untouched.
999
1045
  await waitFor(() =>
1000
- expect(canvasElement.querySelector('.template-selector')).toBeTruthy()
1046
+ expect(Array.from(editor.querySelectorAll('.reqore-tag')).map((chip) => chip.textContent?.trim())).toEqual([
1047
+ 'Interface ID',
1048
+ 'Current Timestamp',
1049
+ ])
1001
1050
  );
1051
+ expect(editor.textContent).toContain('failed at');
1052
+ expect(editor.textContent).not.toContain('$local:id');
1053
+ },
1054
+ };
1055
+
1056
+ export const TemplateReferencesInText: StoryObj<typeof meta> = {
1057
+ ...templateReferencesInText,
1058
+ parameters: {
1059
+ docs: {
1060
+ description: {
1061
+ story:
1062
+ 'Renders TemplateField holding the string "Interface $local:id failed at $timestamp:now". Template mode draws each reference as a chip named from the catalogue (Interface ID, Current Timestamp) inside an editor that can still be typed into; the text between them reads as typed.',
1063
+ },
1064
+ },
1065
+ },
1066
+ };
1067
+
1068
+ export const TemplateReferencesInTextOnPhone: StoryObj<typeof meta> = {
1069
+ ...templateReferencesInText,
1070
+ parameters: {
1071
+ qlip: { viewport: { width: 390, height: 844 } },
1072
+ docs: {
1073
+ description: {
1074
+ story:
1075
+ 'Renders the same string with two template references at phone width (390px): the named chips and the text around them wrap inside the editor rather than overflowing it.',
1076
+ },
1077
+ },
1002
1078
  },
1003
1079
  };
1004
1080
 
@@ -1021,7 +1097,7 @@ export const AnyWithLiteralOpensOnTheValue: StoryObj<typeof meta> = {
1021
1097
  docs: {
1022
1098
  description: {
1023
1099
  story:
1024
- 'An untyped field holding a literal keeps showing that literal — only an empty one defaults to the template selector.',
1100
+ 'An untyped field holding a literal keeps showing that literal — only an empty one defaults to the template selector. Where the field offers templates that editor is the chip editor (a contenteditable box), not a textarea, so the literal appears there.',
1025
1101
  },
1026
1102
  },
1027
1103
  },
@@ -1031,12 +1107,15 @@ export const AnyWithLiteralOpensOnTheValue: StoryObj<typeof meta> = {
1031
1107
  // a control that renders either way, so it says nothing about which view is
1032
1108
  // active.
1033
1109
  await waitFor(() => {
1034
- const held = [
1035
- ...canvasElement.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>(
1036
- 'input, textarea'
1037
- ),
1038
- ].some((el) => el.value === 'already-typed');
1039
- expect(held).toBe(true);
1110
+ // Its editor may be a field or, where templates are offered, the chip editor.
1111
+ const shown = [
1112
+ ...canvasElement.querySelectorAll<HTMLElement>('input, textarea, [contenteditable="true"]'),
1113
+ ].map((el) =>
1114
+ el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ?
1115
+ el.value
1116
+ : (el.textContent ?? '').replace(/\uFEFF/g, '').trim()
1117
+ );
1118
+ expect(shown).toContain('already-typed');
1040
1119
  });
1041
1120
  },
1042
1121
  };