@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
@@ -1,14 +1,23 @@
1
1
  import { ReqoreControlGroup, ReqoreTag, ReqoreVerticalSpacer } from '@qoretechnologies/reqore';
2
2
  import { IReqoreTagProps } from '@qoretechnologies/reqore/dist/components/Tag';
3
+ import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
3
4
  import { IQorusFormField, IQorusFormSchema, TQorusForm } from '@qoretechnologies/ts-toolkit';
4
- import { flatten, size } from 'lodash';
5
+ import { isArray, size } from 'lodash';
5
6
  import { useMemo } from 'react';
6
- import { getRequiredOptionMessage } from '../../../helpers/options';
7
+ import { useContextSelector } from 'use-context-selector';
8
+ import {
9
+ UNAVAILABLE_VALUE_FALLBACK_REASON,
10
+ getRefusalMessage,
11
+ getRequiredOptionMessage,
12
+ } from '../../../helpers/options';
13
+ import { isUntypedOptionType } from '../../../helpers/optionUiTypes';
7
14
  import {
8
15
  hasAllDependenciesFullfilled,
16
+ parseDependency,
9
17
  validateFieldWithResult,
10
18
  validateOptionWithRequiredGroups,
11
19
  } from '../../../helpers/validations';
20
+ import { OptionsContext } from './optionsContext';
12
21
 
13
22
  /** A `messages` entry that appears only for certain values of its siblings. */
14
23
  export interface IConditionalFieldMessage {
@@ -49,10 +58,13 @@ export const isConditionalMessageShown = (
49
58
  optionsSchema?: IQorusFormSchema
50
59
  ): boolean => {
51
60
  const values = allOptions || {};
52
- if (message.when && !hasAllDependenciesFullfilled(message.when as never, values, optionsSchema)) {
61
+ if (message.when && !hasAllDependenciesFullfilled(message.when, values, optionsSchema)) {
53
62
  return false;
54
63
  }
55
- if (message.unless && hasAllDependenciesFullfilled(message.unless as never, values, optionsSchema)) {
64
+ if (
65
+ message.unless &&
66
+ hasAllDependenciesFullfilled(message.unless, values, optionsSchema)
67
+ ) {
56
68
  return false;
57
69
  }
58
70
  return true;
@@ -65,12 +77,270 @@ export const getShownSchemaMessages = <T extends IConditionalFieldMessage>(
65
77
  optionsSchema?: IQorusFormSchema
66
78
  ): T[] => (messages || []).filter((m) => isConditionalMessageShown(m, allOptions, optionsSchema));
67
79
 
80
+ /**
81
+ * Name one `depends_on` entry so the reader can act on it.
82
+ *
83
+ * The list used to be built by looking each entry up as a whole field name,
84
+ * which is only ever true of the bare-name form. A COMPARISON entry
85
+ * (`kind=type`, `kind!=type`) matched no field, was dropped, and a field whose
86
+ * dependencies are all comparisons — the shape every subject field of a test
87
+ * uses — rendered "…are not fulfilled:" with nothing after the colon. The
88
+ * reader was told a dependency was unmet and not which one, by a sentence that
89
+ * stopped mid-way.
90
+ *
91
+ * One parser for both halves, the same one the evaluator uses, so the sentence
92
+ * and the lock cannot disagree about what an entry means.
93
+ */
94
+ const describeDependency = (dependency: string, schema: IQorusFormSchema): string | undefined => {
95
+ const { name, op, value } = parseDependency(dependency);
96
+ const dependencySchema = schema[name];
97
+
98
+ if (!dependencySchema) {
99
+ return undefined;
100
+ }
101
+
102
+ const label = `"${dependencySchema.display_name || name}"`;
103
+
104
+ // `!name` names no value to compare against — it is satisfied by the sibling
105
+ // being unanswered — so it gets a sentence of its own rather than the
106
+ // comparison template, which would read `must not be "undefined"`.
107
+ if (op === '!') {
108
+ return `${label} must have no value`;
109
+ }
110
+
111
+ return op ? `${label} must ${op === '!=' ? 'not ' : ''}be "${value}"` : label;
112
+ };
113
+
114
+ /**
115
+ * The whole `depends_on` list as one phrase.
116
+ *
117
+ * A nested entry is an OR — only one of its members has to hold. It used to be
118
+ * flattened into the same comma-separated list as the top-level entries, which
119
+ * are an AND, so an either/or was shown to the reader as a set of things all
120
+ * required. The alternatives now stay grouped and joined by "or".
121
+ */
122
+ export const describeDependencies = (
123
+ dependencies: (string | string[])[] | string[][],
124
+ schema: IQorusFormSchema
125
+ ): string =>
126
+ (dependencies as (string | string[])[])
127
+ .map((entry) => {
128
+ if (!isArray(entry)) {
129
+ return describeDependency(entry as string, schema);
130
+ }
131
+ const alternatives = (entry as string[])
132
+ .map((dep) => describeDependency(dep, schema))
133
+ .filter((dep): dep is string => !!dep);
134
+ if (!alternatives.length) {
135
+ return undefined;
136
+ }
137
+ return alternatives.length > 1 ? `(${alternatives.join(' or ')})` : alternatives[0];
138
+ })
139
+ .filter((entry): entry is string => !!entry)
140
+ .join(', ');
141
+
142
+ /**
143
+ * An allowed VALUE, as far as being offered is concerned.
144
+ *
145
+ * ts-toolkit 0.5.82 carries both `IQorusAllowedValue.depends_on` and
146
+ * `IQorusFormFieldMessage.when` / `unless`, so this no longer waits on a publish.
147
+ * It remains its own shape because it states only what DECIDING AVAILABILITY needs —
148
+ * three keys out of the twenty an allowed value can carry — so a caller that has
149
+ * only those three can be answered, and the predicate cannot quietly start reading
150
+ * a fourth.
151
+ */
152
+ export interface IAllowedValueAvailabilityInput {
153
+ /**
154
+ * The value is offered only while every entry holds — the SAME grammar, the
155
+ * same parser and the same evaluator a field's `depends_on` uses, one level
156
+ * down. For what this form can decide on its own.
157
+ */
158
+ depends_on?: (string | string[])[];
159
+ /**
160
+ * Someone who already knows has refused it — the server, for a sandbox or
161
+ * permission context, a capability of the selected kind, or a requirement in
162
+ * a scope `depends_on` deliberately cannot reach.
163
+ */
164
+ disabled?: boolean;
165
+ /** Where a refusal says why. */
166
+ messages?: IConditionalFieldMessage[];
167
+ }
168
+
169
+ /** Whether a value can be picked, and the sentence that stands in for it. */
170
+ export interface IAllowedValueAvailability {
171
+ available: boolean;
172
+ /** Why not. Read by the row itself, by its tooltip and by the list's search. */
173
+ reason?: string;
174
+ /** A heading the refusal came with, when it came with one. */
175
+ title?: string;
176
+ intent?: TReqoreIntent;
177
+ }
178
+
179
+ /** One object for every available value, so identity says "nothing to show". */
180
+ export const ALLOWED_VALUE_AVAILABLE: IAllowedValueAvailability = Object.freeze({
181
+ available: true,
182
+ });
183
+
184
+ /**
185
+ * Whether an offered value can be picked, and why not.
186
+ *
187
+ * Two things can refuse a value and they render identically, because a reader
188
+ * has no way to tell them apart and no reason to care:
189
+ *
190
+ * - **the predicate** — the value's own `depends_on`, judged against the form
191
+ * it is standing in. Whatever the form itself can decide.
192
+ * - **the pre-resolved refusal** — `disabled: true` with a message, from
193
+ * whoever already knows. A sandbox that denies a domain, a capability of the
194
+ * selected kind, a requirement in another value scope: none of them are
195
+ * reachable from a sibling's answer, and a predicate that pretended otherwise
196
+ * would be a predicate that never fires.
197
+ *
198
+ * The predicate is checked first when both could speak. It names a field in
199
+ * this very form, so the reader can act on it without leaving the row; a served
200
+ * reason is a statement about somewhere else.
201
+ *
202
+ * A value is never HIDDEN for either reason. A choice that vanishes takes its
203
+ * own explanation with it, and leaves the reader looking for something they
204
+ * were told exists.
205
+ */
206
+ export const getAllowedValueAvailability = (
207
+ value: IAllowedValueAvailabilityInput | undefined,
208
+ allOptions?: TQorusForm,
209
+ schema?: IQorusFormSchema
210
+ ): IAllowedValueAvailability => {
211
+ if (!value) {
212
+ return ALLOWED_VALUE_AVAILABLE;
213
+ }
214
+
215
+ /* `allOptions` is undefined only when there is no form around the picker at
216
+ all (see `OptionsContext`). A predicate about siblings has nothing to judge
217
+ there, and locking every gated value in a standalone picker would be a
218
+ rendering fault rather than a fact about the form. */
219
+ if (
220
+ size(value.depends_on) &&
221
+ allOptions &&
222
+ !hasAllDependenciesFullfilled(value.depends_on!, allOptions, schema)
223
+ ) {
224
+ const dependsOn = schema ? describeDependencies(value.depends_on!, schema) : '';
225
+
226
+ return {
227
+ available: false,
228
+ intent: 'warning',
229
+ reason:
230
+ dependsOn ?
231
+ `Unavailable because some dependencies are not fulfilled: ${dependsOn}`
232
+ // Nothing nameable: every entry pointed at a field this schema does
233
+ // not contain. A sentence that ends in a colon and then stops reads
234
+ // as a rendering fault rather than as a fact about the form.
235
+ : 'Unavailable because some dependencies are not fulfilled',
236
+ };
237
+ }
238
+
239
+ if (value.disabled) {
240
+ /* The refusal picks its own words out of `messages`, through the same
241
+ `when`/`unless` filter a field's messages go through — a served reason
242
+ can itself be conditional. The most serious one speaks: a `danger`
243
+ message is the one that explains a refusal, and an `info` note beside it
244
+ is not. */
245
+ const explanation = getRefusalMessage(getShownSchemaMessages(value.messages, allOptions, schema));
246
+
247
+ return {
248
+ available: false,
249
+ intent: (explanation?.intent as TReqoreIntent) || 'warning',
250
+ title: explanation?.title,
251
+ // A refusal with no message at all still has to say something: silence
252
+ // over a value that will not respond is the defect this whole mechanism
253
+ // exists to remove.
254
+ reason: explanation?.content || UNAVAILABLE_VALUE_FALLBACK_REASON,
255
+ };
256
+ }
257
+
258
+ return ALLOWED_VALUE_AVAILABLE;
259
+ };
260
+
261
+ /**
262
+ * The availability of each offered value, against the form the picker stands in.
263
+ *
264
+ * Aligned with `items`: the result at index `i` describes `items[i]`.
265
+ *
266
+ * The answer is computed INSIDE the context selector, not from a copy of the
267
+ * form values pulled out of it. `OptionsContext` hands down a fresh object on
268
+ * every render of the engine, so a picker that selected the values themselves
269
+ * would re-render on every keystroke anywhere in the form; selecting the
270
+ * computed answer re-renders it only when an availability actually changes.
271
+ *
272
+ * Values that gate on nothing skip the computation entirely, and that is a
273
+ * claim about each VALUE rather than about the field it belongs to. The gate
274
+ * used to be one flag for the whole list, so a single gated value in a
275
+ * thousand-item picker resolved all thousand and serialised all thousand on
276
+ * every keystroke anywhere in the form. Only the gated indices are resolved
277
+ * and serialised; every other position is the same frozen
278
+ * {@link ALLOWED_VALUE_AVAILABLE}, which costs nothing to fill in and lets
279
+ * identity say "nothing to show".
280
+ */
281
+ export const useAllowedValueAvailability = (
282
+ items: readonly IAllowedValueAvailabilityInput[] | undefined
283
+ ): IAllowedValueAvailability[] => {
284
+ const gatedIndices = useMemo(
285
+ () =>
286
+ (items || []).reduce<number[]>((indices, item, index) => {
287
+ if (size(item?.depends_on) || item?.disabled === true) {
288
+ indices.push(index);
289
+ }
290
+ return indices;
291
+ }, []),
292
+ [items]
293
+ );
294
+
295
+ const serialized = useContextSelector(OptionsContext, (context) =>
296
+ gatedIndices.length ?
297
+ JSON.stringify(
298
+ gatedIndices.map((index) =>
299
+ getAllowedValueAvailability(items![index], context.value, context.schema)
300
+ )
301
+ )
302
+ : ''
303
+ );
304
+
305
+ return useMemo(() => {
306
+ const result = (items || []).map(() => ALLOWED_VALUE_AVAILABLE);
307
+
308
+ if (!serialized) {
309
+ return result;
310
+ }
311
+
312
+ // Aligned back onto the caller's own indices: the picker asks for
313
+ // `availability[i]` and must get the answer for `items[i]`.
314
+ const resolved = JSON.parse(serialized) as IAllowedValueAvailability[];
315
+ gatedIndices.forEach((index, position) => {
316
+ result[index] = resolved[position];
317
+ });
318
+
319
+ return result;
320
+ }, [serialized, items, gatedIndices]);
321
+ };
322
+
68
323
  export interface IOptionFieldMessagesProps {
69
324
  schema: IQorusFormSchema;
70
325
  option: IQorusFormField;
71
326
  allOptions?: TQorusForm;
72
327
  name: string;
73
328
  getType: (type: string) => string;
329
+ /**
330
+ * The reader has not edited this field in this session.
331
+ *
332
+ * An error is a report that something went WRONG. "This field is required"
333
+ * under a field nobody has been in yet reports nothing: the form is empty
334
+ * because it is new. The requirement is already carried three other ways —
335
+ * the asterisk on the label, the Needs-attention box the row sits in, and the
336
+ * completion meter — so the message waits until the reader has been in the
337
+ * field and left it empty, which IS a thing that went wrong.
338
+ *
339
+ * Only the plain required message is held back. A value that fails
340
+ * validation, an unmet required GROUP and a locked dependency are all facts
341
+ * about what the form currently holds, and they show immediately.
342
+ */
343
+ untouched?: boolean;
74
344
  }
75
345
 
76
346
  /**
@@ -84,13 +354,26 @@ export const getOptionFieldMessages = ({
84
354
  name,
85
355
  allOptions,
86
356
  getType,
357
+ untouched,
87
358
  }: IOptionFieldMessagesProps): IReqoreTagProps[] => {
88
359
  const optionSchema = schema[name];
89
360
  const result: IReqoreTagProps[] = [];
90
361
 
91
362
  if (option.value || option.value === false || option.value === 0 || option.value === null) {
363
+ /* A concrete type the VALUE carries beats an untyped schema.
364
+ `auto`/`any` declare no type — the author picks one (Binary, Date, …)
365
+ and it is recorded beside the value. Reading the schema first validated
366
+ a binary value as `auto`, which auto-detects from the value and accepts
367
+ anything, so the form said the field needed attention while the field
368
+ itself gave no reason. The form's own check already prefers the stored
369
+ type (`getOptionFieldStorageType`); this is the same precedence. */
370
+ const schemaType = optionSchema?.ui_type as string;
371
+ const storedType = option.type as string;
372
+ const typeToValidate =
373
+ isUntypedOptionType(schemaType) && storedType ? storedType : schemaType || storedType;
374
+
92
375
  const validationData = validateFieldWithResult(
93
- getType((optionSchema?.ui_type as string) || (option.type as string)),
376
+ getType(typeToValidate),
94
377
  option.value,
95
378
  {
96
379
  has_to_have_value: true,
@@ -109,7 +392,7 @@ export const getOptionFieldMessages = ({
109
392
  });
110
393
  }
111
394
  } else {
112
- if (optionSchema?.required) {
395
+ if (optionSchema?.required && !untouched) {
113
396
  result.push({ label: 'This field is required', intent: 'danger' });
114
397
  }
115
398
 
@@ -134,14 +417,16 @@ export const getOptionFieldMessages = ({
134
417
  optionSchema?.depends_on &&
135
418
  !hasAllDependenciesFullfilled(optionSchema.depends_on, allOptions, schema)
136
419
  ) {
137
- const dependsOn = flatten(optionSchema.depends_on)
138
- .filter((dep: string) => !!schema[dep])
139
- .map((dep: string) => schema[dep].display_name || dep)
140
- .map((dep: string) => `"${dep}"`)
141
- .join(', ');
420
+ const dependsOn = describeDependencies(optionSchema.depends_on, schema);
142
421
 
143
422
  result.push({
144
- label: `This field is disabled because some dependencies are not fulfilled: ${dependsOn}`,
423
+ label:
424
+ dependsOn ?
425
+ `This field is disabled because some dependencies are not fulfilled: ${dependsOn}`
426
+ // Nothing nameable: every entry pointed at a field this schema does
427
+ // not contain. A sentence that ends in a colon and then stops reads
428
+ // as a rendering fault rather than as a fact about the form.
429
+ : 'This field is disabled because some dependencies are not fulfilled',
145
430
  intent: 'warning',
146
431
  });
147
432
  }
@@ -155,10 +440,11 @@ export const OptionFieldMessages = ({
155
440
  name,
156
441
  allOptions,
157
442
  getType,
443
+ untouched,
158
444
  }: IOptionFieldMessagesProps) => {
159
445
  const messages: IReqoreTagProps[] = useMemo(
160
- () => getOptionFieldMessages({ schema, option, name, allOptions, getType }),
161
- [JSON.stringify(schema), JSON.stringify(option), JSON.stringify(allOptions), name]
446
+ () => getOptionFieldMessages({ schema, option, name, allOptions, getType, untouched }),
447
+ [JSON.stringify(schema), JSON.stringify(option), JSON.stringify(allOptions), name, untouched]
162
448
  );
163
449
 
164
450
  if (!size(messages)) {
@@ -5,11 +5,14 @@ import {
5
5
  ReqoreP,
6
6
  ReqoreTag,
7
7
  } from '@qoretechnologies/reqore';
8
+ import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
8
9
  import {
9
10
  IQorusFormField,
10
11
  IQorusFormSchema,
11
12
  TQorusFormFieldSchema,
12
13
  } from '@qoretechnologies/ts-toolkit';
14
+ import { IRichtextSegment } from '../../../../helpers/common';
15
+ import { templateTextSegments } from '../../../../helpers/templateText';
13
16
  import React from 'react';
14
17
  import styled from 'styled-components';
15
18
  import { useContextSelector } from 'use-context-selector';
@@ -24,12 +27,15 @@ import {
24
27
  fieldLabel,
25
28
  findAllowedValueOption,
26
29
  formatOptionValue,
30
+ getAllowedValueIcon,
27
31
  getAllowedValueImage,
28
32
  isCodeField,
29
33
  orderedKeys,
30
34
  recordIdentity,
31
35
  titleKeyFor,
32
36
  } from '../readFirst';
37
+ import { TemplateText } from '../../fields/template/TemplateText';
38
+ import { TFieldWithOwnTemplates } from '../rendererTypes';
33
39
  import { isEmptyUiEnvelope, isUiEncodedValue } from './structuredData';
34
40
 
35
41
  /**
@@ -343,6 +349,32 @@ const labelColumn = (value: unknown, schema: IQorusFormSchema): string => {
343
349
  const isNavigableUrl = (value: unknown): value is string =>
344
350
  typeof value === 'string' && /^(https?|wss?|ftps?):\/\//i.test(value);
345
351
 
352
+ /**
353
+ * The reference chips inside a formatted value — none when it holds no
354
+ * reference at all.
355
+ *
356
+ * The preview is the SAME value the row above draws, one level down, and it
357
+ * reached the mono cell as a raw string: a sub-field holding
358
+ * `$._case.mode != 'simulate'` read as a chip on the row and as the engine's
359
+ * own spelling in the inset beneath it, which is a row disagreeing with itself
360
+ * about what its value is.
361
+ *
362
+ * A field's OWN list and grammar win where it declares them — the same
363
+ * precedence the rows use — falling back to the form's shared catalogue.
364
+ */
365
+ const referenceSegments = (
366
+ text: unknown,
367
+ templates: IReqoreFormTemplates | undefined,
368
+ fieldSchema?: TQorusFormFieldSchema
369
+ ): IRichtextSegment[] => {
370
+ if (typeof text !== 'string' || !text) {
371
+ return [];
372
+ }
373
+ const own = fieldSchema as TFieldWithOwnTemplates | undefined;
374
+ const segments = templateTextSegments(text, own?.templates ?? templates, own?.templateToken);
375
+ return segments.some((segment) => segment.kind === 'tag') ? segments : [];
376
+ };
377
+
346
378
  /** One field's value, rendered the way its row above renders it. */
347
379
  const FieldValue = ({
348
380
  field,
@@ -356,7 +388,11 @@ const FieldValue = ({
356
388
  const formatted = formatOptionValue(field, fieldSchema);
357
389
  // An allowed value that carries a logo shows it here too, exactly as the row
358
390
  // above does — the preview is the same value, so it gets the same treatment.
391
+ // An option marked with an ICON rather than a logo gets the same, for the same
392
+ // reason: the mark is how a list of bare words is told apart, and dropping it
393
+ // here would make the preview disagree with the row it previews.
359
394
  const image = getAllowedValueImage(field.value, fieldSchema);
395
+ const icon = getAllowedValueIcon(field.value, fieldSchema);
360
396
 
361
397
  // Data reads in mono; a chosen label does not.
362
398
  //
@@ -368,6 +404,13 @@ const FieldValue = ({
368
404
  // separates the two, and it is the same lookup that produced the text.
369
405
  const isChosenLabel = !!findAllowedValueOption(field.value, fieldSchema);
370
406
 
407
+ /* Read from context, like the code renderer above: a level is arbitrarily
408
+ deep, and the form's catalogue is a property of the form rather than of any
409
+ one level. See `referenceSegments`. */
410
+ const templates = useContextSelector(CompactRowContext, (v) => v.templates);
411
+ const fieldTemplates = (fieldSchema as TFieldWithOwnTemplates | undefined)?.templates ?? templates;
412
+ const segments = isNavigableUrl(field.value) ? [] : referenceSegments(formatted, templates, fieldSchema);
413
+
371
414
  return (
372
415
  <StyledRowValue $color={color}>
373
416
  {field.value === true || field.value === false ?
@@ -387,6 +430,12 @@ const FieldValue = ({
387
430
  size='14px'
388
431
  style={{ flexShrink: 0, marginRight: 6, verticalAlign: 'middle' }}
389
432
  />
433
+ : icon ?
434
+ <ReqoreIcon
435
+ icon={icon as any}
436
+ size='14px'
437
+ style={{ flexShrink: 0, marginRight: 6, verticalAlign: 'middle' }}
438
+ />
390
439
  : null}
391
440
  {isNavigableUrl(field.value) ?
392
441
  // The address IS the link — it used to render as inert text with a
@@ -403,6 +452,10 @@ const FieldValue = ({
403
452
  >
404
453
  {formatted}
405
454
  </ReqoreLink>
455
+ : segments.length ?
456
+ // `full`: the preview is where a value that the row had to cut is
457
+ // shown whole, so its chips wrap rather than clipping.
458
+ <TemplateText segments={segments} templates={fieldTemplates} full />
406
459
  : formatted}
407
460
  </span>
408
461
  }
@@ -410,6 +463,25 @@ const FieldValue = ({
410
463
  );
411
464
  };
412
465
 
466
+ /**
467
+ * A value with no field schema of its own — a bare entry in a list, a level
468
+ * that turned out to be a scalar. It still reads as prose with named chips
469
+ * where it holds references, because a reference is not more raw for having
470
+ * arrived without a schema.
471
+ */
472
+ const ScalarText = ({
473
+ text,
474
+ templates,
475
+ }: {
476
+ text: string;
477
+ templates: IReqoreFormTemplates | undefined;
478
+ }) => {
479
+ const segments = referenceSegments(text, templates);
480
+ return segments.length ?
481
+ <TemplateText segments={segments} templates={templates} full />
482
+ : <>{text}</>;
483
+ };
484
+
413
485
  /** One described hash: its fields as label/value pairs in the shared columns. */
414
486
  const SchemaRecord = ({
415
487
  record,
@@ -547,6 +619,9 @@ const SchemaLevel = ({
547
619
  colors: ISchemaDataViewProps['colors'];
548
620
  }) => {
549
621
  const inner = unwrap(value);
622
+ // Same reason as `FieldValue`: a heading and a bare list entry are values too,
623
+ // and a reference in one reads as a name everywhere else in the form.
624
+ const templates = useContextSelector(CompactRowContext, (v) => v.templates);
550
625
 
551
626
  if (Array.isArray(inner)) {
552
627
  return (
@@ -567,6 +642,14 @@ const SchemaLevel = ({
567
642
  if (!identity) {
568
643
  return null;
569
644
  }
645
+ // An item is headed by one of its own fields, so the
646
+ // heading is that field's value and reads like it: a case
647
+ // named by a reference is named by the reference's NAME.
648
+ const titleSegments = referenceSegments(
649
+ identity.text,
650
+ templates,
651
+ schema[identity.key] as TQorusFormFieldSchema | undefined
652
+ );
570
653
  return (
571
654
  <StyledItemTitle
572
655
  className='schema-view-item-title'
@@ -578,7 +661,9 @@ const SchemaLevel = ({
578
661
  $accent={colors.accent}
579
662
  title={identity.label}
580
663
  >
581
- {identity.text}
664
+ {titleSegments.length ?
665
+ <TemplateText segments={titleSegments} templates={templates} full />
666
+ : identity.text}
582
667
  </StyledItemTitle>
583
668
  );
584
669
  })()}
@@ -595,7 +680,10 @@ const SchemaLevel = ({
595
680
  className='options-readfirst-valuetext schema-view-data'
596
681
  style={{ fontFamily: MONO_FONT_STACK }}
597
682
  >
598
- {formatOptionValue({ value: record } as IQorusFormField)}
683
+ <ScalarText
684
+ text={formatOptionValue({ value: record } as IQorusFormField)}
685
+ templates={templates}
686
+ />
599
687
  </span>
600
688
  </StyledRowValue>
601
689
  }
@@ -613,7 +701,10 @@ const SchemaLevel = ({
613
701
 
614
702
  return (
615
703
  <ReqoreP size='small' style={{ color: colors.key }}>
616
- {formatOptionValue({ value: inner } as IQorusFormField)}
704
+ <ScalarText
705
+ text={formatOptionValue({ value: inner } as IQorusFormField)}
706
+ templates={templates}
707
+ />
617
708
  </ReqoreP>
618
709
  );
619
710
  };
@@ -39,6 +39,21 @@ export type TCodePreviewRenderer = (props: {
39
39
  export interface ICompactRowContext {
40
40
  // Props / config
41
41
  readOnly?: boolean;
42
+ /**
43
+ * What a click on a READ-ONLY row should do, when the consumer has somewhere
44
+ * to send it.
45
+ *
46
+ * A read-only row is not a control, so by default it opens nothing: it shows
47
+ * everything it has and answers a click with silence. That is right when
48
+ * there is nowhere else to go, and a dead end when there is — a reader
49
+ * clicking a value they want to change is telling you exactly what they want,
50
+ * and being ignored teaches them the surface is broken.
51
+ *
52
+ * Given this, the row becomes the way in to wherever that field IS editable,
53
+ * and says so: it takes a button's role and keyboard handling. Without it
54
+ * nothing changes, so a read-only form with no editor behind it stays inert.
55
+ */
56
+ onReadOnlyActivate?: (optionName: string) => void;
42
57
  commitMode: 'immediate' | 'batched';
43
58
  expandMode: 'single' | 'multi';
44
59
 
@@ -54,12 +69,6 @@ export interface ICompactRowContext {
54
69
  // display name in the read-first chip.
55
70
  templates?: IReqoreFormTemplates;
56
71
  expandedOptions: string[];
57
- // Name of the field the engine expanded programmatically for
58
- // `autoFocusFirstRequired`. CompactRow focuses this one with `preventScroll`
59
- // so an off-screen / below-the-fold form is never scrolled into view on
60
- // mount. A ref (not state) so it's current when the 60ms focus timer fires,
61
- // regardless of render batching.
62
- autoFocusNameRef?: MutableRefObject<string | undefined>;
63
72
  highlightedOptions: string[];
64
73
  flashedOptions: string[];
65
74
  infoPanelOverrides: Record<string, boolean>;