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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/.claude/CLAUDE.md +25 -0
  2. package/.tasks/INDEX.md +1 -0
  3. package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
  4. package/README.md +1 -1
  5. package/design/FORM_ENGINE_COMPACT.md +34 -0
  6. package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
  7. package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
  8. package/design/IDE_INTEGRATION.md +13 -0
  9. package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
  10. package/design/SELECT_DIALOG_FLOOR.md +86 -0
  11. package/design/SMART_EDITOR_UX.md +21 -0
  12. package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
  13. package/dist/components/composer/Composer.d.ts +8 -3
  14. package/dist/components/composer/Composer.d.ts.map +1 -1
  15. package/dist/components/composer/Composer.js.map +1 -1
  16. package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
  17. package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
  18. package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
  19. package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
  20. package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
  21. package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
  22. package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
  23. package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
  24. package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
  25. package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
  26. package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
  27. package/dist/components/dpqlEditor/dpqlTags.d.ts +8 -0
  28. package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
  29. package/dist/components/dpqlEditor/dpqlTags.js +5 -1
  30. package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
  31. package/dist/components/dpqlEditor/index.d.ts +4 -0
  32. package/dist/components/dpqlEditor/index.d.ts.map +1 -1
  33. package/dist/components/dpqlEditor/index.js +5 -1
  34. package/dist/components/dpqlEditor/index.js.map +1 -1
  35. package/dist/components/dpqlEditor/types.d.ts +42 -2
  36. package/dist/components/dpqlEditor/types.d.ts.map +1 -1
  37. package/dist/components/dpqlEditor/types.js +0 -4
  38. package/dist/components/dpqlEditor/types.js.map +1 -1
  39. package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
  40. package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
  41. package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
  42. package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
  43. package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
  44. package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
  45. package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
  46. package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
  47. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  48. package/dist/components/form/engine/CompactRow.js +414 -128
  49. package/dist/components/form/engine/CompactRow.js.map +1 -1
  50. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  51. package/dist/components/form/engine/CompactToolbar.js +5 -2
  52. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  53. package/dist/components/form/engine/FormEngine.d.ts +273 -22
  54. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  55. package/dist/components/form/engine/FormEngine.js +669 -192
  56. package/dist/components/form/engine/FormEngine.js.map +1 -1
  57. package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
  58. package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
  59. package/dist/components/form/engine/FormFieldsSkeleton.js +41 -0
  60. package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
  61. package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
  62. package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
  63. package/dist/components/form/engine/OptionFieldMessages.js +203 -13
  64. package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
  65. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  66. package/dist/components/form/engine/_structuredData/SchemaDataView.js +68 -5
  67. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  68. package/dist/components/form/engine/compactRowContext.d.ts +15 -1
  69. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  70. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  71. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  72. package/dist/components/form/engine/compactRowStyles.js +55 -29
  73. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  74. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  75. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  76. package/dist/components/form/engine/optionActions.d.ts +37 -1
  77. package/dist/components/form/engine/optionActions.d.ts.map +1 -1
  78. package/dist/components/form/engine/optionActions.js +71 -1
  79. package/dist/components/form/engine/optionActions.js.map +1 -1
  80. package/dist/components/form/engine/optionsContext.d.ts +22 -0
  81. package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
  82. package/dist/components/form/engine/optionsContext.js +20 -0
  83. package/dist/components/form/engine/optionsContext.js.map +1 -0
  84. package/dist/components/form/engine/readFirst.d.ts +42 -1
  85. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  86. package/dist/components/form/engine/readFirst.js +76 -8
  87. package/dist/components/form/engine/readFirst.js.map +1 -1
  88. package/dist/components/form/engine/rendererTypes.d.ts +42 -3
  89. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
  90. package/dist/components/form/engine/rendererTypes.js +3 -1
  91. package/dist/components/form/engine/rendererTypes.js.map +1 -1
  92. package/dist/components/form/engine/rowFocus.d.ts +30 -0
  93. package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
  94. package/dist/components/form/engine/rowFocus.js +93 -0
  95. package/dist/components/form/engine/rowFocus.js.map +1 -0
  96. package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
  97. package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
  98. package/dist/components/form/engine/rowMenuContext.js +103 -0
  99. package/dist/components/form/engine/rowMenuContext.js.map +1 -0
  100. package/dist/components/form/engine/rowOpenPicker.d.ts +27 -0
  101. package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
  102. package/dist/components/form/engine/rowOpenPicker.js +30 -0
  103. package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
  104. package/dist/components/form/engine/typeChoices.d.ts +42 -0
  105. package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
  106. package/dist/components/form/engine/typeChoices.js +33 -0
  107. package/dist/components/form/engine/typeChoices.js.map +1 -0
  108. package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
  109. package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
  110. package/dist/components/form/expressions/DpqlRendering.js +53 -0
  111. package/dist/components/form/expressions/DpqlRendering.js.map +1 -0
  112. package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
  113. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  114. package/dist/components/form/expressions/ExpressionField.js +203 -45
  115. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  116. package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
  117. package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
  118. package/dist/components/form/expressions/argumentPresence.js +217 -0
  119. package/dist/components/form/expressions/argumentPresence.js.map +1 -0
  120. package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
  121. package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
  122. package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
  123. package/dist/components/form/expressions/builder/index.d.ts +15 -2
  124. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  125. package/dist/components/form/expressions/builder/index.js +21 -65
  126. package/dist/components/form/expressions/builder/index.js.map +1 -1
  127. package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
  128. package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
  129. package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
  130. package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
  131. package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
  132. package/dist/components/form/expressions/mockExpressions.js +8 -4
  133. package/dist/components/form/expressions/mockExpressions.js.map +1 -1
  134. package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
  135. package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
  136. package/dist/components/form/expressions/renderExpressionToText.js +95 -19
  137. package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
  138. package/dist/components/form/expressions/types.d.ts +17 -2
  139. package/dist/components/form/expressions/types.d.ts.map +1 -1
  140. package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
  141. package/dist/components/form/expressions/useExpressions.js +5 -2
  142. package/dist/components/form/expressions/useExpressions.js.map +1 -1
  143. package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
  144. package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
  145. package/dist/components/form/expressions/useRenderExpression.js +50 -126
  146. package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
  147. package/dist/components/form/fields/Field.d.ts +21 -0
  148. package/dist/components/form/fields/Field.d.ts.map +1 -1
  149. package/dist/components/form/fields/Field.js +54 -8
  150. package/dist/components/form/fields/Field.js.map +1 -1
  151. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
  152. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  153. package/dist/components/form/fields/allowed-values/AllowedValues.js +52 -8
  154. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  155. package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
  156. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  157. package/dist/components/form/fields/auto/AutoFormField.js +91 -28
  158. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  159. package/dist/components/form/fields/date/Date.d.ts +1 -1
  160. package/dist/components/form/fields/date/Date.d.ts.map +1 -1
  161. package/dist/components/form/fields/date/Date.js +16 -2
  162. package/dist/components/form/fields/date/Date.js.map +1 -1
  163. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  164. package/dist/components/form/fields/long-string/LongString.js +14 -2
  165. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  166. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  167. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
  168. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  169. package/dist/components/form/fields/number/Number.d.ts +12 -1
  170. package/dist/components/form/fields/number/Number.d.ts.map +1 -1
  171. package/dist/components/form/fields/number/Number.js +6 -3
  172. package/dist/components/form/fields/number/Number.js.map +1 -1
  173. package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
  174. package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
  175. package/dist/components/form/fields/rich-text/RichText.js +153 -12
  176. package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
  177. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  178. package/dist/components/form/fields/select/Select.js +120 -20
  179. package/dist/components/form/fields/select/Select.js.map +1 -1
  180. package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
  181. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  182. package/dist/components/form/fields/select/SelectCollection.js +200 -7
  183. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  184. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
  185. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  186. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
  187. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  188. package/dist/components/form/fields/template/TemplateField.d.ts +34 -2
  189. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  190. package/dist/components/form/fields/template/TemplateField.js +606 -58
  191. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  192. package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
  193. package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
  194. package/dist/components/form/fields/template/TemplateText.js +80 -0
  195. package/dist/components/form/fields/template/TemplateText.js.map +1 -0
  196. package/dist/components/form/index.d.ts +2 -0
  197. package/dist/components/form/index.d.ts.map +1 -1
  198. package/dist/components/form/index.js +4 -0
  199. package/dist/components/form/index.js.map +1 -1
  200. package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
  201. package/dist/components/smartEditor/SmartEditor.js +29 -8
  202. package/dist/components/smartEditor/SmartEditor.js.map +1 -1
  203. package/dist/components/smartEditor/useLspSession.d.ts +5 -1
  204. package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
  205. package/dist/components/smartEditor/useLspSession.js +16 -2
  206. package/dist/components/smartEditor/useLspSession.js.map +1 -1
  207. package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
  208. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
  209. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
  210. package/dist/helpers/common.d.ts.map +1 -1
  211. package/dist/helpers/common.js +7 -3
  212. package/dist/helpers/common.js.map +1 -1
  213. package/dist/helpers/dpqlDetection.d.ts +58 -0
  214. package/dist/helpers/dpqlDetection.d.ts.map +1 -0
  215. package/dist/helpers/dpqlDetection.js +164 -0
  216. package/dist/helpers/dpqlDetection.js.map +1 -0
  217. package/dist/helpers/optionUiTypes.d.ts +31 -3
  218. package/dist/helpers/optionUiTypes.d.ts.map +1 -1
  219. package/dist/helpers/optionUiTypes.js +36 -1
  220. package/dist/helpers/optionUiTypes.js.map +1 -1
  221. package/dist/helpers/options.d.ts +57 -0
  222. package/dist/helpers/options.d.ts.map +1 -1
  223. package/dist/helpers/options.js +97 -1
  224. package/dist/helpers/options.js.map +1 -1
  225. package/dist/helpers/templateItems.d.ts +47 -0
  226. package/dist/helpers/templateItems.d.ts.map +1 -0
  227. package/dist/helpers/templateItems.js +88 -0
  228. package/dist/helpers/templateItems.js.map +1 -0
  229. package/dist/helpers/templateText.d.ts +80 -0
  230. package/dist/helpers/templateText.d.ts.map +1 -0
  231. package/dist/helpers/templateText.js +291 -0
  232. package/dist/helpers/templateText.js.map +1 -0
  233. package/dist/helpers/templates.d.ts +30 -12
  234. package/dist/helpers/templates.d.ts.map +1 -1
  235. package/dist/helpers/templates.js +91 -33
  236. package/dist/helpers/templates.js.map +1 -1
  237. package/dist/helpers/validations.d.ts +35 -8
  238. package/dist/helpers/validations.d.ts.map +1 -1
  239. package/dist/helpers/validations.js +162 -25
  240. package/dist/helpers/validations.js.map +1 -1
  241. package/dist/hooks/useArgSchema.d.ts +2 -1
  242. package/dist/hooks/useArgSchema.d.ts.map +1 -1
  243. package/dist/hooks/useFetch/useFetch.d.ts +3 -1
  244. package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
  245. package/dist/hooks/useFetch/useFetch.js +47 -9
  246. package/dist/hooks/useFetch/useFetch.js.map +1 -1
  247. package/dist/hooks/useTemplates.d.ts +20 -0
  248. package/dist/hooks/useTemplates.d.ts.map +1 -1
  249. package/dist/hooks/useTemplates.js +34 -2
  250. package/dist/hooks/useTemplates.js.map +1 -1
  251. package/dist/index.d.ts +2 -1
  252. package/dist/index.d.ts.map +1 -1
  253. package/dist/index.js +5 -1
  254. package/dist/index.js.map +1 -1
  255. package/dist/utils/fetch.d.ts +151 -1
  256. package/dist/utils/fetch.d.ts.map +1 -1
  257. package/dist/utils/fetch.js +256 -44
  258. package/dist/utils/fetch.js.map +1 -1
  259. package/dist/utils/lspClient.d.ts +6 -0
  260. package/dist/utils/lspClient.d.ts.map +1 -1
  261. package/dist/utils/lspClient.js +62 -11
  262. package/dist/utils/lspClient.js.map +1 -1
  263. package/dist/utils/websocket.d.ts +6 -0
  264. package/dist/utils/websocket.d.ts.map +1 -1
  265. package/dist/utils/websocket.js +15 -7
  266. package/dist/utils/websocket.js.map +1 -1
  267. package/package.json +10 -6
  268. package/src/components/composer/Composer.tsx +7 -2
  269. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
  270. package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
  271. package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
  272. package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
  273. package/src/components/dpqlEditor/dpqlTags.ts +13 -0
  274. package/src/components/dpqlEditor/index.ts +4 -0
  275. package/src/components/dpqlEditor/types.ts +38 -2
  276. package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
  277. package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
  278. package/src/components/form/engine/CompactRow.tsx +487 -160
  279. package/src/components/form/engine/CompactToolbar.tsx +10 -3
  280. package/src/components/form/engine/FormEngine.stories.tsx +1269 -211
  281. package/src/components/form/engine/FormEngine.tsx +922 -270
  282. package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
  283. package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
  284. package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
  285. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
  286. package/src/components/form/engine/compactRowContext.ts +15 -6
  287. package/src/components/form/engine/compactRowStyles.ts +56 -2
  288. package/src/components/form/engine/compactToolbarContext.ts +1 -1
  289. package/src/components/form/engine/optionActions.ts +89 -1
  290. package/src/components/form/engine/optionsContext.ts +24 -0
  291. package/src/components/form/engine/readFirst.ts +88 -8
  292. package/src/components/form/engine/rendererTypes.ts +48 -4
  293. package/src/components/form/engine/rowFocus.ts +101 -0
  294. package/src/components/form/engine/rowMenuContext.ts +159 -0
  295. package/src/components/form/engine/rowOpenPicker.ts +27 -0
  296. package/src/components/form/engine/typeChoices.ts +63 -0
  297. package/src/components/form/expressions/DpqlRendering.tsx +85 -0
  298. package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
  299. package/src/components/form/expressions/ExpressionField.tsx +269 -55
  300. package/src/components/form/expressions/argumentPresence.ts +213 -0
  301. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
  302. package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
  303. package/src/components/form/expressions/builder/index.tsx +39 -81
  304. package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
  305. package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
  306. package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
  307. package/src/components/form/expressions/mockExpressions.ts +8 -4
  308. package/src/components/form/expressions/renderExpressionToText.ts +112 -22
  309. package/src/components/form/expressions/types.ts +17 -2
  310. package/src/components/form/expressions/useExpressions.ts +5 -1
  311. package/src/components/form/expressions/useRenderExpression.ts +43 -127
  312. package/src/components/form/fields/Field.tsx +102 -20
  313. package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
  314. package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
  315. package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
  316. package/src/components/form/fields/date/Date.tsx +17 -1
  317. package/src/components/form/fields/long-string/LongString.tsx +15 -3
  318. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
  319. package/src/components/form/fields/number/Number.tsx +25 -0
  320. package/src/components/form/fields/rich-text/RichText.tsx +248 -30
  321. package/src/components/form/fields/select/Select.stories.tsx +392 -0
  322. package/src/components/form/fields/select/Select.tsx +164 -27
  323. package/src/components/form/fields/select/SelectCollection.tsx +350 -6
  324. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
  325. package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
  326. package/src/components/form/fields/template/TemplateField.tsx +825 -131
  327. package/src/components/form/fields/template/TemplateText.tsx +115 -0
  328. package/src/components/form/index.tsx +4 -0
  329. package/src/components/log/Log.stories.tsx +2 -1
  330. package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
  331. package/src/components/smartEditor/SmartEditor.tsx +41 -9
  332. package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
  333. package/src/components/smartEditor/useLspSession.ts +21 -3
  334. package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
  335. package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
  336. package/src/helpers/common.ts +7 -3
  337. package/src/helpers/dpqlDetection.ts +209 -0
  338. package/src/helpers/optionUiTypes.ts +38 -0
  339. package/src/helpers/options.ts +96 -0
  340. package/src/helpers/templateItems.ts +81 -0
  341. package/src/helpers/templateText.ts +350 -0
  342. package/src/helpers/templates.ts +94 -41
  343. package/src/helpers/validations.ts +203 -51
  344. package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
  345. package/src/hooks/useFetch/useFetch.tsx +53 -7
  346. package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
  347. package/src/hooks/useTemplates.ts +41 -0
  348. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
  349. package/src/index.tsx +14 -1
  350. package/src/stores/currentUser/currentUser.stories.tsx +2 -1
  351. package/src/stories/storyNetwork.ts +92 -0
  352. package/src/utils/fetch.ts +311 -21
  353. package/src/utils/lspClient.ts +68 -11
  354. package/src/utils/websocket.ts +20 -9
  355. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
  356. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
  357. package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
  358. package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
  359. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
  360. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
  361. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
  362. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
  363. package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
  364. package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
  365. package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
  366. package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
  367. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
  368. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
  369. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
  370. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
  371. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
  372. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
  373. package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
  374. package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
  375. package/dist/stories/Data/fileFieldArgSchema.json +0 -86
@@ -1,6 +1,6 @@
1
1
  import { ReqoreP, ReqorePanel } from '@qoretechnologies/reqore';
2
2
  import { GAP_FROM_SIZE, TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
3
- import styled from 'styled-components';
3
+ import styled, { css } from 'styled-components';
4
4
 
5
5
  // Styled primitives for the compact (read-first) rows and their editor cards.
6
6
  // Shared between FormEngine and the extracted CompactRow component.
@@ -64,7 +64,31 @@ export const StyledCompactPanel = styled(ReqorePanel)<{
64
64
  /** True when the toolbar renders no search row (hidden via `compactToolbar`,
65
65
  * self-hidden on a single-option form, or no toolbar at all). */
66
66
  $tightHeader?: boolean;
67
+ /** True when the status boxes inside pin their headers. */
68
+ $stickyBoxes?: boolean;
67
69
  }>`
70
+ /* The wrapper must not clip, or nothing INSIDE it can pin.
71
+
72
+ ReqorePanel sets its wrapper to \`overflow: hidden\` unless its own header
73
+ is sticky — and a clipping box is a scrollport, so a status box header
74
+ inside it resolves against the wrapper instead of the page and stops
75
+ pinning. That coupling is right for the panel's own header and wrong for
76
+ its descendants': a host that turns this form's toolbar off (a two-field
77
+ form inside a longer page, where a pinned search bar is the biggest thing
78
+ on screen and the least useful) was silently turning the box headers off
79
+ with it.
80
+
81
+ The clip is the panel's rounded corners, and this panel is transparent and
82
+ border-less — \`flat raised minimal\`, no background of its own — so there
83
+ is nothing for it to clip. */
84
+ ${({ $stickyBoxes }) =>
85
+ $stickyBoxes ?
86
+ css`
87
+ &&& {
88
+ overflow: visible;
89
+ }
90
+ `
91
+ : ''}
68
92
  > .reqore-panel-title {
69
93
  /* The blur + translateZ exist only to make the STICKY top-level toolbar ghost
70
94
  content beneath it; a nested sub-form's header isn't sticky, so skip them
@@ -154,7 +178,12 @@ export const StyledGroupHeaderLine = styled.span<{ $color: string }>`
154
178
  // a barely-there accent border + a ~5%-opacity tint, NOT the loud intent border a
155
179
  // stock ReqorePanel draws. `$accent` is the box's theme colour (warning/success/
156
180
  // muted).
157
- export const StyledStatusBox = styled(ReqorePanel)<{ $accent: string; $bg?: string }>`
181
+ export const StyledStatusBox = styled(ReqorePanel)<{
182
+ $accent: string;
183
+ $bg?: string;
184
+ /** The page's own background, for the PINNED header — see below. */
185
+ $surface?: string;
186
+ }>`
158
187
  &&& {
159
188
  border: 1px solid ${({ $accent }) => `${$accent}33`};
160
189
  /* $bg lets the muted "Optional" box opt into a darker, recessed surface
@@ -162,6 +191,31 @@ export const StyledStatusBox = styled(ReqorePanel)<{ $accent: string; $bg?: stri
162
191
  background: ${({ $accent, $bg }) => $bg || `${$accent}1f`};
163
192
  border-radius: 10px;
164
193
  }
194
+
195
+ /* A PINNED header needs a surface of its own.
196
+
197
+ The box's tint lives on the wrapper, behind the header, and a \`minimal\`
198
+ panel header is transparent — which is right while the header sits in the
199
+ flow over its own box, and wrong the moment it pins, because the rows then
200
+ scroll THROUGH it. Reqore's backdrop blur softens them but a 12% tint over
201
+ moving text still reads as two overlaid labels.
202
+
203
+ So repaint the pinned header as the box looks at rest: an opaque layer of
204
+ the page background with the box's own translucent tint composited on top,
205
+ which is exactly what the eye sees where the box meets the page. The
206
+ Optional box already carries an opaque \`$bg\`, so that is used as-is.
207
+
208
+ Only when \`$surface\` is supplied — i.e. only on a form that pins at all —
209
+ so an unpinned box is pixel-identical to before. */
210
+ ${({ $surface, $accent, $bg }) =>
211
+ $surface ?
212
+ css`
213
+ &&& > .reqore-panel-title {
214
+ background-color: ${$bg || $surface};
215
+ ${$bg ? '' : `background-image: linear-gradient(${$accent}1f, ${$accent}1f);`}
216
+ }
217
+ `
218
+ : ''}
165
219
  `;
166
220
 
167
221
  // "One of the below is required" cluster box — wraps the members of an unmet
@@ -35,7 +35,7 @@ export interface ICompactToolbarContext {
35
35
  // Completion meter
36
36
  invalidCount: number;
37
37
  attentionCount: number;
38
- completion: { set: number; total: number; pct: number };
38
+ completion: { set: number; done: number; total: number; pct: number };
39
39
  // Invalid-fields banner (pinned in the sticky header)
40
40
  showInvalidOnly: boolean;
41
41
  onToggleInvalidOnly: () => void;
@@ -1,4 +1,8 @@
1
- import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
1
+ import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
2
+ import {
3
+ IReqorePanelAction,
4
+ IReqorePanelSubAction,
5
+ } from '@qoretechnologies/reqore/dist/components/Panel';
2
6
  import { IQorusFormField, IQorusFormSchema } from '@qoretechnologies/ts-toolkit';
3
7
 
4
8
  /**
@@ -38,3 +42,87 @@ export const resolveOptionActions = (
38
42
 
39
43
  return (actions ?? []).filter((action): action is IReqorePanelAction => !!action);
40
44
  };
45
+
46
+ /** How many injected actions a row shows as buttons before the rest go into its menu. */
47
+ export const MAX_INLINE_OPTION_ACTIONS = 2;
48
+
49
+ /**
50
+ * Which injected actions stay as buttons, and which move into the row's menu.
51
+ *
52
+ * Everything moves on touch and on narrow viewports (`collapse`) — a button
53
+ * that waits for a hover is unreachable without one — and anything past the
54
+ * inline cap moves regardless, so a consumer injecting ten actions can never
55
+ * push the row's value out of view. Both render paths decide it here.
56
+ */
57
+ export const splitOptionActions = (
58
+ actions: IReqorePanelAction[],
59
+ collapse: boolean
60
+ ): [IReqorePanelAction[], IReqorePanelAction[]] =>
61
+ collapse ?
62
+ [[], actions]
63
+ : [actions.slice(0, MAX_INLINE_OPTION_ACTIONS), actions.slice(MAX_INLINE_OPTION_ACTIONS)];
64
+
65
+ /**
66
+ * An injected action as a row in a menu.
67
+ *
68
+ * A panel action labels itself with `label`, but an icon-only one (the IDE's
69
+ * AI-assist button) carries its name in the tooltip — and a menu row has no
70
+ * hover to fall back on.
71
+ */
72
+ export const optionActionAsMenuItem = (
73
+ action: IReqorePanelAction,
74
+ index: number
75
+ ): IReqoreDropdownItem => ({
76
+ /* A menu row's label has to be text. `tooltip` may be a tooltip OBJECT
77
+ (`IReqoreTooltip`), which is why this was cast before — an action with a
78
+ configured tooltip and no label put that object where a string belongs. */
79
+ label:
80
+ action.label ??
81
+ (typeof action.tooltip === 'string' ? action.tooltip : undefined) ??
82
+ `Action ${index + 1}`,
83
+ icon: action.icon,
84
+ intent: action.intent,
85
+ disabled: action.disabled,
86
+ className: action.className,
87
+ onClick: () => action.onClick?.(),
88
+ });
89
+
90
+ export interface IOptionRowActionsInput {
91
+ /** The consumer's actions for this option, already resolved. */
92
+ injected: IReqorePanelAction[];
93
+ /** Move every injected action into the menu (touch, narrow viewport). */
94
+ collapse: boolean;
95
+ /** The row's own secondary actions; a falsy entry is one that does not apply. */
96
+ menu: (IReqorePanelSubAction | false | null | undefined | 0 | '')[];
97
+ }
98
+
99
+ /**
100
+ * A classic form row's header actions.
101
+ *
102
+ * Injected actions stay as buttons up to the inline cap; the rest, and the
103
+ * row's own secondary actions, go into ONE ⋯ menu that is always drawn, so
104
+ * nothing the row offers is reachable only by hovering it.
105
+ */
106
+ export const optionRowActions = ({
107
+ injected,
108
+ collapse,
109
+ menu,
110
+ }: IOptionRowActionsInput): IReqorePanelAction[] => {
111
+ const [inline, overflow] = splitOptionActions(injected, collapse);
112
+ const items: IReqorePanelSubAction[] = [
113
+ ...overflow.map(optionActionAsMenuItem),
114
+ ...menu.filter((item): item is IReqorePanelSubAction => !!item),
115
+ ];
116
+
117
+ const more: IReqorePanelAction = {
118
+ icon: 'More2Fill',
119
+ size: 'tiny',
120
+ minimal: true,
121
+ flat: true,
122
+ className: 'options-item-more',
123
+ tooltip: 'More actions',
124
+ actions: items,
125
+ };
126
+
127
+ return [...inline, ...(items.length ? [more] : [])];
128
+ };
@@ -0,0 +1,24 @@
1
+ import { IQorusFormSchema, TQorusForm } from '@qoretechnologies/ts-toolkit';
2
+ import { createContext } from 'use-context-selector';
3
+
4
+ /** The form a field is standing in: what it declares, and what it currently holds. */
5
+ export interface IOptionsContext {
6
+ schema?: IQorusFormSchema;
7
+ value?: TQorusForm;
8
+ }
9
+
10
+ /**
11
+ * The surrounding form, for a renderer that has to judge one field against its
12
+ * SIBLINGS — an allowed value gated on another field's answer, for instance.
13
+ *
14
+ * It lives in a module of its own rather than in `FormEngine`, which imports
15
+ * the field renderers that read it: a renderer importing the engine back would
16
+ * close a cycle around a value created at module scope, and the context would
17
+ * be `undefined` for whichever module happened to be evaluated first.
18
+ *
19
+ * An empty default is load-bearing. `value` is `undefined` only when there is
20
+ * no form around the renderer at all — a picker mounted on its own, a story, a
21
+ * test — and a predicate about siblings has nothing to say there. Inside a form
22
+ * it is always an object, empty or not.
23
+ */
24
+ export const OptionsContext = createContext<IOptionsContext>({});
@@ -10,6 +10,7 @@ import { renderExpressionToText } from '../expressions/renderExpressionToText';
10
10
  import { IExpressionValue } from '../expressions/types';
11
11
  import yaml from 'js-yaml';
12
12
  import { formatTimeoutValue, richtextToString } from '../../../helpers/common';
13
+ import { firstDeclaredType } from '../../../helpers/optionUiTypes';
13
14
 
14
15
  /** "N noun" with naive pluralisation ("1 item", "2 items"). */
15
16
  const pluralize = (count: number, noun: string): string =>
@@ -224,7 +225,7 @@ export const shouldAutoCollapseCompactAllowedValueOption = (
224
225
  * user chooses either boolean value in compact mode, the choice is complete
225
226
  * and the inline editor can close without a separate confirmation. */
226
227
  export const isCompactBooleanOption = (schema: TQorusFormFieldSchema | undefined): boolean => {
227
- const type = (schema?.ui_type || schema?.type)?.toLowerCase();
228
+ const type = firstDeclaredType(schema?.ui_type || schema?.type)?.toLowerCase();
228
229
  return type === 'bool' || type === 'boolean';
229
230
  };
230
231
 
@@ -316,6 +317,31 @@ export const getAllowedValueImage = (
316
317
  return (match?.image as string) || undefined;
317
318
  };
318
319
 
320
+ /**
321
+ * The selected option's ICON, when it carries one and no logo — for the value
322
+ * cell.
323
+ *
324
+ * The same idea as {@link getAllowedValueImage} and read for the same reason:
325
+ * an option's mark belongs beside the value it names. Only images were rendered,
326
+ * so a picker whose entries are distinguished by ICON — an interface-kind list
327
+ * where every entry is a bare word, `Service`, `Job`, `Qog` — showed each mark
328
+ * while choosing and then dropped it the moment the row went read-only, which is
329
+ * the surface a reader spends the most time on.
330
+ *
331
+ * The image wins where an option has both: a logo is the more specific mark, and
332
+ * an icon standing behind it is a fallback rather than a second thing to draw.
333
+ */
334
+ export const getAllowedValueIcon = (
335
+ value: unknown,
336
+ schema?: TQorusFormFieldSchema
337
+ ): string | undefined => {
338
+ const match = findAllowedOption(value, schema);
339
+ if (!match || match.image) {
340
+ return undefined;
341
+ }
342
+ return (match.icon as string) || undefined;
343
+ };
344
+
319
345
  /** Clamp an RGB channel to a 0–255 integer. */
320
346
  const clampChannel = (n: number): number => Math.max(0, Math.min(255, Math.round(n)));
321
347
  const toHexPart = (n: number): string => clampChannel(n).toString(16).padStart(2, '0');
@@ -556,6 +582,36 @@ export const summariseMarkdown = (value: unknown): string => {
556
582
  return capSummary(prose);
557
583
  };
558
584
 
585
+ /**
586
+ * The expression AST a field holds, whichever shape it arrived in.
587
+ *
588
+ * An expression arrives in TWO shapes, and only the flat one used to be
589
+ * checked. At runtime the editor writes the flag onto the option
590
+ * (`{ is_expression: true, value: ast }`); a value read back from a saved draft
591
+ * carries the envelope NESTED (`{ value: { is_expression: true, value: ast } }`)
592
+ * with no flag on the option at all. Missing the nested shape sent a reloaded
593
+ * expression to the generic formatter, which printed the AST — the row showed
594
+ * `is_expression true / value / exp + / args 1 2` where the expression should
595
+ * be.
596
+ *
597
+ * Exported because the SUMMARY is not the only place that has to know. The AST
598
+ * is how an expression is stored, not what it is, so nothing may draw it as
599
+ * data: `CompactRow` asks this before deciding a hash-shaped value has earned a
600
+ * structured inset. Answering it in one place is what keeps the row's two
601
+ * halves — the line and the inset under it — talking about the same value.
602
+ */
603
+ export const getExpressionAst = (option?: {
604
+ is_expression?: boolean;
605
+ value?: unknown;
606
+ }): IExpressionValue | undefined =>
607
+ option?.is_expression ? (option?.value as IExpressionValue | undefined)
608
+ : (
609
+ (option?.value as { is_expression?: boolean; value?: IExpressionValue } | undefined)
610
+ ?.is_expression
611
+ ) ?
612
+ (option?.value as { value?: IExpressionValue }).value
613
+ : undefined;
614
+
559
615
  export const formatOptionValue = (
560
616
  option?: IQorusFormField,
561
617
  schema?: TQorusFormFieldSchema
@@ -572,11 +628,13 @@ export const formatOptionValue = (
572
628
  return '••••••';
573
629
  }
574
630
 
575
- if (option?.is_expression) {
631
+ const expressionAst = getExpressionAst(option);
632
+
633
+ if (expressionAst !== undefined) {
576
634
  // Offline summary of the {exp,args} AST already in the form value — the
577
635
  // same client-side renderer the editor's "Explain" seam falls back to when
578
636
  // the LSP is unreachable. The drill-in editor shows the canonical DPQL.
579
- return renderExpressionToText(option?.value as IExpressionValue | undefined) || 'Expression';
637
+ return renderExpressionToText(expressionAst) || 'Expression';
580
638
  }
581
639
 
582
640
  // schema-definition is stored as a hash envelope; summarise it as the schema
@@ -792,7 +850,10 @@ export const getOptionGroupLabel = (
792
850
  /** Completion summary for the read-first progress meter. */
793
851
  export interface IReadFirstCompletion {
794
852
  total: number;
853
+ /** Fields holding a value, whether or not anything is wrong with it. */
795
854
  set: number;
855
+ /** Fields holding a value that nothing is flagging — what the meter fills. */
856
+ done: number;
796
857
  pct: number;
797
858
  }
798
859
 
@@ -831,14 +892,33 @@ export const getReadFirstBucket = (status: TReadFirstStatus): 'attention' | 'set
831
892
 
832
893
  /** Count how many of the shown options have a value set, for the progress meter. */
833
894
  export const getReadFirstCompletion = (
834
- shownOptions: Record<string, IQorusFormField | undefined> = {}
895
+ shownOptions: Record<string, IQorusFormField | undefined> = {},
896
+ /**
897
+ * Whether the form flags this field as needing attention. Optional: without
898
+ * it the meter falls back to counting values alone, which is what a caller
899
+ * with no bucketing of its own can honestly say.
900
+ */
901
+ needsAttention?: (name: string) => boolean
835
902
  ): IReadFirstCompletion => {
836
903
  const names = Object.keys(shownOptions);
837
904
  const total = names.length;
838
- const set = names.filter((name) => !isOptionValueEmpty(shownOptions[name]?.value)).length;
839
- const pct = total ? Math.round((set / total) * 100) : 0;
840
-
841
- return { total, set, pct };
905
+ const hasValue = (name: string) => !isOptionValueEmpty(shownOptions[name]?.value);
906
+ const set = names.filter(hasValue).length;
907
+ /* "Set" and "needs attention" are not opposites. A field can hold a value and
908
+ still be wrong — one that fails validation, or one the host has flagged
909
+ because something INSIDE it is unfinished — and counting those as progress
910
+ is how a form comes to report 100% while the box beneath it says a field
911
+ needs attention.
912
+
913
+ It also cost the meter its amber run: the bar draws attention from `set%`
914
+ onward, so a field counted in both pushed that run past the right edge,
915
+ where `overflow: hidden` swallowed it. The one visual signal that something
916
+ was outstanding was the one thing the double-count hid. */
917
+ const done =
918
+ needsAttention ? names.filter((name) => hasValue(name) && !needsAttention(name)).length : set;
919
+ const pct = total ? Math.round((done / total) * 100) : 0;
920
+
921
+ return { total, set, done, pct };
842
922
  };
843
923
 
844
924
  /** What the "first field to fix" selector needs to know about one field.
@@ -1,4 +1,37 @@
1
- import { TQorusType } from '@qoretechnologies/ts-toolkit';
1
+ import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
2
+ import { firstDeclaredType } from '../../../helpers/optionUiTypes';
3
+
4
+ /**
5
+ * A field schema that carries a template list of its own.
6
+ *
7
+ * `templates` is deliberately NOT added to `TQorusFormFieldSchema`: that type is
8
+ * owned by ts-toolkit and shared with consumers that have no notion of a form
9
+ * engine, so the capability is read through this narrow view instead of widening
10
+ * the shared type from here. A schema that sets it is offering a list scoped to
11
+ * one field rather than to the whole form.
12
+ */
13
+ export type TFieldWithOwnTemplates = {
14
+ templates?: IReqoreFormTemplates;
15
+ /**
16
+ * The token grammar this field's value is WRITTEN in.
17
+ *
18
+ * A host may spell a reference in a grammar that is not reqraft's — a Qorus
19
+ * test writes `$._case.mode`, which has no `$key:` in it at all — and a field
20
+ * that speaks one says so here, exactly as it hands down its own `templates`.
21
+ * Declaring it on the schema is what lets every surface that draws the value
22
+ * agree about it, instead of printing the raw token the author never typed.
23
+ *
24
+ * Both directions read it: the read-only renderings
25
+ * (`templateTextSegments`) and the rich-text editor a row opens
26
+ * (`templateTextToNodes`, through `RichTextFormField`'s `templateToken`
27
+ * prop). They resolve references with the same code, so a row and the editor
28
+ * it opens cannot disagree about what one is.
29
+ *
30
+ * A `RegExp` or its source: a schema that crossed a serialization boundary
31
+ * carries the string.
32
+ */
33
+ templateToken?: RegExp | string;
34
+ };
2
35
 
3
36
  /**
4
37
  * A schema entry carries two type-ish keys with different jobs:
@@ -41,6 +74,15 @@ export const BUILT_IN_RENDERER_ONLY_UI_TYPES: readonly string[] = [
41
74
  'tool-catalog',
42
75
  ];
43
76
 
77
+ /**
78
+ * A `ui_type` as the predicate takes it: any name, not only a `TQorusType`.
79
+ * The names it exists to recognise are open-ended — `cron` is not a
80
+ * `TQorusType`, and neither is an editor a consumer declares — so typing the
81
+ * argument as the shared union made the predicate uncallable for exactly the
82
+ * values it is for.
83
+ */
84
+ export type TRendererTypeName = string | readonly string[];
85
+
44
86
  /**
45
87
  * Build the renderer-only predicate for one FormEngine instance.
46
88
  *
@@ -52,11 +94,13 @@ export const BUILT_IN_RENDERER_ONLY_UI_TYPES: readonly string[] = [
52
94
  */
53
95
  export const createRendererOnlyUiTypeCheck = (
54
96
  extraTypes?: readonly string[]
55
- ): ((type?: TQorusType | TQorusType[]) => boolean) => {
97
+ ): ((type?: TRendererTypeName) => boolean) => {
56
98
  const known = new Set<string>([...BUILT_IN_RENDERER_ONLY_UI_TYPES, ...(extraTypes ?? [])]);
57
99
 
58
- return (type?: TQorusType | TQorusType[]): boolean =>
59
- typeof type === 'string' && known.has(type);
100
+ return (type?: TRendererTypeName): boolean => {
101
+ const declared = firstDeclaredType(type);
102
+ return declared !== undefined && known.has(declared);
103
+ };
60
104
  };
61
105
 
62
106
  /** The default predicate — built-ins only, for module-scope callers with no props. */
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Which control an open compact row hands the caret to.
3
+ *
4
+ * A row's editor is whatever the field's type asks for, and only some of those
5
+ * take typed text. A pick-one renders `ReqoreCheckbox`es (a `[tabindex]`
6
+ * element), a selector renders a `<button>`, a code field a `[contenteditable]`
7
+ * — so a scan that looked for `input`, `textarea` and `[contenteditable]` alone
8
+ * found nothing on most of the first questions a form asks ("which kind?",
9
+ * "which connection?", "which interface?"). The row opened and the caret stayed
10
+ * on whatever made it open.
11
+ *
12
+ * Text entry wins when the row offers it. Every editor that takes typing also
13
+ * draws affordances around it — a template picker, a type menu, a clear button
14
+ * — and those come first in document order often enough that "the first
15
+ * focusable element" would answer a text field with a button beside it.
16
+ */
17
+
18
+ /** Controls the reader types into. */
19
+ const TEXT_ENTRY_SELECTOR =
20
+ 'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), [contenteditable="true"]';
21
+
22
+ /**
23
+ * Everything else a row can offer. `[tabindex]` is what carries reqore's own
24
+ * controls: a checkbox is an icon element made focusable, not an `<input>`.
25
+ */
26
+ const OTHER_CONTROL_SELECTOR =
27
+ 'button:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
28
+
29
+ /**
30
+ * Subtrees inside the row's value cell that are NOT this field's editor: a
31
+ * sibling field absorbed into this row (a different field, which the engine did
32
+ * not open) and the schema-message strip above the editor (guidance about the
33
+ * value, never a place to put one).
34
+ */
35
+ const NOT_THE_FIELDS_OWN_EDITOR = '.options-readfirst-absorbed, .options-readfirst-info-panel';
36
+
37
+ const belongsToTheFieldsOwnEditor = (container: HTMLElement, element: Element): boolean => {
38
+ const foreign = element.closest(NOT_THE_FIELDS_OWN_EDITOR);
39
+ // `closest` walks past the container too, so a match only disqualifies the
40
+ // element when it is inside THIS container.
41
+ return !foreign || !container.contains(foreign);
42
+ };
43
+
44
+ const firstMatch = (container: HTMLElement, selector: string): HTMLElement | undefined => {
45
+ const matches = Array.from(container.querySelectorAll<HTMLElement>(selector));
46
+ return matches.find(
47
+ (element) =>
48
+ belongsToTheFieldsOwnEditor(container, element) &&
49
+ // An element hidden from assistive technology is not a destination for a
50
+ // caret either — reqore draws decorative, focusable-looking chrome that
51
+ // way.
52
+ !element.closest('[aria-hidden="true"]')
53
+ );
54
+ };
55
+
56
+ /**
57
+ * The control an open row should focus, or `undefined` when it offers none —
58
+ * a read-only preview, or an editor that has not mounted yet (see the caller,
59
+ * which watches for it to arrive).
60
+ *
61
+ * @param container the row's value cell
62
+ */
63
+ export const findRowFocusTarget = (
64
+ container: HTMLElement | null | undefined
65
+ ): HTMLElement | undefined => {
66
+ if (!container) {
67
+ return undefined;
68
+ }
69
+ return (
70
+ firstMatch(container, TEXT_ENTRY_SELECTOR) ?? firstMatch(container, OTHER_CONTROL_SELECTOR)
71
+ );
72
+ };
73
+
74
+ /**
75
+ * Controls whose ONLY behaviour is to reveal the choices.
76
+ *
77
+ * A row that opens hands the caret to the control it offers, and for most
78
+ * editors that is the end of it: a text box takes typing, a pick-one's
79
+ * checkboxes take a click. A PICKER is different — its closed state prints the
80
+ * value it already holds, which is the very thing the read row printed, so
81
+ * opening the row changed nothing on screen and the author had to click a
82
+ * second time to see the list. Read-first belongs to the collapsed row; it must
83
+ * not be the answer twice.
84
+ *
85
+ * Declared with `aria-haspopup`, which is what the attribute means and is
86
+ * already the contract with assistive technology — so a host's own editor opts
87
+ * in by being accessible rather than by knowing about this file. `aria-expanded`
88
+ * is respected: a picker that is already showing its list is not re-activated.
89
+ */
90
+ const PICKER_TRIGGER_SELECTOR =
91
+ '[aria-haspopup="listbox"], [aria-haspopup="dialog"], [aria-haspopup="menu"], [aria-haspopup="tree"], [aria-haspopup="grid"]';
92
+
93
+ /**
94
+ * Whether this control is a picker that is still closed — i.e. whether
95
+ * activating it is what "open the editor" means for this row.
96
+ */
97
+ export const isClosedPickerTrigger = (element: HTMLElement | null | undefined): boolean =>
98
+ !!element &&
99
+ typeof element.matches === 'function' &&
100
+ element.matches(PICKER_TRIGGER_SELECTOR) &&
101
+ element.getAttribute('aria-expanded') !== 'true';