@qoretechnologies/reqraft 0.10.66-pr.145.gcb1e59d → 0.10.66

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 (913) hide show
  1. package/README.md +53 -1
  2. package/dist/components/Description/AllowedValuesHelp.d.ts +20 -0
  3. package/dist/components/Description/AllowedValuesHelp.d.ts.map +1 -0
  4. package/dist/components/Description/AllowedValuesHelp.js +86 -0
  5. package/dist/components/Description/AllowedValuesHelp.js.map +1 -0
  6. package/dist/components/Description/describedControl.d.ts +54 -0
  7. package/dist/components/Description/describedControl.d.ts.map +1 -0
  8. package/dist/components/Description/describedControl.js +81 -0
  9. package/dist/components/Description/describedControl.js.map +1 -0
  10. package/dist/components/Description/index.d.ts.map +1 -1
  11. package/dist/components/Description/index.js +3 -2
  12. package/dist/components/Description/index.js.map +1 -1
  13. package/dist/components/colorValue/colorValue.d.ts +59 -0
  14. package/dist/components/colorValue/colorValue.d.ts.map +1 -0
  15. package/dist/components/colorValue/colorValue.js +245 -0
  16. package/dist/components/colorValue/colorValue.js.map +1 -0
  17. package/dist/components/colorValue/index.d.ts +34 -0
  18. package/dist/components/colorValue/index.d.ts.map +1 -0
  19. package/dist/components/colorValue/index.js +136 -0
  20. package/dist/components/colorValue/index.js.map +1 -0
  21. package/dist/components/colorValue/labels.d.ts +12 -0
  22. package/dist/components/colorValue/labels.d.ts.map +1 -0
  23. package/dist/components/colorValue/labels.js +11 -0
  24. package/dist/components/colorValue/labels.js.map +1 -0
  25. package/dist/components/composer/Composer.d.ts +1 -1
  26. package/dist/components/composer/Composer.d.ts.map +1 -1
  27. package/dist/components/composer/Composer.js +2 -2
  28. package/dist/components/composer/Composer.js.map +1 -1
  29. package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
  30. package/dist/components/dpqlEditor/DpqlEditor.js +156 -10
  31. package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
  32. package/dist/components/dpqlEditor/dpqlCompletions.d.ts +39 -0
  33. package/dist/components/dpqlEditor/dpqlCompletions.d.ts.map +1 -0
  34. package/dist/components/dpqlEditor/dpqlCompletions.js +294 -0
  35. package/dist/components/dpqlEditor/dpqlCompletions.js.map +1 -0
  36. package/dist/components/dpqlEditor/dpqlHelpers.d.ts +54 -5
  37. package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
  38. package/dist/components/dpqlEditor/dpqlHelpers.js +420 -63
  39. package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
  40. package/dist/components/dpqlEditor/dpqlInserter.d.ts.map +1 -1
  41. package/dist/components/dpqlEditor/dpqlInserter.js +67 -29
  42. package/dist/components/dpqlEditor/dpqlInserter.js.map +1 -1
  43. package/dist/components/dpqlEditor/dpqlTags.d.ts +17 -7
  44. package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
  45. package/dist/components/dpqlEditor/dpqlTags.js +44 -17
  46. package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
  47. package/dist/components/dpqlEditor/types.d.ts +94 -8
  48. package/dist/components/dpqlEditor/types.d.ts.map +1 -1
  49. package/dist/components/dpqlEditor/useDpqlSession.d.ts +7 -2
  50. package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
  51. package/dist/components/dpqlEditor/useDpqlSession.js +49 -17
  52. package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
  53. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  54. package/dist/components/form/engine/CompactRow.js +336 -243
  55. package/dist/components/form/engine/CompactRow.js.map +1 -1
  56. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  57. package/dist/components/form/engine/CompactToolbar.js +20 -57
  58. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  59. package/dist/components/form/engine/FormEngine.d.ts +67 -59
  60. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  61. package/dist/components/form/engine/FormEngine.js +606 -409
  62. package/dist/components/form/engine/FormEngine.js.map +1 -1
  63. package/dist/components/form/engine/HiddenFieldsNote.d.ts +28 -0
  64. package/dist/components/form/engine/HiddenFieldsNote.d.ts.map +1 -0
  65. package/dist/components/form/engine/HiddenFieldsNote.js +32 -0
  66. package/dist/components/form/engine/HiddenFieldsNote.js.map +1 -0
  67. package/dist/components/form/engine/OptionFieldMessages.d.ts +2 -18
  68. package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
  69. package/dist/components/form/engine/OptionFieldMessages.js +28 -25
  70. package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
  71. package/dist/components/form/engine/OptionsHelpDialog.d.ts.map +1 -1
  72. package/dist/components/form/engine/OptionsHelpDialog.js +19 -2
  73. package/dist/components/form/engine/OptionsHelpDialog.js.map +1 -1
  74. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts +2 -2
  75. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  76. package/dist/components/form/engine/_structuredData/SchemaDataView.js +11 -31
  77. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  78. package/dist/components/form/engine/compactRowContext.d.ts +30 -6
  79. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  80. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  81. package/dist/components/form/engine/compactRowStyles.d.ts +56 -31
  82. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  83. package/dist/components/form/engine/compactRowStyles.js +121 -110
  84. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  85. package/dist/components/form/engine/compactToolbarColors.d.ts +16 -0
  86. package/dist/components/form/engine/compactToolbarColors.d.ts.map +1 -0
  87. package/dist/components/form/engine/compactToolbarColors.js +21 -0
  88. package/dist/components/form/engine/compactToolbarColors.js.map +1 -0
  89. package/dist/components/form/engine/compactToolbarContext.d.ts +0 -3
  90. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  91. package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
  92. package/dist/components/form/engine/inheritProps.d.ts +29 -0
  93. package/dist/components/form/engine/inheritProps.d.ts.map +1 -0
  94. package/dist/components/form/engine/inheritProps.js +43 -0
  95. package/dist/components/form/engine/inheritProps.js.map +1 -0
  96. package/dist/components/form/engine/operatorSummary.d.ts +26 -0
  97. package/dist/components/form/engine/operatorSummary.d.ts.map +1 -0
  98. package/dist/components/form/engine/operatorSummary.js +35 -0
  99. package/dist/components/form/engine/operatorSummary.js.map +1 -0
  100. package/dist/components/form/engine/readFirst.d.ts +26 -22
  101. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  102. package/dist/components/form/engine/readFirst.js +79 -115
  103. package/dist/components/form/engine/readFirst.js.map +1 -1
  104. package/dist/components/form/engine/readFirstColors.d.ts +30 -0
  105. package/dist/components/form/engine/readFirstColors.d.ts.map +1 -0
  106. package/dist/components/form/engine/readFirstColors.js +51 -0
  107. package/dist/components/form/engine/readFirstColors.js.map +1 -0
  108. package/dist/components/form/engine/rowFocus.d.ts +3 -9
  109. package/dist/components/form/engine/rowFocus.d.ts.map +1 -1
  110. package/dist/components/form/engine/rowFocus.js +30 -19
  111. package/dist/components/form/engine/rowFocus.js.map +1 -1
  112. package/dist/components/form/engine/typeChoices.d.ts +1 -1
  113. package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -1
  114. package/dist/components/form/engine/variants/VariantCalmTable.js +2 -1
  115. package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -1
  116. package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -1
  117. package/dist/components/form/engine/variants/VariantCards.js +2 -1
  118. package/dist/components/form/engine/variants/VariantCards.js.map +1 -1
  119. package/dist/components/form/engine/variants/VariantFocus.js +1 -1
  120. package/dist/components/form/engine/variants/VariantFocus.js.map +1 -1
  121. package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -1
  122. package/dist/components/form/engine/variants/VariantMinimal.js +2 -1
  123. package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -1
  124. package/dist/components/form/engine/variants/variantParts.d.ts +1 -1
  125. package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -1
  126. package/dist/components/form/engine/variants/variantParts.js +25 -17
  127. package/dist/components/form/engine/variants/variantParts.js.map +1 -1
  128. package/dist/components/form/expressions/DpqlRendering.d.ts +1 -1
  129. package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -1
  130. package/dist/components/form/expressions/DpqlRendering.js +7 -6
  131. package/dist/components/form/expressions/DpqlRendering.js.map +1 -1
  132. package/dist/components/form/expressions/ExpressionField.d.ts +67 -3
  133. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  134. package/dist/components/form/expressions/ExpressionField.js +536 -114
  135. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  136. package/dist/components/form/expressions/argumentPresence.d.ts +14 -0
  137. package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -1
  138. package/dist/components/form/expressions/argumentPresence.js +16 -1
  139. package/dist/components/form/expressions/argumentPresence.js.map +1 -1
  140. package/dist/components/form/expressions/builder/argumentDetail.d.ts.map +1 -1
  141. package/dist/components/form/expressions/builder/argumentDetail.js +4 -1
  142. package/dist/components/form/expressions/builder/argumentDetail.js.map +1 -1
  143. package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
  144. package/dist/components/form/expressions/builder/argumentLabel.js +30 -11
  145. package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
  146. package/dist/components/form/expressions/builder/argumentWrapper.d.ts.map +1 -1
  147. package/dist/components/form/expressions/builder/argumentWrapper.js +3 -1
  148. package/dist/components/form/expressions/builder/argumentWrapper.js.map +1 -1
  149. package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.d.ts +9 -0
  150. package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.d.ts.map +1 -1
  151. package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.js +30 -27
  152. package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.js.map +1 -1
  153. package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.d.ts +1 -1
  154. package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.d.ts.map +1 -1
  155. package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.js +7 -5
  156. package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.js.map +1 -1
  157. package/dist/components/form/expressions/builder/index.d.ts +34 -2
  158. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  159. package/dist/components/form/expressions/builder/index.js +170 -80
  160. package/dist/components/form/expressions/builder/index.js.map +1 -1
  161. package/dist/components/form/expressions/builder/item.d.ts +6 -2
  162. package/dist/components/form/expressions/builder/item.d.ts.map +1 -1
  163. package/dist/components/form/expressions/builder/item.js +19 -5
  164. package/dist/components/form/expressions/builder/item.js.map +1 -1
  165. package/dist/components/form/expressions/builder/renderTemplate.js +1 -1
  166. package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
  167. package/dist/components/form/expressions/dpqlMockRecordPath.d.ts +11 -0
  168. package/dist/components/form/expressions/dpqlMockRecordPath.d.ts.map +1 -0
  169. package/dist/components/form/expressions/dpqlMockRecordPath.js +254 -0
  170. package/dist/components/form/expressions/dpqlMockRecordPath.js.map +1 -0
  171. package/dist/components/form/expressions/expressionOperands.d.ts +16 -0
  172. package/dist/components/form/expressions/expressionOperands.d.ts.map +1 -0
  173. package/dist/components/form/expressions/expressionOperands.js +97 -0
  174. package/dist/components/form/expressions/expressionOperands.js.map +1 -0
  175. package/dist/components/form/expressions/fieldPath.d.ts +24 -0
  176. package/dist/components/form/expressions/fieldPath.d.ts.map +1 -0
  177. package/dist/components/form/expressions/fieldPath.js +152 -0
  178. package/dist/components/form/expressions/fieldPath.js.map +1 -0
  179. package/dist/components/form/expressions/loneValue.d.ts +17 -0
  180. package/dist/components/form/expressions/loneValue.d.ts.map +1 -0
  181. package/dist/components/form/expressions/loneValue.js +33 -0
  182. package/dist/components/form/expressions/loneValue.js.map +1 -0
  183. package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
  184. package/dist/components/form/expressions/mockExpressions.js +22 -5
  185. package/dist/components/form/expressions/mockExpressions.js.map +1 -1
  186. package/dist/components/form/expressions/renderExpressionToText.d.ts +2 -0
  187. package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
  188. package/dist/components/form/expressions/renderExpressionToText.js +37 -25
  189. package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
  190. package/dist/components/form/expressions/textOfExpression.d.ts +18 -0
  191. package/dist/components/form/expressions/textOfExpression.d.ts.map +1 -0
  192. package/dist/components/form/expressions/textOfExpression.js +49 -0
  193. package/dist/components/form/expressions/textOfExpression.js.map +1 -0
  194. package/dist/components/form/expressions/types.d.ts +12 -0
  195. package/dist/components/form/expressions/types.d.ts.map +1 -1
  196. package/dist/components/form/expressions/types.js +6 -1
  197. package/dist/components/form/expressions/types.js.map +1 -1
  198. package/dist/components/form/expressions/useExpressionType.d.ts +26 -0
  199. package/dist/components/form/expressions/useExpressionType.d.ts.map +1 -0
  200. package/dist/components/form/expressions/useExpressionType.js +181 -0
  201. package/dist/components/form/expressions/useExpressionType.js.map +1 -0
  202. package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
  203. package/dist/components/form/expressions/useRenderExpression.js +13 -9
  204. package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
  205. package/dist/components/form/expressions/wildcardExpressions.d.ts +27 -0
  206. package/dist/components/form/expressions/wildcardExpressions.d.ts.map +1 -0
  207. package/dist/components/form/expressions/wildcardExpressions.js +101 -0
  208. package/dist/components/form/expressions/wildcardExpressions.js.map +1 -0
  209. package/dist/components/form/fields/Field.d.ts.map +1 -1
  210. package/dist/components/form/fields/Field.js +39 -18
  211. package/dist/components/form/fields/Field.js.map +1 -1
  212. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +0 -7
  213. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  214. package/dist/components/form/fields/allowed-values/AllowedValues.js +46 -35
  215. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  216. package/dist/components/form/fields/allowed-values/choicePicture.d.ts +37 -0
  217. package/dist/components/form/fields/allowed-values/choicePicture.d.ts.map +1 -0
  218. package/dist/components/form/fields/allowed-values/choicePicture.js +64 -0
  219. package/dist/components/form/fields/allowed-values/choicePicture.js.map +1 -0
  220. package/dist/components/form/fields/array/ArrayAuto.d.ts.map +1 -1
  221. package/dist/components/form/fields/array/ArrayAuto.js +24 -0
  222. package/dist/components/form/fields/array/ArrayAuto.js.map +1 -1
  223. package/dist/components/form/fields/array/ArrayAutoField.d.ts.map +1 -1
  224. package/dist/components/form/fields/array/ArrayAutoField.js +36 -13
  225. package/dist/components/form/fields/array/ArrayAutoField.js.map +1 -1
  226. package/dist/components/form/fields/auto/AutoFormField.d.ts +30 -1
  227. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  228. package/dist/components/form/fields/auto/AutoFormField.js +123 -79
  229. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  230. package/dist/components/form/fields/auto/hostEditorValue.d.ts +30 -0
  231. package/dist/components/form/fields/auto/hostEditorValue.d.ts.map +1 -0
  232. package/dist/components/form/fields/auto/hostEditorValue.js +53 -0
  233. package/dist/components/form/fields/auto/hostEditorValue.js.map +1 -0
  234. package/dist/components/form/fields/binary/Binary.d.ts.map +1 -1
  235. package/dist/components/form/fields/binary/Binary.js +23 -3
  236. package/dist/components/form/fields/binary/Binary.js.map +1 -1
  237. package/dist/components/form/fields/boolean/Boolean.d.ts +5 -0
  238. package/dist/components/form/fields/boolean/Boolean.d.ts.map +1 -1
  239. package/dist/components/form/fields/boolean/Boolean.js +9 -1
  240. package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
  241. package/dist/components/form/fields/color/Color.d.ts +103 -16
  242. package/dist/components/form/fields/color/Color.d.ts.map +1 -1
  243. package/dist/components/form/fields/color/Color.js +616 -16
  244. package/dist/components/form/fields/color/Color.js.map +1 -1
  245. package/dist/components/form/fields/emittedValues.d.ts +34 -3
  246. package/dist/components/form/fields/emittedValues.d.ts.map +1 -1
  247. package/dist/components/form/fields/emittedValues.js +24 -1
  248. package/dist/components/form/fields/emittedValues.js.map +1 -1
  249. package/dist/components/form/fields/file/File.d.ts +1 -1
  250. package/dist/components/form/fields/file/File.d.ts.map +1 -1
  251. package/dist/components/form/fields/file/File.js +4 -2
  252. package/dist/components/form/fields/file/File.js.map +1 -1
  253. package/dist/components/form/fields/long-string/LongString.d.ts +1 -7
  254. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  255. package/dist/components/form/fields/long-string/LongString.js +30 -13
  256. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  257. package/dist/components/form/fields/markdown/MarkdownView.js +1 -1
  258. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  259. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +10 -7
  260. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  261. package/dist/components/form/fields/number/Number.d.ts +7 -4
  262. package/dist/components/form/fields/number/Number.d.ts.map +1 -1
  263. package/dist/components/form/fields/number/Number.js +45 -18
  264. package/dist/components/form/fields/number/Number.js.map +1 -1
  265. package/dist/components/form/fields/object/Object.d.ts +1 -1
  266. package/dist/components/form/fields/object/Object.d.ts.map +1 -1
  267. package/dist/components/form/fields/object/Object.js +4 -2
  268. package/dist/components/form/fields/object/Object.js.map +1 -1
  269. package/dist/components/form/fields/pendingEdit.d.ts +52 -0
  270. package/dist/components/form/fields/pendingEdit.d.ts.map +1 -0
  271. package/dist/components/form/fields/pendingEdit.js +79 -0
  272. package/dist/components/form/fields/pendingEdit.js.map +1 -0
  273. package/dist/components/form/fields/radio-group/RadioGroup.d.ts.map +1 -1
  274. package/dist/components/form/fields/radio-group/RadioGroup.js +8 -13
  275. package/dist/components/form/fields/radio-group/RadioGroup.js.map +1 -1
  276. package/dist/components/form/fields/rich-text/RichText.d.ts +20 -1
  277. package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
  278. package/dist/components/form/fields/rich-text/RichText.js +426 -91
  279. package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
  280. package/dist/components/form/fields/rich-text/useTemplateTags.d.ts +18 -0
  281. package/dist/components/form/fields/rich-text/useTemplateTags.d.ts.map +1 -0
  282. package/dist/components/form/fields/rich-text/useTemplateTags.js +80 -0
  283. package/dist/components/form/fields/rich-text/useTemplateTags.js.map +1 -0
  284. package/dist/components/form/fields/schema-definition/CatalogLeafForm.d.ts.map +1 -1
  285. package/dist/components/form/fields/schema-definition/CatalogLeafForm.js +4 -1
  286. package/dist/components/form/fields/schema-definition/CatalogLeafForm.js.map +1 -1
  287. package/dist/components/form/fields/schema-definition/CatalogNodeEditor.d.ts.map +1 -1
  288. package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js +20 -5
  289. package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js.map +1 -1
  290. package/dist/components/form/fields/schema-definition/TableEditor.d.ts.map +1 -1
  291. package/dist/components/form/fields/schema-definition/TableEditor.js +10 -2
  292. package/dist/components/form/fields/schema-definition/TableEditor.js.map +1 -1
  293. package/dist/components/form/fields/schema-definition/index.d.ts.map +1 -1
  294. package/dist/components/form/fields/schema-definition/index.js +8 -4
  295. package/dist/components/form/fields/schema-definition/index.js.map +1 -1
  296. package/dist/components/form/fields/select/Select.d.ts +10 -1
  297. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  298. package/dist/components/form/fields/select/Select.js +28 -14
  299. package/dist/components/form/fields/select/Select.js.map +1 -1
  300. package/dist/components/form/fields/select/SelectCollection.d.ts +5 -0
  301. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  302. package/dist/components/form/fields/select/SelectCollection.js +25 -12
  303. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  304. package/dist/components/form/fields/select/unavailableRow.d.ts +23 -0
  305. package/dist/components/form/fields/select/unavailableRow.d.ts.map +1 -0
  306. package/dist/components/form/fields/select/unavailableRow.js +55 -0
  307. package/dist/components/form/fields/select/unavailableRow.js.map +1 -0
  308. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +11 -5
  309. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  310. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +21 -5
  311. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  312. package/dist/components/form/fields/template/TemplateBrowser.d.ts +16 -0
  313. package/dist/components/form/fields/template/TemplateBrowser.d.ts.map +1 -0
  314. package/dist/components/form/fields/template/TemplateBrowser.js +89 -0
  315. package/dist/components/form/fields/template/TemplateBrowser.js.map +1 -0
  316. package/dist/components/form/fields/template/TemplateField.d.ts +105 -1
  317. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  318. package/dist/components/form/fields/template/TemplateField.js +706 -168
  319. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  320. package/dist/components/form/fields/template/TemplatePicker.d.ts +54 -0
  321. package/dist/components/form/fields/template/TemplatePicker.d.ts.map +1 -0
  322. package/dist/components/form/fields/template/TemplatePicker.js +459 -0
  323. package/dist/components/form/fields/template/TemplatePicker.js.map +1 -0
  324. package/dist/components/form/fields/template/TemplateText.d.ts +0 -17
  325. package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -1
  326. package/dist/components/form/fields/template/TemplateText.js +22 -16
  327. package/dist/components/form/fields/template/TemplateText.js.map +1 -1
  328. package/dist/components/form/fields/template/referenceNames.d.ts +63 -0
  329. package/dist/components/form/fields/template/referenceNames.d.ts.map +1 -0
  330. package/dist/components/form/fields/template/referenceNames.js +69 -0
  331. package/dist/components/form/fields/template/referenceNames.js.map +1 -0
  332. package/dist/components/form/fields/template/writtenAsText.d.ts +61 -0
  333. package/dist/components/form/fields/template/writtenAsText.d.ts.map +1 -0
  334. package/dist/components/form/fields/template/writtenAsText.js +204 -0
  335. package/dist/components/form/fields/template/writtenAsText.js.map +1 -0
  336. package/dist/components/form/index.d.ts +12 -2
  337. package/dist/components/form/index.d.ts.map +1 -1
  338. package/dist/components/form/index.js +25 -3
  339. package/dist/components/form/index.js.map +1 -1
  340. package/dist/components/form/typeSentenceForm.d.ts +17 -0
  341. package/dist/components/form/typeSentenceForm.d.ts.map +1 -0
  342. package/dist/components/form/typeSentenceForm.js +54 -0
  343. package/dist/components/form/typeSentenceForm.js.map +1 -0
  344. package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -1
  345. package/dist/components/imageLightbox/ImageLightbox.js +19 -9
  346. package/dist/components/imageLightbox/ImageLightbox.js.map +1 -1
  347. package/dist/components/log/Log.d.ts.map +1 -1
  348. package/dist/components/log/Log.js +20 -8
  349. package/dist/components/log/Log.js.map +1 -1
  350. package/dist/components/menu/Menu.d.ts.map +1 -1
  351. package/dist/components/menu/Menu.js +19 -10
  352. package/dist/components/menu/Menu.js.map +1 -1
  353. package/dist/components/smartEditor/CompletionMenu.d.ts +53 -0
  354. package/dist/components/smartEditor/CompletionMenu.d.ts.map +1 -0
  355. package/dist/components/smartEditor/CompletionMenu.js +302 -0
  356. package/dist/components/smartEditor/CompletionMenu.js.map +1 -0
  357. package/dist/components/smartEditor/MarkdownDoc.d.ts +1 -1
  358. package/dist/components/smartEditor/MarkdownDoc.js +3 -3
  359. package/dist/components/smartEditor/MarkdownDoc.js.map +1 -1
  360. package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
  361. package/dist/components/smartEditor/SmartEditor.js +616 -190
  362. package/dist/components/smartEditor/SmartEditor.js.map +1 -1
  363. package/dist/components/smartEditor/completionStyle.d.ts +28 -0
  364. package/dist/components/smartEditor/completionStyle.d.ts.map +1 -0
  365. package/dist/components/smartEditor/completionStyle.js +79 -0
  366. package/dist/components/smartEditor/completionStyle.js.map +1 -0
  367. package/dist/components/smartEditor/contextTitle.d.ts +10 -0
  368. package/dist/components/smartEditor/contextTitle.d.ts.map +1 -0
  369. package/dist/components/smartEditor/contextTitle.js +21 -0
  370. package/dist/components/smartEditor/contextTitle.js.map +1 -0
  371. package/dist/components/smartEditor/editingAids.d.ts +64 -0
  372. package/dist/components/smartEditor/editingAids.d.ts.map +1 -0
  373. package/dist/components/smartEditor/editingAids.js +213 -0
  374. package/dist/components/smartEditor/editingAids.js.map +1 -0
  375. package/dist/components/smartEditor/index.d.ts +6 -1
  376. package/dist/components/smartEditor/index.d.ts.map +1 -1
  377. package/dist/components/smartEditor/index.js +19 -1
  378. package/dist/components/smartEditor/index.js.map +1 -1
  379. package/dist/components/smartEditor/templateCompletions.d.ts +98 -0
  380. package/dist/components/smartEditor/templateCompletions.d.ts.map +1 -0
  381. package/dist/components/smartEditor/templateCompletions.js +380 -0
  382. package/dist/components/smartEditor/templateCompletions.js.map +1 -0
  383. package/dist/components/smartEditor/typedReferences.d.ts +34 -0
  384. package/dist/components/smartEditor/typedReferences.d.ts.map +1 -0
  385. package/dist/components/smartEditor/typedReferences.js +179 -0
  386. package/dist/components/smartEditor/typedReferences.js.map +1 -0
  387. package/dist/components/smartEditor/types.d.ts +116 -2
  388. package/dist/components/smartEditor/types.d.ts.map +1 -1
  389. package/dist/components/smartEditor/useEditingAids.d.ts +38 -0
  390. package/dist/components/smartEditor/useEditingAids.d.ts.map +1 -0
  391. package/dist/components/smartEditor/useEditingAids.js +392 -0
  392. package/dist/components/smartEditor/useEditingAids.js.map +1 -0
  393. package/dist/components/smartEditor/useLspAutocomplete.d.ts +106 -3
  394. package/dist/components/smartEditor/useLspAutocomplete.d.ts.map +1 -1
  395. package/dist/components/smartEditor/useLspAutocomplete.js +592 -149
  396. package/dist/components/smartEditor/useLspAutocomplete.js.map +1 -1
  397. package/dist/components/smartEditor/useLspHover.d.ts +33 -7
  398. package/dist/components/smartEditor/useLspHover.d.ts.map +1 -1
  399. package/dist/components/smartEditor/useLspHover.js +364 -35
  400. package/dist/components/smartEditor/useLspHover.js.map +1 -1
  401. package/dist/components/smartEditor/useLspSemanticTokens.js +1 -1
  402. package/dist/components/smartEditor/useLspSemanticTokens.js.map +1 -1
  403. package/dist/components/smartEditor/useLspSession.d.ts +6 -0
  404. package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
  405. package/dist/components/smartEditor/useLspSession.js +7 -6
  406. package/dist/components/smartEditor/useLspSession.js.map +1 -1
  407. package/dist/components/smartEditor/useLspSignatureHelp.d.ts.map +1 -1
  408. package/dist/components/smartEditor/useLspSignatureHelp.js +6 -11
  409. package/dist/components/smartEditor/useLspSignatureHelp.js.map +1 -1
  410. package/dist/components/smartEditor/useTabCameIn.d.ts +10 -0
  411. package/dist/components/smartEditor/useTabCameIn.d.ts.map +1 -0
  412. package/dist/components/smartEditor/useTabCameIn.js +42 -0
  413. package/dist/components/smartEditor/useTabCameIn.js.map +1 -0
  414. package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -1
  415. package/dist/components/supportTicket/AttachmentChips.js +15 -5
  416. package/dist/components/supportTicket/AttachmentChips.js.map +1 -1
  417. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +2 -3
  418. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -1
  419. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -1
  420. package/dist/components/supportTicket/ReferencePicker.d.ts +9 -3
  421. package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
  422. package/dist/components/supportTicket/ReferencePicker.js +38 -23
  423. package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
  424. package/dist/components/supportTicket/TicketHeader.d.ts +3 -4
  425. package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -1
  426. package/dist/components/supportTicket/TicketHeader.js +21 -6
  427. package/dist/components/supportTicket/TicketHeader.js.map +1 -1
  428. package/dist/components/supportTicket/TicketMetaTags.d.ts +1 -1
  429. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
  430. package/dist/components/supportTicket/TicketReplyBox.js +10 -29
  431. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
  432. package/dist/components/supportTicket/useLoadedImages.d.ts +0 -10
  433. package/dist/components/supportTicket/useLoadedImages.d.ts.map +1 -1
  434. package/dist/components/supportTicket/useLoadedImages.js +1 -63
  435. package/dist/components/supportTicket/useLoadedImages.js.map +1 -1
  436. package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -1
  437. package/dist/components/ticketReferences/TicketReferences.js +10 -9
  438. package/dist/components/ticketReferences/TicketReferences.js.map +1 -1
  439. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
  440. package/dist/components/ticketThread/TicketThread.js +52 -21
  441. package/dist/components/ticketThread/TicketThread.js.map +1 -1
  442. package/dist/helpers/allowedValues.d.ts +21 -12
  443. package/dist/helpers/allowedValues.d.ts.map +1 -1
  444. package/dist/helpers/allowedValues.js +40 -20
  445. package/dist/helpers/allowedValues.js.map +1 -1
  446. package/dist/helpers/descriptionTooltip.d.ts +46 -0
  447. package/dist/helpers/descriptionTooltip.d.ts.map +1 -0
  448. package/dist/helpers/descriptionTooltip.js +76 -0
  449. package/dist/helpers/descriptionTooltip.js.map +1 -0
  450. package/dist/helpers/domProps.d.ts +5 -0
  451. package/dist/helpers/domProps.d.ts.map +1 -0
  452. package/dist/helpers/domProps.js +47 -0
  453. package/dist/helpers/domProps.js.map +1 -0
  454. package/dist/helpers/expressions.d.ts +115 -4
  455. package/dist/helpers/expressions.d.ts.map +1 -1
  456. package/dist/helpers/expressions.js +247 -29
  457. package/dist/helpers/expressions.js.map +1 -1
  458. package/dist/helpers/hoveringPointer.d.ts +26 -0
  459. package/dist/helpers/hoveringPointer.d.ts.map +1 -0
  460. package/dist/helpers/hoveringPointer.js +29 -0
  461. package/dist/helpers/hoveringPointer.js.map +1 -0
  462. package/dist/helpers/markdownText.d.ts +14 -0
  463. package/dist/helpers/markdownText.d.ts.map +1 -0
  464. package/dist/helpers/markdownText.js +160 -0
  465. package/dist/helpers/markdownText.js.map +1 -0
  466. package/dist/helpers/noDescription.d.ts +9 -0
  467. package/dist/helpers/noDescription.d.ts.map +1 -0
  468. package/dist/helpers/noDescription.js +13 -0
  469. package/dist/helpers/noDescription.js.map +1 -0
  470. package/dist/helpers/optionUiTypes.d.ts +2 -2
  471. package/dist/helpers/qorusTypeCatalogue.d.ts +147 -0
  472. package/dist/helpers/qorusTypeCatalogue.d.ts.map +1 -0
  473. package/dist/helpers/qorusTypeCatalogue.js +505 -0
  474. package/dist/helpers/qorusTypeCatalogue.js.map +1 -0
  475. package/dist/helpers/readableColors.d.ts +36 -0
  476. package/dist/helpers/readableColors.d.ts.map +1 -0
  477. package/dist/helpers/readableColors.js +116 -0
  478. package/dist/helpers/readableColors.js.map +1 -0
  479. package/dist/helpers/recordFieldReference.d.ts +15 -0
  480. package/dist/helpers/recordFieldReference.d.ts.map +1 -0
  481. package/dist/helpers/recordFieldReference.js +89 -0
  482. package/dist/helpers/recordFieldReference.js.map +1 -0
  483. package/dist/helpers/singleLineString.d.ts.map +1 -1
  484. package/dist/helpers/singleLineString.js +0 -1
  485. package/dist/helpers/singleLineString.js.map +1 -1
  486. package/dist/helpers/templateCatalogue.d.ts +104 -0
  487. package/dist/helpers/templateCatalogue.d.ts.map +1 -0
  488. package/dist/helpers/templateCatalogue.js +116 -0
  489. package/dist/helpers/templateCatalogue.js.map +1 -0
  490. package/dist/helpers/templateCatalogueContext.d.ts +19 -0
  491. package/dist/helpers/templateCatalogueContext.d.ts.map +1 -0
  492. package/dist/helpers/templateCatalogueContext.js +31 -0
  493. package/dist/helpers/templateCatalogueContext.js.map +1 -0
  494. package/dist/helpers/templateItems.d.ts +2 -2
  495. package/dist/helpers/templateItems.d.ts.map +1 -1
  496. package/dist/helpers/templateItems.js +6 -4
  497. package/dist/helpers/templateItems.js.map +1 -1
  498. package/dist/helpers/templateText.d.ts +1 -0
  499. package/dist/helpers/templateText.d.ts.map +1 -1
  500. package/dist/helpers/templateText.js +7 -3
  501. package/dist/helpers/templateText.js.map +1 -1
  502. package/dist/helpers/templateTree.d.ts +248 -0
  503. package/dist/helpers/templateTree.d.ts.map +1 -0
  504. package/dist/helpers/templateTree.js +892 -0
  505. package/dist/helpers/templateTree.js.map +1 -0
  506. package/dist/helpers/templateType.d.ts +28 -0
  507. package/dist/helpers/templateType.d.ts.map +1 -0
  508. package/dist/helpers/templateType.js +30 -0
  509. package/dist/helpers/templateType.js.map +1 -0
  510. package/dist/helpers/templates.d.ts +86 -16
  511. package/dist/helpers/templates.d.ts.map +1 -1
  512. package/dist/helpers/templates.js +305 -45
  513. package/dist/helpers/templates.js.map +1 -1
  514. package/dist/helpers/validations.d.ts +27 -0
  515. package/dist/helpers/validations.d.ts.map +1 -1
  516. package/dist/helpers/validations.js +169 -39
  517. package/dist/helpers/validations.js.map +1 -1
  518. package/dist/helpers/webLink.d.ts +8 -0
  519. package/dist/helpers/webLink.d.ts.map +1 -0
  520. package/dist/helpers/webLink.js +29 -0
  521. package/dist/helpers/webLink.js.map +1 -0
  522. package/dist/hooks/useQorusTypes.d.ts +12 -17
  523. package/dist/hooks/useQorusTypes.d.ts.map +1 -1
  524. package/dist/hooks/useQorusTypes.js +44 -172
  525. package/dist/hooks/useQorusTypes.js.map +1 -1
  526. package/dist/hooks/useReadableIntentShade.d.ts +9 -0
  527. package/dist/hooks/useReadableIntentShade.d.ts.map +1 -0
  528. package/dist/hooks/useReadableIntentShade.js +23 -0
  529. package/dist/hooks/useReadableIntentShade.js.map +1 -0
  530. package/dist/hooks/useReadableOpacity.d.ts +6 -0
  531. package/dist/hooks/useReadableOpacity.d.ts.map +1 -0
  532. package/dist/hooks/useReadableOpacity.js +17 -0
  533. package/dist/hooks/useReadableOpacity.js.map +1 -0
  534. package/dist/index.d.ts +8 -0
  535. package/dist/index.d.ts.map +1 -1
  536. package/dist/index.js +24 -1
  537. package/dist/index.js.map +1 -1
  538. package/dist/tracking/ui/ExperimentCard.d.ts +1 -1
  539. package/dist/tracking/ui/ExperimentCard.d.ts.map +1 -1
  540. package/dist/tracking/ui/ExperimentCard.js +26 -10
  541. package/dist/tracking/ui/ExperimentCard.js.map +1 -1
  542. package/dist/tracking/ui/card.d.ts +2 -2
  543. package/dist/tracking/ui/card.d.ts.map +1 -1
  544. package/dist/tracking/ui/card.js +2 -2
  545. package/dist/tracking/ui/card.js.map +1 -1
  546. package/dist/tracking/ui/readable.d.ts +14 -0
  547. package/dist/tracking/ui/readable.d.ts.map +1 -0
  548. package/dist/tracking/ui/readable.js +60 -0
  549. package/dist/tracking/ui/readable.js.map +1 -0
  550. package/dist/utils/fetch.d.ts +9 -2
  551. package/dist/utils/fetch.d.ts.map +1 -1
  552. package/dist/utils/fetch.js +6 -5
  553. package/dist/utils/fetch.js.map +1 -1
  554. package/dist/utils/lspClient.types.d.ts +2 -0
  555. package/dist/utils/lspClient.types.d.ts.map +1 -1
  556. package/dist/utils/websocket.d.ts +65 -2
  557. package/dist/utils/websocket.d.ts.map +1 -1
  558. package/dist/utils/websocket.js +276 -44
  559. package/dist/utils/websocket.js.map +1 -1
  560. package/package.json +14 -6
  561. package/.claude/CLAUDE.md +0 -317
  562. package/.tasks/EXPORT_EXPRESSION_BUILDER.md +0 -38
  563. package/.tasks/EXPRESSION_BUILDER_REPORT.md +0 -149
  564. package/.tasks/EXPRESSION_FIELD_SEAMS.md +0 -66
  565. package/.tasks/FIELD_STACK_REPORT.md +0 -234
  566. package/.tasks/FORM_ENGINE_COMPACT_READ_FIRST.md +0 -558
  567. package/.tasks/FORM_ENGINE_EXPRESSIONS.md +0 -197
  568. package/.tasks/FORM_ENGINE_OPTIONS_SYNC.md +0 -95
  569. package/.tasks/FORM_FIELD_EXTRAS.md +0 -72
  570. package/.tasks/INDEX.md +0 -111
  571. package/.tasks/LSP_SHARED_CONNECTION.md +0 -75
  572. package/.tasks/QONSOLE_ASSIST_FEATURES.md +0 -306
  573. package/.tasks/RENDER_EXPRESSION_LSP.md +0 -159
  574. package/.tasks/REQRAFT_AUTO_FIELD.md +0 -176
  575. package/.tasks/SCHEMA_DEFINITION_FIELD.md +0 -250
  576. package/.tasks/SMART_EDITOR_CONTEXT_AND_POLISH.md +0 -340
  577. package/.tasks/SMART_EDITOR_LSP_FEATURES.md +0 -299
  578. package/.tasks/SMART_EDITOR_REVIEW_FOLLOWUPS.md +0 -280
  579. package/.tasks/SMART_EDITOR_UX_POLISH.md +0 -383
  580. package/.tasks/SMART_EDITOR_VISUAL_POLISH.md +0 -246
  581. package/.tasks/STORY_NETWORK_ISOLATION.md +0 -98
  582. package/.tasks/TEMPLATE_TOKEN_GRAMMAR.md +0 -72
  583. package/CODE_OF_CONDUCT.md +0 -105
  584. package/CONTRIBUTING.MD +0 -78
  585. package/SECURITY.md +0 -3
  586. package/chromatic.config.json +0 -5
  587. package/design/COMPACT_ENGINE_REDESIGN.md +0 -156
  588. package/design/COMPACT_INFO_DISPLAY.md +0 -87
  589. package/design/COMPACT_PARITY.md +0 -85
  590. package/design/EXPRESSION_BUILDER_REPORT_STRATEGY.md +0 -92
  591. package/design/FORM_ENGINE_COMPACT.md +0 -300
  592. package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +0 -353
  593. package/design/FORM_ENGINE_EXPRESSIONS.md +0 -257
  594. package/design/FORM_ENGINE_FIELD_MIGRATION.md +0 -419
  595. package/design/IDE_INTEGRATION.md +0 -64
  596. package/design/RENDER_EXPRESSION_TRANSPORT.md +0 -156
  597. package/design/SELECT_DIALOG_FLOOR.md +0 -86
  598. package/design/SMART_EDITOR_UX.md +0 -438
  599. package/design/TRACKING.md +0 -290
  600. package/design/UNAVAILABLE_ALLOWED_VALUES.md +0 -226
  601. package/dist/components/form/engine/formAppearance.d.ts +0 -49
  602. package/dist/components/form/engine/formAppearance.d.ts.map +0 -1
  603. package/dist/components/form/engine/formAppearance.js +0 -50
  604. package/dist/components/form/engine/formAppearance.js.map +0 -1
  605. package/dist/components/form/fields/ChoiceGroupHeading.d.ts +0 -21
  606. package/dist/components/form/fields/ChoiceGroupHeading.d.ts.map +0 -1
  607. package/dist/components/form/fields/ChoiceGroupHeading.js +0 -33
  608. package/dist/components/form/fields/ChoiceGroupHeading.js.map +0 -1
  609. package/dist/components/form/fields/allowed-values/savedValues.d.ts +0 -30
  610. package/dist/components/form/fields/allowed-values/savedValues.d.ts.map +0 -1
  611. package/dist/components/form/fields/allowed-values/savedValues.js +0 -39
  612. package/dist/components/form/fields/allowed-values/savedValues.js.map +0 -1
  613. package/dist/helpers/fieldDescription.d.ts +0 -16
  614. package/dist/helpers/fieldDescription.d.ts.map +0 -1
  615. package/dist/helpers/fieldDescription.js +0 -40
  616. package/dist/helpers/fieldDescription.js.map +0 -1
  617. package/src/components/Description/index.tsx +0 -254
  618. package/src/components/Description/markdownRendererContext.tsx +0 -29
  619. package/src/components/FocusedEditing/index.tsx +0 -50
  620. package/src/components/codeSize/index.tsx +0 -79
  621. package/src/components/composer/Composer.stories.tsx +0 -238
  622. package/src/components/composer/Composer.tsx +0 -554
  623. package/src/components/composer/helpers.ts +0 -74
  624. package/src/components/composer/index.ts +0 -2
  625. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +0 -1183
  626. package/src/components/dpqlEditor/DpqlEditor.tsx +0 -219
  627. package/src/components/dpqlEditor/dpqlHelpers.ts +0 -287
  628. package/src/components/dpqlEditor/dpqlInserter.ts +0 -121
  629. package/src/components/dpqlEditor/dpqlProbe.ts +0 -165
  630. package/src/components/dpqlEditor/dpqlTags.ts +0 -114
  631. package/src/components/dpqlEditor/index.ts +0 -41
  632. package/src/components/dpqlEditor/types.ts +0 -203
  633. package/src/components/dpqlEditor/useDpqlProbe.ts +0 -33
  634. package/src/components/dpqlEditor/useDpqlSession.ts +0 -411
  635. package/src/components/form/engine/CompactRow.tsx +0 -2740
  636. package/src/components/form/engine/CompactToolbar.tsx +0 -399
  637. package/src/components/form/engine/FormEngine.stories.tsx +0 -9934
  638. package/src/components/form/engine/FormEngine.tsx +0 -4891
  639. package/src/components/form/engine/FormEngineAppearance.stories.tsx +0 -609
  640. package/src/components/form/engine/FormEngineRemote.stories.tsx +0 -679
  641. package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +0 -335
  642. package/src/components/form/engine/FormFieldsSkeleton.tsx +0 -82
  643. package/src/components/form/engine/OptionFieldMessages.tsx +0 -548
  644. package/src/components/form/engine/OptionsHelpDialog.tsx +0 -29
  645. package/src/components/form/engine/__fixtures__/basicFormOptions.ts +0 -309
  646. package/src/components/form/engine/__fixtures__/fieldChromeOptions.ts +0 -45
  647. package/src/components/form/engine/_structuredData/SchemaDataView.stories.tsx +0 -233
  648. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +0 -755
  649. package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +0 -85
  650. package/src/components/form/engine/_structuredData/StructuredDataView.tsx +0 -75
  651. package/src/components/form/engine/_structuredData/structuredData.ts +0 -145
  652. package/src/components/form/engine/compactRowContext.ts +0 -153
  653. package/src/components/form/engine/compactRowStyles.ts +0 -1087
  654. package/src/components/form/engine/compactToolbarContext.ts +0 -72
  655. package/src/components/form/engine/formAppearance.ts +0 -79
  656. package/src/components/form/engine/optionActions.ts +0 -128
  657. package/src/components/form/engine/optionsContext.ts +0 -24
  658. package/src/components/form/engine/readFirst.ts +0 -1090
  659. package/src/components/form/engine/rendererTypes.ts +0 -107
  660. package/src/components/form/engine/revealRow.ts +0 -123
  661. package/src/components/form/engine/rowFocus.ts +0 -112
  662. package/src/components/form/engine/rowMenuContext.ts +0 -159
  663. package/src/components/form/engine/rowOpenPicker.ts +0 -27
  664. package/src/components/form/engine/typeChoices.ts +0 -63
  665. package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +0 -159
  666. package/src/components/form/engine/variants/VariantCalmTable.tsx +0 -244
  667. package/src/components/form/engine/variants/VariantCards.tsx +0 -212
  668. package/src/components/form/engine/variants/VariantFocus.tsx +0 -382
  669. package/src/components/form/engine/variants/VariantMinimal.tsx +0 -170
  670. package/src/components/form/engine/variants/focusDemo.ts +0 -145
  671. package/src/components/form/engine/variants/variantModel.ts +0 -223
  672. package/src/components/form/engine/variants/variantParts.tsx +0 -313
  673. package/src/components/form/expressions/DpqlRendering.tsx +0 -85
  674. package/src/components/form/expressions/ExpressionField.stories.tsx +0 -1395
  675. package/src/components/form/expressions/ExpressionField.tsx +0 -503
  676. package/src/components/form/expressions/argumentPresence.ts +0 -213
  677. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +0 -1683
  678. package/src/components/form/expressions/builder/argumentDetail.tsx +0 -17
  679. package/src/components/form/expressions/builder/argumentLabel.tsx +0 -146
  680. package/src/components/form/expressions/builder/argumentSlot.tsx +0 -101
  681. package/src/components/form/expressions/builder/argumentWrapper.tsx +0 -243
  682. package/src/components/form/expressions/builder/confirmMismatchedTypesModal.tsx +0 -120
  683. package/src/components/form/expressions/builder/confirmUnsupportedTypeModal.tsx +0 -69
  684. package/src/components/form/expressions/builder/index.tsx +0 -1545
  685. package/src/components/form/expressions/builder/item.tsx +0 -89
  686. package/src/components/form/expressions/builder/moveToPositionStrip.tsx +0 -156
  687. package/src/components/form/expressions/builder/renderTemplate.tsx +0 -42
  688. package/src/components/form/expressions/dpqlMockLanguage.ts +0 -566
  689. package/src/components/form/expressions/dpqlMockLsp.ts +0 -126
  690. package/src/components/form/expressions/mockExpressions.ts +0 -317
  691. package/src/components/form/expressions/renderExpressionToText.ts +0 -152
  692. package/src/components/form/expressions/types.ts +0 -110
  693. package/src/components/form/expressions/useExpressions.ts +0 -101
  694. package/src/components/form/expressions/useRenderExpression.ts +0 -165
  695. package/src/components/form/fields/ChoiceGroupHeading.tsx +0 -38
  696. package/src/components/form/fields/Field.stories.tsx +0 -143
  697. package/src/components/form/fields/Field.tsx +0 -734
  698. package/src/components/form/fields/allowed-values/AllowedValues.tsx +0 -321
  699. package/src/components/form/fields/allowed-values/savedValues.ts +0 -64
  700. package/src/components/form/fields/array/ArrayAuto.tsx +0 -237
  701. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +0 -475
  702. package/src/components/form/fields/array/ArrayAutoField.tsx +0 -367
  703. package/src/components/form/fields/auto/AutoFormField.stories.tsx +0 -825
  704. package/src/components/form/fields/auto/AutoFormField.tsx +0 -1183
  705. package/src/components/form/fields/binary/Binary.stories.tsx +0 -122
  706. package/src/components/form/fields/binary/Binary.tsx +0 -91
  707. package/src/components/form/fields/boolean/Boolean.stories.tsx +0 -125
  708. package/src/components/form/fields/boolean/Boolean.tsx +0 -34
  709. package/src/components/form/fields/byte-size/ByteSize.stories.tsx +0 -87
  710. package/src/components/form/fields/byte-size/ByteSize.tsx +0 -58
  711. package/src/components/form/fields/color/Color.stories.tsx +0 -99
  712. package/src/components/form/fields/color/Color.tsx +0 -46
  713. package/src/components/form/fields/cron/Cron.stories.tsx +0 -68
  714. package/src/components/form/fields/cron/Cron.tsx +0 -80
  715. package/src/components/form/fields/date/Date.stories.tsx +0 -77
  716. package/src/components/form/fields/date/Date.tsx +0 -55
  717. package/src/components/form/fields/emittedValues.ts +0 -70
  718. package/src/components/form/fields/file/File.stories.tsx +0 -161
  719. package/src/components/form/fields/file/File.tsx +0 -227
  720. package/src/components/form/fields/long-string/LongString.stories.tsx +0 -147
  721. package/src/components/form/fields/long-string/LongString.tsx +0 -141
  722. package/src/components/form/fields/markdown/Markdown.stories.tsx +0 -76
  723. package/src/components/form/fields/markdown/Markdown.tsx +0 -81
  724. package/src/components/form/fields/markdown/MarkdownView.tsx +0 -222
  725. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +0 -140
  726. package/src/components/form/fields/number/Number.stories.tsx +0 -159
  727. package/src/components/form/fields/number/Number.tsx +0 -175
  728. package/src/components/form/fields/object/Object.stories.tsx +0 -398
  729. package/src/components/form/fields/object/Object.tsx +0 -255
  730. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +0 -95
  731. package/src/components/form/fields/radio-group/RadioGroup.tsx +0 -52
  732. package/src/components/form/fields/radio-group/images/java-96x128.png +0 -0
  733. package/src/components/form/fields/radio-group/images/python-129x128.png +0 -0
  734. package/src/components/form/fields/radio-group/images/qore-106x128.png +0 -0
  735. package/src/components/form/fields/rich-text/RichText.stories.tsx +0 -260
  736. package/src/components/form/fields/rich-text/RichText.tsx +0 -537
  737. package/src/components/form/fields/schema-definition/CatalogLeafForm.tsx +0 -242
  738. package/src/components/form/fields/schema-definition/CatalogNodeEditor.tsx +0 -758
  739. package/src/components/form/fields/schema-definition/Hint.tsx +0 -22
  740. package/src/components/form/fields/schema-definition/InlineValidationBanner.tsx +0 -138
  741. package/src/components/form/fields/schema-definition/Loader.tsx +0 -20
  742. package/src/components/form/fields/schema-definition/MigrationsTab.tsx +0 -187
  743. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +0 -286
  744. package/src/components/form/fields/schema-definition/TableEditor.tsx +0 -155
  745. package/src/components/form/fields/schema-definition/TablesTab.tsx +0 -138
  746. package/src/components/form/fields/schema-definition/catalog.ts +0 -237
  747. package/src/components/form/fields/schema-definition/helpers.ts +0 -90
  748. package/src/components/form/fields/schema-definition/index.tsx +0 -418
  749. package/src/components/form/fields/schema-definition/mockCatalog.ts +0 -325
  750. package/src/components/form/fields/schema-definition/mockDefinition.ts +0 -111
  751. package/src/components/form/fields/schema-definition/referenceSuggestions.tsx +0 -152
  752. package/src/components/form/fields/schema-definition/starterTemplate.ts +0 -68
  753. package/src/components/form/fields/schema-definition/types.ts +0 -185
  754. package/src/components/form/fields/schema-definition/useSchemaOptionCatalog.ts +0 -49
  755. package/src/components/form/fields/select/Select.stories.tsx +0 -872
  756. package/src/components/form/fields/select/Select.tsx +0 -624
  757. package/src/components/form/fields/select/SelectCollection.tsx +0 -594
  758. package/src/components/form/fields/string/String.stories.tsx +0 -86
  759. package/src/components/form/fields/string/String.tsx +0 -43
  760. package/src/components/form/fields/template/ExampleValueModal.stories.tsx +0 -114
  761. package/src/components/form/fields/template/ExampleValueModal.tsx +0 -99
  762. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +0 -47
  763. package/src/components/form/fields/template/TemplateField.stories.tsx +0 -1121
  764. package/src/components/form/fields/template/TemplateField.tsx +0 -1749
  765. package/src/components/form/fields/template/TemplateText.tsx +0 -115
  766. package/src/components/form/fields/template/__fixtures__/longExampleValue.ts +0 -7
  767. package/src/components/form/fields/template/__fixtures__/multiLevelTemplates.json +0 -147
  768. package/src/components/form/fields/template/__fixtures__/templates.json +0 -83
  769. package/src/components/form/fields/timeout/Timeout.stories.tsx +0 -228
  770. package/src/components/form/fields/timeout/Timeout.tsx +0 -87
  771. package/src/components/form/fields/url/Url.stories.tsx +0 -94
  772. package/src/components/form/fields/url/Url.tsx +0 -85
  773. package/src/components/form/index.tsx +0 -49
  774. package/src/components/imageLightbox/ImageLightbox.stories.tsx +0 -238
  775. package/src/components/imageLightbox/ImageLightbox.tsx +0 -238
  776. package/src/components/imageLightbox/index.ts +0 -1
  777. package/src/components/log/Log.stories.tsx +0 -333
  778. package/src/components/log/Log.tsx +0 -285
  779. package/src/components/menu/Menu.stories.tsx +0 -155
  780. package/src/components/menu/Menu.tsx +0 -312
  781. package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +0 -797
  782. package/src/components/qonsoleSmartInput/QonsoleSmartInput.tsx +0 -105
  783. package/src/components/qonsoleSmartInput/index.ts +0 -16
  784. package/src/components/qonsoleSmartInput/qonsoleInserter.ts +0 -77
  785. package/src/components/qonsoleSmartInput/types.ts +0 -133
  786. package/src/components/qonsoleSmartInput/useQonsoleSession.ts +0 -124
  787. package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +0 -62
  788. package/src/components/searchFilterBar/SearchFilterBar.tsx +0 -144
  789. package/src/components/searchFilterBar/index.ts +0 -1
  790. package/src/components/smartEditor/MarkdownDoc.tsx +0 -190
  791. package/src/components/smartEditor/SmartEditor.stories.tsx +0 -475
  792. package/src/components/smartEditor/SmartEditor.tsx +0 -808
  793. package/src/components/smartEditor/__fixtures__/mockLspServer.ts +0 -205
  794. package/src/components/smartEditor/helpers.ts +0 -217
  795. package/src/components/smartEditor/index.ts +0 -35
  796. package/src/components/smartEditor/styling.ts +0 -45
  797. package/src/components/smartEditor/types.ts +0 -211
  798. package/src/components/smartEditor/useLspAutocomplete.ts +0 -758
  799. package/src/components/smartEditor/useLspDiagnosticDecorations.ts +0 -125
  800. package/src/components/smartEditor/useLspHover.ts +0 -230
  801. package/src/components/smartEditor/useLspSemanticTokens.ts +0 -298
  802. package/src/components/smartEditor/useLspSession.ts +0 -231
  803. package/src/components/smartEditor/useLspSignatureHelp.ts +0 -198
  804. package/src/components/supportTicket/AttachmentChips.stories.tsx +0 -171
  805. package/src/components/supportTicket/AttachmentChips.tsx +0 -147
  806. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -257
  807. package/src/components/supportTicket/InterfaceReferenceTags.tsx +0 -130
  808. package/src/components/supportTicket/ReferencePicker.stories.tsx +0 -580
  809. package/src/components/supportTicket/ReferencePicker.tsx +0 -573
  810. package/src/components/supportTicket/TicketConversation.stories.tsx +0 -120
  811. package/src/components/supportTicket/TicketHeader.stories.tsx +0 -183
  812. package/src/components/supportTicket/TicketHeader.tsx +0 -248
  813. package/src/components/supportTicket/TicketMetaTags.stories.tsx +0 -103
  814. package/src/components/supportTicket/TicketMetaTags.tsx +0 -85
  815. package/src/components/supportTicket/TicketReplyBox.stories.tsx +0 -357
  816. package/src/components/supportTicket/TicketReplyBox.tsx +0 -172
  817. package/src/components/supportTicket/__fixtures__/kindMarks.ts +0 -28
  818. package/src/components/supportTicket/attachmentAccess.ts +0 -97
  819. package/src/components/supportTicket/fileToAttachment.ts +0 -24
  820. package/src/components/supportTicket/index.ts +0 -10
  821. package/src/components/supportTicket/meta.ts +0 -206
  822. package/src/components/supportTicket/ticketModel.ts +0 -131
  823. package/src/components/supportTicket/useLoadedImages.ts +0 -93
  824. package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -341
  825. package/src/components/ticketReferences/TicketReferences.tsx +0 -914
  826. package/src/components/ticketReferences/index.ts +0 -2
  827. package/src/components/ticketThread/TicketThread.stories.tsx +0 -898
  828. package/src/components/ticketThread/TicketThread.tsx +0 -706
  829. package/src/components/ticketThread/index.ts +0 -6
  830. package/src/contexts/FetchContext.tsx +0 -25
  831. package/src/contexts/ReqraftContext.tsx +0 -9
  832. package/src/contexts/StorageContext.tsx +0 -33
  833. package/src/global.d.ts +0 -4
  834. package/src/helpers/allowedValues.ts +0 -268
  835. package/src/helpers/choiceChip.ts +0 -227
  836. package/src/helpers/common.ts +0 -326
  837. package/src/helpers/dpqlDetection.ts +0 -209
  838. package/src/helpers/expressions.ts +0 -140
  839. package/src/helpers/fieldDescription.ts +0 -39
  840. package/src/helpers/optionUiTypes.ts +0 -199
  841. package/src/helpers/options.ts +0 -150
  842. package/src/helpers/providerValue.ts +0 -125
  843. package/src/helpers/scrollbar.ts +0 -32
  844. package/src/helpers/singleLineString.ts +0 -62
  845. package/src/helpers/templateItems.ts +0 -81
  846. package/src/helpers/templateText.ts +0 -350
  847. package/src/helpers/templates.ts +0 -602
  848. package/src/helpers/validations.ts +0 -2085
  849. package/src/hooks/useArgSchema.ts +0 -37
  850. package/src/hooks/useCoarsePointer.ts +0 -30
  851. package/src/hooks/useFetch/useFetch.stories.tsx +0 -379
  852. package/src/hooks/useFetch/useFetch.tsx +0 -154
  853. package/src/hooks/usePhoneViewport.ts +0 -34
  854. package/src/hooks/useQorusTypes.ts +0 -233
  855. package/src/hooks/useReqraftProperty.ts +0 -16
  856. package/src/hooks/useStorage/useStorage.stories.tsx +0 -168
  857. package/src/hooks/useStorage/useStorage.ts +0 -35
  858. package/src/hooks/useTemplates.ts +0 -123
  859. package/src/hooks/useValidation.ts +0 -9
  860. package/src/hooks/useWebSocket/useWebSocket.ts +0 -224
  861. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +0 -640
  862. package/src/hooks/useWhyDidYouUpdate.ts +0 -38
  863. package/src/index.tsx +0 -86
  864. package/src/providers/FetchProvider.tsx +0 -40
  865. package/src/providers/ReqraftProvider.tsx +0 -52
  866. package/src/providers/StorageProvider.tsx +0 -131
  867. package/src/stores/currentUser/currentUser.stories.tsx +0 -100
  868. package/src/stores/currentUser/currentUser.tsx +0 -94
  869. package/src/stories/Data/fileFieldArgSchema.json +0 -86
  870. package/src/stories/Tests/utils.ts +0 -320
  871. package/src/stories/storyNetwork.ts +0 -92
  872. package/src/stories/tracking.tsx +0 -206
  873. package/src/tracking/browser/attach.ts +0 -186
  874. package/src/tracking/browser/env.ts +0 -78
  875. package/src/tracking/core/consent.ts +0 -37
  876. package/src/tracking/core/engagement.ts +0 -47
  877. package/src/tracking/core/experiments.ts +0 -258
  878. package/src/tracking/core/hash.ts +0 -26
  879. package/src/tracking/core/ids.ts +0 -25
  880. package/src/tracking/core/observable.ts +0 -23
  881. package/src/tracking/core/queue.ts +0 -131
  882. package/src/tracking/core/sections.ts +0 -166
  883. package/src/tracking/core/storage.ts +0 -69
  884. package/src/tracking/core/tracker.ts +0 -417
  885. package/src/tracking/core/types.ts +0 -162
  886. package/src/tracking/core/utm.ts +0 -23
  887. package/src/tracking/index.ts +0 -72
  888. package/src/tracking/markup.ts +0 -17
  889. package/src/tracking/react/Experiment.tsx +0 -44
  890. package/src/tracking/react/TrackingProvider.tsx +0 -56
  891. package/src/tracking/react/hooks.ts +0 -106
  892. package/src/tracking/react/index.ts +0 -13
  893. package/src/tracking/testing.ts +0 -108
  894. package/src/tracking/ui/ExperimentCard.stories.tsx +0 -296
  895. package/src/tracking/ui/ExperimentCard.tsx +0 -667
  896. package/src/tracking/ui/ExperimentsOverlay.stories.tsx +0 -155
  897. package/src/tracking/ui/ExperimentsOverlay.tsx +0 -437
  898. package/src/tracking/ui/anchor.ts +0 -180
  899. package/src/tracking/ui/card.ts +0 -464
  900. package/src/tracking/ui/client.ts +0 -117
  901. package/src/tracking/ui/index.ts +0 -77
  902. package/src/tracking/ui/types.ts +0 -110
  903. package/src/tracking/ui/useExperimentsAdmin.ts +0 -103
  904. package/src/types/Form.ts +0 -29
  905. package/src/types.ts +0 -14
  906. package/src/utils/datetime.ts +0 -10
  907. package/src/utils/fetch.ts +0 -457
  908. package/src/utils/lspClient.ts +0 -711
  909. package/src/utils/lspClient.types.ts +0 -211
  910. package/src/utils/queryUnlessCancelled.ts +0 -28
  911. package/src/utils/websocket.ts +0 -293
  912. package/vite.config.ts +0 -58
  913. package/vitest.config.ts +0 -69
@@ -1,2740 +0,0 @@
1
- import {
2
- ReqoreButton,
3
- ReqoreCollapsibleContent,
4
- ReqoreControlGroup,
5
- ReqoreDropdown,
6
- ReqoreIcon,
7
- ReqoreMessage,
8
- ReqoreP,
9
- ReqoreTag,
10
- ReqoreTagGroup,
11
- } from '@qoretechnologies/reqore';
12
- import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
13
- import { RowMenuContext, useRowMenuRegistry } from './rowMenuContext';
14
- import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
15
- import { optionActionAsMenuItem, resolveOptionActions, splitOptionActions } from './optionActions';
16
- import {
17
- IQorusFormField,
18
- IQorusFormSchema,
19
- TQorusForm,
20
- TQorusFormFieldSchema,
21
- } from '@qoretechnologies/ts-toolkit';
22
- import flatten from 'lodash/flatten';
23
- import isEqual from 'lodash/isEqual';
24
- import size from 'lodash/size';
25
- import React, { memo } from 'react';
26
- import { useContextSelector } from 'use-context-selector';
27
- import { hasAllDependenciesFullfilled, parseDependency } from '../../../helpers/validations';
28
- import { findTemplate, isValueTemplate } from '../../../helpers/templates';
29
- import { getDefaultValue, richtextToSegments, richtextToString } from '../../../helpers/common';
30
- import { fieldHasLongDescription } from '../../../helpers/fieldDescription';
31
- import { hasTemplateText, templateTextSegments } from '../../../helpers/templateText';
32
- import { ReadOnlyTemplateTag } from '../fields/template/ReadOnlyTemplateTag';
33
- import { TemplateText } from '../fields/template/TemplateText';
34
- import { describeCodeSize, formatCodeChars, formatCodeLines } from '../../codeSize';
35
- import { Description } from '../../Description';
36
- import { useMarkdownRenderer } from '../../Description/markdownRendererContext';
37
- import { FocusedEditing } from '../../FocusedEditing';
38
- import { CompactRowContext } from './compactRowContext';
39
- import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL, useIsCalmForm } from './formAppearance';
40
- import { revealRow } from './revealRow';
41
- import {
42
- StyledActionSlot,
43
- StyledCardHeading,
44
- StyledCardLabel,
45
- StyledCodePreview,
46
- StyledColorSwatch,
47
- StyledColumn,
48
- StyledEditCard,
49
- StyledLabelBlock,
50
- StyledAbsorbedField,
51
- StyledLabelDesc,
52
- StyledRowActions,
53
- StyledRowInset,
54
- StyledRowLabel,
55
- StyledRowValue,
56
- StyledStatusDot,
57
- } from './compactRowStyles';
58
- import { getShownSchemaMessages, getOptionFieldMessages } from './OptionFieldMessages';
59
- import { findRowFocusTarget, isClosedPickerTrigger } from './rowFocus';
60
- import { RowOpenPickerContext } from './rowOpenPicker';
61
- import { SchemaDataView, canRenderWithSchema } from './_structuredData/SchemaDataView';
62
- import { queryUnlessCancelled } from '../../../utils/queryUnlessCancelled';
63
- import {
64
- colorToCss,
65
- findAllowedValueOption,
66
- formatBytes,
67
- formatOptionValue,
68
- getAllowedValueIcon,
69
- getAllowedValueImage,
70
- getFileSize,
71
- getHashEntries,
72
- getReadFirstStatus,
73
- getValueType,
74
- isFixedCompactAllowedValueOption,
75
- isOptionValueEmpty,
76
- optionHasImages,
77
- getExpressionAst,
78
- } from './readFirst';
79
- import { StructuredDataView } from './_structuredData/StructuredDataView';
80
- import { DpqlRendering } from '../expressions/DpqlRendering';
81
- import { TFieldWithOwnTemplates } from './rendererTypes';
82
-
83
- /**
84
- * Does this markdown value have more document in it than one row can show?
85
- *
86
- * Blank trailing lines do not count: a textarea leaves them behind constantly,
87
- * and a one-line description that gained a stray newline must not suddenly
88
- * grow an inset.
89
- */
90
- const isMultilineMarkdown = (value: string): boolean => value.trim().includes('\n');
91
-
92
- // Types that are not hashes by declaration but hold a plain object as their
93
- // value, and so get the hash row's structured preview under their summary.
94
- // Kept explicit: a `schema` or `richtext` value is an object too, and each of
95
- // those already has a preview of its own.
96
- const OBJECT_VALUED_PREVIEW_TYPES = new Set([
97
- 'data-provider',
98
- 'options',
99
- 'system-options',
100
- 'processor-mappings',
101
- ]);
102
-
103
- // Types whose editors are too tall/nested to edit in-row — these keep the edit
104
- // card; `arg_schema` and operator fields are excluded separately.
105
- const COMPACT_COMPLEX_TYPES = new Set([
106
- 'hash',
107
- 'free-hash',
108
- 'list',
109
- 'free-list',
110
- 'array',
111
- 'array-auto',
112
- 'file',
113
- 'file-string',
114
- 'richtext',
115
- 'long-string',
116
- 'any',
117
- 'auto',
118
- 'schema',
119
- 'schema-definition',
120
- 'data-provider',
121
- 'processor-mappings',
122
- 'options',
123
- 'system-options',
124
- 'byte-size',
125
- 'timeout',
126
- 'markdown',
127
- 'method-name',
128
- 'class-connectors',
129
- 'class-array',
130
- 'yaml',
131
- ]);
132
-
133
- // Card editors that hold a SINGLE value through one text-bearing input — their
134
- // built-in ReqoreInput clear duplicates the card's own "Clear value" action, so
135
- // we suppress it (the card trash action is the single source). Structural/multi-input
136
- // editors (hash, list, schema…) and polymorphic ones (auto/any) are NOT listed:
137
- // their per-sub-field clears are distinct affordances and must stay, while the
138
- // card ✕ clears the whole value. (Operators force a card on a scalar value, so
139
- // 'string' covers them — and `expr`, which is ui_type 'string'.)
140
- const COMPACT_SINGLE_VALUE_TYPES = new Set([
141
- 'string',
142
- 'long-string',
143
- 'markdown',
144
- 'richtext',
145
- 'byte-size',
146
- 'timeout',
147
- 'method-name',
148
- ]);
149
-
150
- // One read-first row: label | value | action collapsed; the real editor (the
151
- // classic renderOption) expanded. `hidden` = search-surfaced optional —
152
- // activating the row adds the field first.
153
- /**
154
- * The text inside a rich-text envelope, when a list item is one.
155
- *
156
- * An option like Gmail's `to` is `type: "list"`, `element_type: "string"`,
157
- * `supports_templates: true`: each element is a plain string that may carry
158
- * template tags, so its editor is a rich-text one and the element value arrives
159
- * wrapped as `{type: "richtext", value: [{type: "paragraph", …}]}`.
160
- *
161
- * That envelope is a string in a coat, not structured data. The hash-list test
162
- * unwraps `item.value`, found the Slate document — an object — and classed a
163
- * list of email addresses as a list of hashes, so the row drew an expandable
164
- * `type / children / text` tree instead of the address the operator typed.
165
- */
166
- export const richtextItemText = (item: unknown): string | undefined =>
167
- item && typeof item === 'object' && (item as { type?: unknown }).type === 'richtext' ?
168
- richtextToString((item as { value?: Parameters<typeof richtextToString>[0] }).value)
169
- : undefined;
170
-
171
- export const CompactRow = memo(
172
- ({
173
- optionName,
174
- optionField,
175
- hidden = false,
176
- clustered = false,
177
- clusterFirst = false,
178
- clusterLast = false,
179
- absorbedFields,
180
- }: {
181
- optionName: string;
182
- optionField: IQorusFormField;
183
- hidden?: boolean;
184
- /** Siblings this row renders inside its own container instead of leaving
185
- * them a row each — declared by the schema's `absorb_fields`. The pair it
186
- * exists for is a code editor and its language: one decision, so one
187
- * element. */
188
- absorbedFields?: string[];
189
- // Rendered inside a required-group cluster: leading status node + rail, and no
190
- // per-row "one of" chip (the cluster header carries it). first/last trim the
191
- // rail segment so it spans node-to-node, not past the end members.
192
- clustered?: boolean;
193
- clusterFirst?: boolean;
194
- clusterLast?: boolean;
195
- }) => {
196
- const readOnly = useContextSelector(CompactRowContext, (v) => v.readOnly);
197
- // The look only — see `TFormEngineAppearance`. Nothing a row DOES reads it.
198
- const calm = useIsCalmForm();
199
- const commitMode = useContextSelector(CompactRowContext, (v) => v.commitMode);
200
- const options = useContextSelector(CompactRowContext, (v) => v.options);
201
- // Whether this field's emptiness is a fault yet: the person has opened it and left,
202
- // or the host reported a submit attempt. Before that a required field is only to
203
- // fill in, and says so in the quiet colour.
204
- const visited = useContextSelector(
205
- CompactRowContext,
206
- (v) => v.revealRequired || !!v.visitedOptions?.includes(optionName)
207
- );
208
-
209
- // An absorbed field keeps its own name — it is a different field, not a
210
- // property of its host — so the label comes from its schema exactly as it
211
- // would on its own row.
212
- const getAbsorbedLabel = (absorbedName: string): string =>
213
- (options?.[absorbedName] as { display_name?: string } | undefined)?.display_name ||
214
- absorbedName;
215
- const codePreviewRenderer = useContextSelector(CompactRowContext, (v) => v.codePreviewRenderer);
216
- const markdownRenderer = useMarkdownRenderer();
217
- const operators = useContextSelector(CompactRowContext, (v) => v.operators);
218
- const focusedEditing = useContextSelector(CompactRowContext, (v) => v.focusedEditing);
219
- const showFieldTypes = useContextSelector(CompactRowContext, (v) => v.showFieldTypes);
220
- const showAllDescriptions = useContextSelector(CompactRowContext, (v) => v.showAllDescriptions);
221
- const isExpanded = useContextSelector(CompactRowContext, (v) =>
222
- v.expandedOptions.includes(optionName)
223
- );
224
- const isHighlighted = useContextSelector(CompactRowContext, (v) =>
225
- v.highlightedOptions.includes(optionName)
226
- );
227
- const isFlashed = useContextSelector(CompactRowContext, (v) =>
228
- v.flashedOptions.includes(optionName)
229
- );
230
- const setHighlightedOptions = useContextSelector(
231
- CompactRowContext,
232
- (v) => v.setHighlightedOptions
233
- );
234
- const setFocusedEditing = useContextSelector(CompactRowContext, (v) => v.setFocusedEditing);
235
- const readRowHeights = useContextSelector(CompactRowContext, (v) => v.readRowHeights);
236
- const onReadOnlyActivate = useContextSelector(
237
- CompactRowContext,
238
- (v) => v.onReadOnlyActivate
239
- );
240
-
241
- /**
242
- * The floor recorded when a row is opened lasts until the author changes
243
- * something.
244
- *
245
- * `activate()` measures the row as it was READ and pins that as the
246
- * editing row's `min-height`, so opening a row cannot yank the content
247
- * below it up under the pointer. That is right for the transition and
248
- * wrong for the rest of the session: a row that was tall because it
249
- * carried a warning keeps that height after the author fixes the very
250
- * thing the warning was about, leaving a large empty box where the
251
- * explanation used to be — reported as "the chip is removed, but the
252
- * vertical size of the option remains the same".
253
- *
254
- * Released on the first change to the value, which is exactly the moment
255
- * the author has acted and the row should settle to what it now holds.
256
- * Nothing is yanked by that: the author caused it and is looking at it.
257
- *
258
- * Keyed on a STRING rather than the value itself. A form value is often a
259
- * fresh object on every render (`{source, value}` for a reference), so a
260
- * dependency on its identity would drop the floor on the first re-render
261
- * and the transition it exists for would never be smoothed at all.
262
- */
263
- const valueKey =
264
- optionField?.value === null || typeof optionField?.value !== 'object' ?
265
- String(optionField?.value)
266
- : JSON.stringify(optionField?.value);
267
-
268
- /* `valueKey` alone, deliberately - see above. No eslint-disable: this repo
269
- does not load `react-hooks`, so a suppression for a rule that is not
270
- there is itself an error, and the pre-push lint refuses the push. */
271
- React.useEffect(() => {
272
- delete readRowHeights.current[optionName];
273
- }, [valueKey]);
274
- const originalValue = useContextSelector(CompactRowContext, (v) => v.originalValue);
275
- const availableOptions = useContextSelector(CompactRowContext, (v) => v.availableOptions);
276
- const requiredGroupsInfo = useContextSelector(CompactRowContext, (v) => v.requiredGroupsInfo);
277
- const handleValueChange = useContextSelector(CompactRowContext, (v) => v.handleValueChange);
278
- const toggleExpandedOption = useContextSelector(
279
- CompactRowContext,
280
- (v) => v.toggleExpandedOption
281
- );
282
- const flashOption = useContextSelector(CompactRowContext, (v) => v.flashOption);
283
- const hasOptionChanged = useContextSelector(CompactRowContext, (v) => v.hasOptionChanged);
284
- const handleOptionLabelClick = useContextSelector(
285
- CompactRowContext,
286
- (v) => v.handleOptionLabelClick
287
- );
288
- const removeSelectedOption = useContextSelector(
289
- CompactRowContext,
290
- (v) => v.removeSelectedOption
291
- );
292
- const getTypeForOption = useContextSelector(CompactRowContext, (v) => v.getTypeForOption);
293
- const isRendererOnly = useContextSelector(CompactRowContext, (v) => v.isRendererOnly);
294
- const confirmAction = useContextSelector(CompactRowContext, (v) => v.confirmAction);
295
- const optionActions = useContextSelector(CompactRowContext, (v) => v.optionActions);
296
- const collapseOptionActions = useContextSelector(
297
- CompactRowContext,
298
- (v) => v.collapseOptionActions
299
- );
300
- const renderOption = useContextSelector(CompactRowContext, (v) => v.renderOption);
301
- const theme = useContextSelector(CompactRowContext, (v) => v.theme);
302
- const cMuted = useContextSelector(CompactRowContext, (v) => v.cMuted);
303
- const templates = useContextSelector(CompactRowContext, (v) => v.templates);
304
- const cKey = useContextSelector(CompactRowContext, (v) => v.cKey);
305
- const cDivider = useContextSelector(CompactRowContext, (v) => v.cDivider);
306
- const cHover = useContextSelector(CompactRowContext, (v) => v.cHover);
307
- const cDanger = useContextSelector(CompactRowContext, (v) => v.cDanger);
308
- const cWarning = useContextSelector(CompactRowContext, (v) => v.cWarning);
309
- const cInfo = useContextSelector(CompactRowContext, (v) => v.cInfo);
310
- const cText = useContextSelector(CompactRowContext, (v) => v.cText);
311
- // A field's NAME: full text colour by default; grey in the calm look, so the
312
- // value beside it is what the eye lands on. One colour for the read row, the
313
- // open row and the edit card, so a name does not change as its field opens.
314
- const cLabel = calm ? `${cText}b3` : cKey;
315
- // A markdown value renders as a document in the row's inset; the host's
316
- // renderer when it supplied one, reqraft's own otherwise.
317
-
318
- // Value-cell content: colour adds a swatch, file an icon + size; hash keeps
319
- // its "N fields" summary (sub-fields reveal beneath the row).
320
- const renderReadFirstValue = (
321
- field: IQorusFormField,
322
- schema: TQorusFormFieldSchema | undefined,
323
- formatted: string,
324
- // An editable row gets one line and an ellipsis — its value line is a
325
- // summary you click to open. A read-only row is the only rendering the
326
- // value gets, so it wraps.
327
- full = false
328
- ): React.ReactNode => {
329
- const valueType = getValueType(field, schema);
330
- const wrapStyle: React.CSSProperties = {
331
- display: 'inline-flex',
332
- alignItems: 'center',
333
- gap: 6,
334
- minWidth: 0,
335
- maxWidth: '100%',
336
- ...(full ? { flexWrap: 'wrap' } : {}),
337
- };
338
- const textStyle: React.CSSProperties =
339
- full ?
340
- { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }
341
- : { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
342
-
343
- /* A row has to SAY when its value is an expression.
344
-
345
- Reported from the live IDE: an author accepted the *Use as expression*
346
- offer on one field and not on the other, and the two rows read exactly
347
- the same - `1 + 2` and `2 + 1`, both plain text - so there was no way
348
- to tell which had been converted. The assertion then compared the
349
- string `"1 + 2"` against the number `3` and the author had no way to
350
- see why.
351
-
352
- The same Σ the expression editor uses, so the mark means the same thing
353
- wherever it appears. */
354
- const withExpressionMarker = (rendered: React.ReactNode): React.ReactNode => {
355
- if (!(field as { is_expression?: boolean })?.is_expression) {
356
- return rendered;
357
- }
358
-
359
- return (
360
- <span style={{ ...wrapStyle, gap: 4 }}>
361
- <ReqoreIcon
362
- icon='Functions'
363
- size='tiny'
364
- /* A stable hook: reqore renders every icon as a bare
365
- `.reqore-icon`, so without this there is nothing to select the
366
- mark by — in a test or in a consumer's stylesheet. */
367
- className='reqraft-expression-marker'
368
- /* Opacity through `effect`, which is how this repo dims a reqore
369
- component (see the card's `short_desc` below); only the flex
370
- rule, which reqore has no prop for, stays inline. */
371
- effect={{ opacity: 0.7 }}
372
- style={{ flexShrink: 0 }}
373
- tooltip='This value is an expression'
374
- />
375
- {rendered}
376
- </span>
377
- );
378
- };
379
-
380
- if (valueType === 'rgbcolor') {
381
- const swatch = colorToCss(field?.value);
382
- return (
383
- <span
384
- style={
385
- swatch ?
386
- {
387
- ...wrapStyle,
388
- // a slight wash of the chosen colour behind the swatch + hex
389
- background: `color-mix(in srgb, ${swatch} 14%, transparent)`,
390
- padding: '1px 8px',
391
- borderRadius: 4,
392
- }
393
- : wrapStyle
394
- }
395
- >
396
- {swatch ?
397
- <StyledColorSwatch aria-hidden $color={swatch} $border={cDivider} />
398
- : null}
399
- <span style={textStyle}>{formatted}</span>
400
- </span>
401
- );
402
- }
403
-
404
- if (valueType === 'file') {
405
- const fileSize = getFileSize(field?.value);
406
- // Match the File editor's chip: info-blue, FileLine icon, size as the
407
- // trailing key pill (the editor renders it as a ReqoreButton badge).
408
- return (
409
- <ReqoreTag
410
- size='small'
411
- minimal
412
- intent='info'
413
- icon='FileLine'
414
- label={formatted}
415
- labelKey={fileSize !== undefined ? formatBytes(fileSize) : undefined}
416
- />
417
- );
418
- }
419
-
420
- if (valueType === 'bool' || valueType === 'boolean') {
421
- // Calm: Yes and No are words. Neither is a status — "No" is not a
422
- // failure — so neither is coloured.
423
- if (calm) {
424
- return <span style={textStyle}>{formatted}</span>;
425
- }
426
- const truthy = field?.value === true || field?.value === 'true';
427
- return (
428
- <ReqoreTag
429
- size='small'
430
- minimal
431
- intent={truthy ? 'success' : 'danger'}
432
- label={formatted}
433
- />
434
- );
435
- }
436
-
437
- // A test's cases read as CHIPS here, the same as the names in the table
438
- // this row opens (`CasesTable`, qorus-ide). `test-cases` is a
439
- // renderer-only ui_type, so it reached none of the branches above and
440
- // fell through to the generic summary, which flattens the array of case
441
- // hashes to a comma-joined string — "case_1, resolved_expected_values".
442
- // A case name is an identifier the author types into `$.` references and
443
- // skip conditions, and a chip is how the rest of the product writes an
444
- // identifier down; the table already agreed, so the collapsed row was the
445
- // only surface still printing them as prose.
446
- //
447
- // `intent` is a FILL, which is what a tag is — the hazard the other
448
- // branches warn about is only ever using an intent as a TEXT colour.
449
- //
450
- // Falls through to the plain summary unless EVERY entry yields a name: a
451
- // partly-chipped row would read as though the unnamed cases were missing.
452
- if (valueType === 'test-cases' && Array.isArray(field?.value)) {
453
- const caseNames = (field.value as unknown[]).map((item) => {
454
- const name = (item as { name?: unknown } | null | undefined)?.name;
455
- return typeof name === 'string' ? name.trim() : '';
456
- });
457
- if (caseNames.length > 0 && caseNames.every((name) => name !== '')) {
458
- return (
459
- <ReqoreControlGroup
460
- gapSize='tiny'
461
- verticalAlign='center'
462
- // An editable row is ONE line: further chips clip with the row
463
- // instead of growing it. A read-only row is the only rendering
464
- // the value gets, so it wraps and shows every case.
465
- wrap={full}
466
- style={full ? undefined : { minWidth: 0, overflow: 'hidden' }}
467
- >
468
- {caseNames.map((name, index) => (
469
- <ReqoreTag
470
- key={`${name}-${index}`}
471
- className='options-readfirst-case-tag'
472
- size='small'
473
- minimal
474
- intent='info'
475
- label={name}
476
- />
477
- ))}
478
- </ReqoreControlGroup>
479
- );
480
- }
481
- }
482
-
483
- // Code-editor read summary: replace the truncated string with a chip
484
- // showing line count + character count. The actual code renders below
485
- // the row as a monospace `<pre>` inside a `ReqoreCollapsibleContent` —
486
- // see `showCodePreview` below. Keeps the row height fixed while making
487
- // multi-line source readable without opening the full editor.
488
- if (valueType === 'code-editor' && typeof field?.value === 'string') {
489
- // An absorbed sibling has no row of its own, so a COLLAPSED host has to
490
- // carry its value or it disappears from the form entirely when closed.
491
- // It reads as a chip beside the size chip — "Qore · 62 lines" — which
492
- // is the same summary the read-only interface pages already show.
493
- return (
494
- <ReqoreControlGroup gapSize='tiny' verticalAlign='center' wrap>
495
- {(absorbedFields ?? []).map((absorbedName) => {
496
- const absorbedValue = (
497
- availableOptions?.[absorbedName] as IQorusFormField | undefined
498
- )?.value;
499
- if (absorbedValue === undefined || absorbedValue === null || absorbedValue === '') {
500
- return null;
501
- }
502
- return (
503
- <ReqoreTag
504
- key={absorbedName}
505
- className='options-readfirst-absorbed-tag'
506
- size='small'
507
- minimal
508
- label={String(absorbedValue)}
509
- tooltip={getAbsorbedLabel(absorbedName)}
510
- />
511
- );
512
- })}
513
- </ReqoreControlGroup>
514
- );
515
- }
516
-
517
- // Markdown read summary: the row prints the value's prose (the markdown
518
- // markers are stripped by `formatOptionValue`), so a multi-line document
519
- // gets a chip carrying what the one truncated line cannot -- how much
520
- // more there is -- and renders in full in the inset below, exactly as a
521
- // code value does. A single-line value needs neither: its one line IS
522
- // the whole document, already rendered as prose.
523
- if (
524
- valueType === 'markdown' &&
525
- typeof field?.value === 'string' &&
526
- isMultilineMarkdown(field.value)
527
- ) {
528
- // Prose only. The line count is metadata ABOUT the field, so it sits
529
- // under the field's name (see `lineCountNote`) rather than in front of
530
- // its content — the value column carries the value and nothing else.
531
- //
532
- // This branch is reached only when no host renderer is present; with one,
533
- // the whole value line is suppressed in favour of the rendered document
534
- // below (see `showMarkdownPreview` at the value span).
535
- return <span style={textStyle}>{formatted}</span>;
536
- }
537
-
538
- // Selected allowed-value / enum item with a logo (e.g. language images):
539
- // render it beside the value, where it belongs — not as the field's icon.
540
- const allowedImage = getAllowedValueImage(field?.value, schema);
541
- if (allowedImage) {
542
- return (
543
- <span style={wrapStyle}>
544
- <ReqoreIcon image={allowedImage} size='16px' style={{ flexShrink: 0 }} />
545
- <span style={textStyle}>{formatted}</span>
546
- </span>
547
- );
548
- }
549
-
550
- // The same, for an option whose mark is an ICON rather than a logo. Only
551
- // images were drawn here, so a list whose entries are told apart by their
552
- // icon — every interface kind is a bare word, `Service`, `Job`, `Qog` —
553
- // showed the mark while choosing and lost it the moment the row went
554
- // read-only, which is where a reader spends most of their time.
555
- const allowedIcon = getAllowedValueIcon(field?.value, schema);
556
- if (allowedIcon) {
557
- return (
558
- <span style={wrapStyle}>
559
- <ReqoreIcon icon={allowedIcon as any} size='16px' style={{ flexShrink: 0 }} />
560
- <span style={textStyle}>{formatted}</span>
561
- </span>
562
- );
563
- }
564
-
565
- // Template value ($local:…): the read-only template picker — the SAME chip
566
- // TemplateField renders when disabled, so a template reads identically here
567
- // and in the editor (qorus-ide intent scheme: info / qorus purple).
568
- //
569
- // A field may also declare templates OF ITS OWN, and a value drawn from
570
- // that list is just as much a template even when it does not look like
571
- // `$name:key` — `isValueTemplate` is a guess at the shape of the built-in
572
- // grammar, and a host's own grammar (a Qorus test's `$.order.id`) fails it.
573
- // Asking the field's list whether it CONTAINS the value settles it without
574
- // guessing at syntax, and hands back the entry whose label the chip prints,
575
- // so a reference reads as the name it was chosen by here as well as in the
576
- // editor rather than as a raw path.
577
- const ownTemplates = (schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates;
578
- // The grammar the field declares its references in, when it speaks one of
579
- // its own — the same schema key its rich-text editor is given.
580
- const templateGrammar = (schema as TFieldWithOwnTemplates | undefined)?.templateToken;
581
- if (
582
- typeof field?.value === 'string' &&
583
- !(field as { is_expression?: boolean }).is_expression &&
584
- (isValueTemplate(field.value) || !!findTemplate(ownTemplates ?? {}, field.value))
585
- ) {
586
- return <ReadOnlyTemplateTag value={field.value} templates={ownTemplates} size='small' />;
587
- }
588
-
589
- // A LIST whose elements are rich-text envelopes is a list of strings, so it
590
- // reads as those strings. Without this the row fell through to the generic
591
- // list summary and printed a bare count ("1 item") above the structured
592
- // tree, never the address itself.
593
- if (
594
- Array.isArray(field?.value) &&
595
- field.value.some((item) => richtextItemText(item) !== undefined)
596
- ) {
597
- const texts = field.value.map((item) => richtextItemText(item) ?? String(item ?? ''));
598
- return <span style={textStyle}>{texts.filter((text) => text !== '').join(', ')}</span>;
599
- }
600
-
601
- // Richtext read-first summary: inline $-chips for embedded template tags
602
- // with the prose around them (the IDE intent scheme via getTemplateTagStyle),
603
- // kept lightweight instead of mounting the full editor — that lives in the
604
- // expanded card. Pure-text richtext (no tags) falls through to the plain
605
- // `formatted` string and keeps the single-line ellipsis.
606
- if (valueType === 'richtext' && Array.isArray(field?.value)) {
607
- const segments = richtextToSegments(field.value as never);
608
- if (segments.some((segment) => segment.kind === 'tag')) {
609
- return <TemplateText segments={segments} templates={ownTemplates} full={full} />;
610
- }
611
- }
612
-
613
- // An expression reads as DPQL wherever it is shown without being edited:
614
- // the same rendering its Explain panel and Preview use — monospace,
615
- // coloured by the language server, template references as chips — so a
616
- // collapsed row and the editor it opens never disagree about what the
617
- // value looks like. See `DpqlRendering`.
618
- if ((field as { is_expression?: boolean })?.is_expression && formatted) {
619
- return withExpressionMarker(
620
- <span style={{ minWidth: 0, flex: '1 1 auto', ...(full ? {} : { overflow: 'hidden' }) }}>
621
- <DpqlRendering text={formatted} templates={ownTemplates} />
622
- </span>
623
- );
624
- }
625
-
626
- /* A value that MENTIONS a reference rather than being one.
627
-
628
- The branch above answers "is this value a template?", and a predicate is
629
- not: `$._case.mode != 'simulate'` is a comparison the author wrote with a
630
- reference inside it. So it fell all the way through to the plain string
631
- and the row printed the engine's own spelling of a value the author had
632
- picked by name — the one thing the template mechanism exists to stop.
633
- Reported against the Skip-when field of a test case, where the list
634
- above the drawer already named it and the drawer contradicted it.
635
-
636
- Drawn from `formatted` rather than from the raw value, so everything the
637
- formatter already did — a joined list, a sensitive value's dots — still
638
- holds, and only the references inside the resulting line become chips.
639
- The author's own text around them survives exactly as typed. */
640
- const templateSegments =
641
- typeof formatted === 'string' && formatted ?
642
- templateTextSegments(formatted, ownTemplates, templateGrammar)
643
- : [];
644
- if (templateSegments.some((segment) => segment.kind === 'tag')) {
645
- return withExpressionMarker(
646
- <TemplateText segments={templateSegments} templates={ownTemplates} full={full} />
647
- );
648
- }
649
-
650
- return withExpressionMarker(full ? <span style={textStyle}>{formatted}</span> : formatted);
651
- };
652
-
653
- const schema = options?.[optionName];
654
- const label = schema?.display_name || optionName;
655
- const injectedOptionActions = React.useMemo<IReqorePanelAction[]>(
656
- () =>
657
- schema ?
658
- resolveOptionActions(optionActions, {
659
- name: optionName,
660
- schema,
661
- value: availableOptions?.[optionName],
662
- }).filter((action) => action.show !== false)
663
- : [],
664
- [availableOptions?.[optionName], optionActions, optionName, schema]
665
- );
666
- // Which injected actions stay as inline buttons, and which move into the
667
- // overflow menu — see `splitOptionActions`.
668
- const [inlineOptionActions, menuOptionActions] = React.useMemo<
669
- [IReqorePanelAction[], IReqorePanelAction[]]
670
- >(
671
- () => splitOptionActions(injectedOptionActions, !!collapseOptionActions),
672
- [collapseOptionActions, injectedOptionActions]
673
- );
674
- const injectedOptionActionMenuItems = React.useMemo<IReqoreDropdownItem[]>(
675
- () => menuOptionActions.map(optionActionAsMenuItem),
676
- [menuOptionActions]
677
- );
678
- const renderInjectedOptionAction = (
679
- action: IReqorePanelAction,
680
- index: number,
681
- size: 'tiny' | 'small' = 'small'
682
- ) => {
683
- // `show` drives visibility here instead of reaching the DOM. The classic
684
- // path hands these to ReqorePanel, which honours `show: 'hover'` itself;
685
- // the compact slots render plain buttons, so 'hover' becomes a CSS gate on
686
- // the containing row/card (see compactRowStyles) — same config, same
687
- // behaviour in both modes.
688
- // `size` is destructured out and deliberately NOT honoured: the row owns
689
- // its action strip's rhythm, and a consumer-supplied size made the
690
- // injected button visibly smaller than the revert / more / confirm
691
- // buttons beside it. The contextual `size` argument is the row's own
692
- // (small when editing or in a card, tiny on a read row), so injected
693
- // actions always match their neighbours.
694
- const {
695
- label: actionLabel,
696
- onClick,
697
- show,
698
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
699
- size: _ignoredSize,
700
- className,
701
- ...actionProps
702
- } = action;
703
-
704
- return (
705
- <ReqoreButton
706
- key={`${optionName}-injected-action-${index}`}
707
- size={size}
708
- minimal
709
- flat
710
- fixed
711
- {...actionProps}
712
- className={`${className ? `${className} ` : ''}options-injected-action${
713
- show === 'hover' ? ' options-injected-action-hover' : ''
714
- }`}
715
- onClick={(event: React.MouseEvent<HTMLElement>) => {
716
- event.stopPropagation();
717
- onClick?.();
718
- }}
719
- >
720
- {actionLabel}
721
- </ReqoreButton>
722
- );
723
- };
724
- const required = !!(schema?.required || schema?.required_groups);
725
- // The asterisk says "you have to fill this in". Read-only, nobody does. The
726
- // calm look draws none at all: an empty required field says so in its
727
- // value column instead ("Required · add a value"), and a filled one has
728
- // nothing left to ask for.
729
- const requiredMark = required && !readOnly && !calm;
730
- const removable =
731
- !readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
732
- const changed = !hidden && !readOnly && hasOptionChanged(optionField?.value, optionName);
733
- // "Has a value" = set to anything non-empty. Drives the edit-row Clear
734
- // button and the cluster node's filled state (see `memberSet`).
735
- /* "Is there anything to clear?" — this predicate has exactly one job, and
736
- both of its uses are the Clear-value button.
737
-
738
- An empty list is not a value. `[]` is neither `undefined`, `null` nor `''`,
739
- so a field whose last item had just been removed went on offering to clear
740
- it, and the confirmation asked whether to clear nothing. A test's cases
741
- table is where this shows: delete the last case and a red clear-all sat
742
- above an empty table, guarding a no-op.
743
-
744
- Deliberately arrays only. An empty hash is arguable — "present but empty"
745
- can be a real state for a hash — and nothing has demonstrated the same
746
- defect there, so widening this predicate would be a guess. */
747
- const hasValue =
748
- optionField?.value !== undefined &&
749
- optionField?.value !== null &&
750
- optionField?.value !== '' &&
751
- !(Array.isArray(optionField.value) && optionField.value.length === 0);
752
- // Required-group membership shows a PERSISTENT chip on every member: amber
753
- // "One of" while the group is unmet (tap → flash siblings), then a muted-green
754
- // resolution once satisfied — "Covers" on the field that satisfied it,
755
- // "Covered by <X>" on the rest. The resolution folds in what used to be a
756
- // separate value-slot note.
757
- const memberGroups: string[] = (schema?.required_groups as string[]) || [];
758
- const unmetGroups = memberGroups.filter(
759
- (groupName) => !requiredGroupsInfo.satisfiedBy[groupName]
760
- );
761
- const coveredByGroup = memberGroups.find(
762
- (groupName) =>
763
- requiredGroupsInfo.satisfiedBy[groupName] &&
764
- requiredGroupsInfo.satisfiedBy[groupName] !== optionName
765
- );
766
- const coveredByLabel =
767
- coveredByGroup && !schema?.required ?
768
- (options?.[requiredGroupsInfo.satisfiedBy[coveredByGroup] as string]
769
- ?.display_name as string) || requiredGroupsInfo.satisfiedBy[coveredByGroup]
770
- : undefined;
771
- // Unmet → drive the chip off the unmet groups; resolved → keep listing the
772
- // members so the "covers / covered by" chip stays a locate-the-siblings
773
- // dropdown rather than going inert.
774
- const chipGroups = unmetGroups.length ? unmetGroups : memberGroups;
775
- const groupResolved = !!memberGroups.length && !unmetGroups.length;
776
- const requiredGroupChip =
777
- !hidden && !readOnly && memberGroups.length && !schema?.required ?
778
- <span
779
- style={{ display: 'inline-flex' }}
780
- role='presentation'
781
- onClick={(e) => e.stopPropagation()}
782
- onKeyDown={(e) => e.stopPropagation()}
783
- onMouseEnter={() =>
784
- setHighlightedOptions(requiredGroupsInfo.members[chipGroups[0]] || [])
785
- }
786
- onMouseLeave={() => setHighlightedOptions([])}
787
- >
788
- <ReqoreDropdown
789
- className='options-readfirst-required-group'
790
- size='tiny'
791
- minimal
792
- flat
793
- compact
794
- effect={calm ? undefined : { uppercase: true, spaced: 1 }}
795
- // Calm: a met group is nothing to point at — only the unmet one is
796
- // coloured.
797
- intent={
798
- groupResolved ?
799
- calm ? undefined
800
- : 'success'
801
- : 'warning'
802
- }
803
- icon={groupResolved ? 'CheckLine' : 'LinkM'}
804
- label={
805
- !groupResolved ? 'One of'
806
- : coveredByLabel ?
807
- `Covered by “${coveredByLabel}”`
808
- : 'Covers'
809
- }
810
- filterable={
811
- chipGroups.reduce((n, g) => n + requiredGroupsInfo.members[g].length, 0) > 6
812
- }
813
- items={chipGroups.flatMap((groupName): IReqoreDropdownItem[] => [
814
- ...(chipGroups.length > 1 ?
815
- [{ divider: true, label: groupName, dividerAlign: 'left' } as IReqoreDropdownItem]
816
- : []),
817
- ...requiredGroupsInfo.members[groupName].map((member): IReqoreDropdownItem => ({
818
- label: (options?.[member]?.display_name as string) || member,
819
- icon: member === optionName ? 'MapPinLine' : 'ArrowRightLine',
820
- selected: member === optionName,
821
- disabled: member === optionName,
822
- onClick: member === optionName ? undefined : () => flashOption(member),
823
- })),
824
- ])}
825
- />
826
- </span>
827
- : null;
828
- const fixedAllowedValueOption = isFixedCompactAllowedValueOption(schema);
829
- const editType =
830
- (fixedAllowedValueOption ?
831
- (schema?.type as string) || (schema?.ui_type as string)
832
- : (schema?.ui_type as string) || (schema?.type as string)) ?? '';
833
- // Scalars edit in place inside the row; complex fields (tall or nested
834
- // editors) still open the expanded card below.
835
- const inlineEditableNow =
836
- !readOnly &&
837
- !schema?.arg_schema &&
838
- !(operators && size(operators)) &&
839
- // Expression fields open the builder/DPQL editor — too tall for inline.
840
- !(optionField as { is_expression?: boolean }).is_expression &&
841
- // A choice with per-option logos (e.g. language) reads better collapsed.
842
- !optionHasImages(schema) &&
843
- !COMPACT_COMPLEX_TYPES.has(editType);
844
-
845
- /* Which of the two editors a row gets is settled when the row OPENS.
846
- *
847
- * Every term above reads the VALUE or a schema derived from it, so the
848
- * answer can change while the author is inside the control — and the two
849
- * branches are different subtrees, so a changed answer does not restyle
850
- * the editor, it unmounts one and mounts the other. Everything the old one
851
- * held goes with it: what was typed, the caret, and any state the editor
852
- * keeps about how it was entered.
853
- *
854
- * `is_expression` is the term that actually fires, and it fires on the one
855
- * interaction that exists to keep the author where they are: accepting
856
- * "Use as expression" is the moment the value becomes an expression, and
857
- * the expression shell opens on its Text view precisely because the author
858
- * has just written the expression as text. The remount discarded that and
859
- * dropped them into the visual builder, so the sentence they had typed was
860
- * nowhere on screen. Clicking Text then appeared to work and bounced back,
861
- * because the next value change remounted the row again.
862
- *
863
- * So the row keeps the editor it opened with for as long as it is open,
864
- * and asks the question again when it is next opened — the same trade
865
- * `editEntryValue` above makes for a different reason, and the one
866
- * qorus-ide's `referenceEditorLatch` makes for this exact one. A field that
867
- * already holds an expression when it is opened still gets the card; it is
868
- * only a field that BECOMES one under the author that stays put.
869
- *
870
- * Held in a ref and settled during render rather than in an effect: an
871
- * effect lands one render late, and one render late is a visible flash of
872
- * the wrong editor — which is the thing being fixed.
873
- */
874
- const inlineEditableLatch = React.useRef<boolean | undefined>(undefined);
875
- /* Whether THIS opening was the author asking to change the value.
876
- *
877
- * A click (or Enter/Space) on a closed row means "let me edit this"; a row
878
- * opened by the form itself — `initialExpandedOptions` from an address, the
879
- * first-attention row, a diagram opening the step it is showing — means
880
- * "here it is". Only the first answers the question the picker answers, so
881
- * only the first opens one (see the caret effect below). A ref, because it
882
- * is read by the effect that runs after the open and must not itself cause
883
- * a render. */
884
- const openedByAuthor = React.useRef(false);
885
- if (!isExpanded) {
886
- inlineEditableLatch.current = undefined;
887
- } else if (inlineEditableLatch.current === undefined) {
888
- inlineEditableLatch.current = inlineEditableNow;
889
- }
890
- const inlineEditable = isExpanded ? !!inlineEditableLatch.current : inlineEditableNow;
891
-
892
- /* The author's intent survives until the row is closed again, and no longer.
893
- *
894
- * An EFFECT, keyed on the row actually closing, rather than a line in the
895
- * render body: between the click and the row opening there is a committed
896
- * render that still reads `expandedOptions` without this row in it (the
897
- * context propagates a beat after the handler runs), and clearing the flag
898
- * on every collapsed render cleared it in that gap — the picker then never
899
- * opened, which is the defect this whole mechanism exists to fix. Keyed on
900
- * `isExpanded`, the transient render is not a change and the effect does
901
- * not run; a genuine collapse is, and it does. */
902
- React.useEffect(() => {
903
- if (!isExpanded) {
904
- openedByAuthor.current = false;
905
- }
906
- }, [isExpanded]);
907
-
908
- /* The instruction the row gives its editor when it opens into a picker.
909
- *
910
- * State, not a call on the control: reqore's popover binds its own click
911
- * listener in an effect keyed on the trigger element arriving, so a
912
- * synthetic click dispatched from the row's own effect lands before
913
- * anything is listening and does nothing at all. The editor opening itself
914
- * from a prop has no such ordering to lose. */
915
- const [openThePicker, setOpenThePicker] = React.useState(false);
916
- React.useEffect(() => {
917
- // One shot: true for the commit the editor acts on, false again after.
918
- if (openThePicker) {
919
- setOpenThePicker(false);
920
- }
921
- }, [openThePicker]);
922
-
923
- // Put the caret in the row's editor when the row opens, so the reader can
924
- // answer straight away (the prototype's tap-to-edit feel) — and so
925
- // `autoFocusFirstRequired` means what it says.
926
- //
927
- // Whatever control the row OFFERS: `findRowFocusTarget` knows a pick-one is
928
- // a set of `[tabindex]` checkboxes and a selector is a `<button>`, which a
929
- // text-only scan skipped — so the engine opened the right row and the caret
930
- // never moved. That is most of the first questions a form asks.
931
- //
932
- // The row is revealed before the caret lands in it, `block: 'nearest'` on
933
- // the ROW. A caret nobody can see is not a caret: a form opened by a click
934
- // usually mounts below the fold, and focusing something 700px down changes
935
- // nothing on screen while the next keystroke goes somewhere invisible.
936
- // `nearest` is by spec a no-op for a row already in view, so a form on
937
- // screen never jumps, and it is the row that carries the label saying what
938
- // the question is. A caller that wants the row opened WITHOUT the caret
939
- // has `expandFirstRequired` for exactly that.
940
- //
941
- // A MutationObserver, not a timer: a host editor that resolves a catalogue
942
- // before it can draw a control mounts it several commits after this effect
943
- // runs, and there is no fixed time to wait for it — the control appearing
944
- // IS the event. The 60ms timer this replaces missed every such editor, and
945
- // fired too early or too late for every other one. One shot, and it gives
946
- // up the moment the caret belongs to someone outside this row, so it can
947
- // never pull a reader out of a control they moved into themselves.
948
- const editorRef = React.useRef<HTMLDivElement>(null);
949
- const rowRef = React.useRef<HTMLDivElement>(null);
950
- React.useEffect(() => {
951
- if (!isExpanded) return undefined;
952
- const editor = editorRef.current;
953
- if (!editor) return undefined;
954
- let settled = false;
955
- /* A control that is not a text box holds the caret PROVISIONALLY. An editor that
956
- takes text can mount after the row opens (a code editor loads late), and until
957
- it does the only control there is its chrome: a fullscreen toggle. When the
958
- text box arrives it takes the caret, unless the reader has moved it since. A
959
- row that never grows one (a pick-one) simply keeps its control. */
960
- let provisional: HTMLElement | null = null;
961
- const takeCaret = (): boolean => {
962
- if (settled) return true;
963
- const active = document.activeElement as HTMLElement | null;
964
- if (provisional) {
965
- if (active !== provisional) {
966
- // The reader moved the caret themselves. Leave it there.
967
- settled = true;
968
- return true;
969
- }
970
- const textEntry = findRowFocusTarget(editor, { textEntryOnly: true });
971
- if (!textEntry) return false;
972
- textEntry.focus({ preventScroll: true });
973
- // An editor's text box can be in the DOM before it can take focus (Monaco's
974
- // edit area, mid-layout), and `focus()` then does nothing. Only a caret
975
- // that landed counts; otherwise try again on the editor's next change.
976
- if (document.activeElement !== textEntry) return false;
977
- settled = true;
978
- return true;
979
- }
980
- if (active && active !== document.body && !rowRef.current?.contains(active)) {
981
- // Someone else holds the caret — a field the reader moved into while
982
- // this row's editor was still arriving. Leave it where it is.
983
- settled = true;
984
- return true;
985
- }
986
- // A card hands the caret only to where text goes (see `findRowFocusTarget`).
987
- const control = findRowFocusTarget(editor, { textEntryOnly: !inlineEditable });
988
- if (!control) return false;
989
- // The least scroll that shows the row clear of the pinned box header,
990
- // and none when it is already in view (see `revealRow`).
991
- if (rowRef.current) {
992
- revealRow(rowRef.current, 'auto');
993
- }
994
- // `preventScroll`, because the reveal above has already put the row
995
- // where it belongs; letting focus scroll as well moves it twice.
996
- control.focus({ preventScroll: true });
997
- // A control that cannot take focus yet (still laying out) did not take
998
- // the caret: try again on the editor's next change.
999
- if (document.activeElement !== control) return false;
1000
- /* …and if the control the row offers is a PICKER, open it.
1001
- *
1002
- * A closed picker prints the value it holds, which is exactly what the
1003
- * read row printed — so the row opened, nothing on screen changed, and
1004
- * changing the value still cost a second click. Read-first is the
1005
- * COLLAPSED row's job; an open row is the editor.
1006
- *
1007
- * Narrow on purpose, and each term is a case where read-first is still
1008
- * right: only a row the author opened themselves (a form that opens a
1009
- * row to SHOW it must not throw a list over it), only an inline scalar
1010
- * row (a complex field opens a card that is a form in its own right —
1011
- * its first control is one of several and nobody asked for it), and
1012
- * only a control that declares itself a picker, so nothing else is
1013
- * ever clicked on the author's behalf. */
1014
- if (openedByAuthor.current && inlineEditable && isClosedPickerTrigger(control)) {
1015
- openedByAuthor.current = false;
1016
- setOpenThePicker(true);
1017
- settled = true;
1018
- return true;
1019
- }
1020
- if (findRowFocusTarget(editor, { textEntryOnly: true }) !== control) {
1021
- // Not a text box: hold it until one arrives (see `provisional`).
1022
- provisional = control;
1023
- return false;
1024
- }
1025
- settled = true;
1026
- return true;
1027
- };
1028
- if (takeCaret() || typeof MutationObserver === 'undefined') return undefined;
1029
- const observer = new MutationObserver(() => {
1030
- if (takeCaret()) observer.disconnect();
1031
- });
1032
- // Attributes too: an editor's text box can become focusable by an attribute or
1033
- // style change after it is inserted (see the `focus()` checks above).
1034
- observer.observe(editor, { childList: true, subtree: true, attributes: true });
1035
- return () => observer.disconnect();
1036
- }, [isExpanded, optionName, inlineEditable]);
1037
-
1038
- // What the field held when it was opened — the baseline Cancel restores.
1039
- //
1040
- // Deliberately NOT `originalValue`, which is the whole form's load-time
1041
- // snapshot. That answers "undo every change ever made to this field", which
1042
- // is a different question from "forget what I just typed"; a Cancel sitting
1043
- // next to Done has to mean the second one, or it silently throws away edits
1044
- // the user made earlier and had already accepted.
1045
- //
1046
- // The dependency list is `isExpanded` alone on purpose: adding the value
1047
- // would re-take the snapshot on every keystroke, leaving nothing to cancel.
1048
- const editEntryValue = React.useRef<{ value: any; type?: string } | undefined>(undefined);
1049
- React.useEffect(() => {
1050
- if (isExpanded) {
1051
- editEntryValue.current = { value: optionField?.value, type: optionField?.type };
1052
- } else {
1053
- editEntryValue.current = undefined;
1054
- }
1055
- }, [isExpanded]);
1056
-
1057
- // True while the open editor holds something other than what it opened with.
1058
- const editedSinceOpened =
1059
- isExpanded &&
1060
- !!editEntryValue.current &&
1061
- !isEqual(editEntryValue.current.value, optionField?.value);
1062
-
1063
- // Discard whatever was typed since the field was opened and close it. Used
1064
- // by both the Cancel button and Escape, so the two cannot diverge.
1065
- const cancelEdit = React.useCallback(() => {
1066
- const snapshot = editEntryValue.current;
1067
- if (snapshot && !isEqual(snapshot.value, optionField?.value)) {
1068
- handleValueChange(optionName, snapshot.value, snapshot.type);
1069
- }
1070
- toggleExpandedOption(optionName);
1071
- }, [handleValueChange, optionField?.value, optionName, toggleExpandedOption]);
1072
-
1073
- // The sub-schema describing this field's value, when it has one. A value the
1074
- // form knows the shape of is previewed THROUGH that shape (see
1075
- // `SchemaDataView`); only genuinely undescribed data falls through to the
1076
- // untyped data tree.
1077
- //
1078
- // The server does NOT inline that sub-schema. `encodeFieldsForUi()` registers
1079
- // it and sends an id string in its place, which the consumer fetches back —
1080
- // exactly what AutoFormField does to build the editor ("Some arg schemas are
1081
- // not provided as objects, but as strings so we need to fetch them"). A
1082
- // preview that understood only the inline object therefore fell through to
1083
- // the untyped tree for every field a real server describes: the schema view
1084
- // worked against hand-written story fixtures and nowhere else.
1085
- //
1086
- // Resolved here rather than beside the preview it feeds: the preview renders
1087
- // below an early return, so a hook placed there would run conditionally.
1088
- const rawArgSchema = (schema as { arg_schema?: IQorusFormSchema | string } | undefined)
1089
- ?.arg_schema;
1090
- const argSchemaId = typeof rawArgSchema === 'string' ? rawArgSchema : undefined;
1091
- const [resolvedArgSchema, setResolvedArgSchema] = React.useState<IQorusFormSchema | undefined>(
1092
- undefined
1093
- );
1094
-
1095
- // Only worth a request when there is a described value to preview: a row whose
1096
- // value is unset or scalar renders no inset, so fetching its schema would buy
1097
- // a round trip per row and show nothing. `query` caches GETs, so rows sharing
1098
- // an id (every row of one list) cost one request between them.
1099
- const valueLooksStructured = !!optionField?.value && typeof optionField.value === 'object';
1100
-
1101
- React.useEffect(() => {
1102
- if (!argSchemaId || !valueLooksStructured) {
1103
- return undefined;
1104
- }
1105
- let cancelled = false;
1106
- (async () => {
1107
- const response = await queryUnlessCancelled<IQorusFormSchema>({
1108
- url: `dataprovider/arg_schemas/${argSchemaId}`,
1109
- method: 'GET',
1110
- });
1111
- // A failed fetch is not an error state here: the preview simply stays the
1112
- // untyped tree, which is what it was before the schema view existed. Nor
1113
- // is an abandoned one (no response).
1114
- if (!cancelled && response?.ok) {
1115
- setResolvedArgSchema(response.data as IQorusFormSchema);
1116
- }
1117
- })();
1118
- return () => {
1119
- cancelled = true;
1120
- };
1121
- }, [argSchemaId, valueLooksStructured]);
1122
-
1123
- const previewArgSchema =
1124
- argSchemaId ? resolvedArgSchema : (rawArgSchema as IQorusFormSchema | undefined);
1125
- const previewWithSchema = React.useMemo(
1126
- () => canRenderWithSchema(optionField?.value, previewArgSchema),
1127
- [optionField?.value, previewArgSchema]
1128
- );
1129
-
1130
- // The form-load revert: back to the value the FORM was loaded with.
1131
- //
1132
- // In an open editor this is offered only when it would do something
1133
- // `cancelEditButton` does not — that is, when the field was already
1134
- // modified BEFORE it was opened, so "undo this edit" and "undo every edit"
1135
- // are different answers. On the usual path (open an untouched field, type,
1136
- // change your mind) the two are identical, and two buttons with the same
1137
- // effect and different names are worse than one.
1138
- //
1139
- // It is unconditional on the READ row, where it renders inline in the
1140
- // status-dot column — there is no edit in progress there to cancel, so the
1141
- // whole-field revert is the only undo that makes sense.
1142
- const revertButton =
1143
- changed ?
1144
- <ReqoreButton
1145
- className='options-readfirst-revert'
1146
- size='small'
1147
- flat
1148
- minimal
1149
- icon='HistoryLine'
1150
- tooltip='Revert changes'
1151
- onClick={(e: React.MouseEvent) => {
1152
- e.stopPropagation();
1153
- handleValueChange(
1154
- optionName,
1155
- originalValue.current?.[optionName]?.value,
1156
- originalValue.current?.[optionName]?.type
1157
- );
1158
- }}
1159
- />
1160
- : null;
1161
- // Clear-value: empties a set field (keeps the field; the value goes blank)
1162
- // — distinct from the read-row "Remove field" (which deletes an optional
1163
- // field entirely). Once cleared, `changed` flips on and `hasValue` off, so
1164
- // this button gives way to `revertButton` in the same slot — the morph the
1165
- // edit row's actions are designed around. Clearing is destructive even
1166
- // though it can be reverted before saving, so both the trigger and its
1167
- // confirmation use danger intent.
1168
- //
1169
- // Only editors with NO built-in clear of their own get this button —
1170
- // toggles and fixed-choice pickers. The text/number/date inputs already
1171
- // render ReqoreInput's ✕ (which itself trips `changed`, so `revertButton`
1172
- // takes over once emptied), so adding ours there would double the ✕.
1173
- const editorLacksOwnClear =
1174
- editType === 'bool' ||
1175
- editType === 'boolean' ||
1176
- // Non-creatable allowed_values render a radio/select (no input clear); a
1177
- // CREATABLE one still renders the raw input (with its own ✕), so it keeps
1178
- // its clear — matching the dispatch in AutoFormField/TemplateField.
1179
- (!!size(schema?.allowed_values) && !schema?.allowed_values_creatable);
1180
- const confirmClearValue = (event?: React.MouseEvent) => {
1181
- event?.stopPropagation();
1182
- confirmAction({
1183
- title: 'Clear value',
1184
- description: `Clear the current value of "${label}"?`,
1185
- confirmLabel: 'Clear value',
1186
- confirmButtonIntent: 'danger',
1187
- intent: 'danger',
1188
- onConfirm: () => handleValueChange(optionName, undefined),
1189
- });
1190
- };
1191
- const clearValueButton =
1192
- hasValue && !readOnly && editorLacksOwnClear ?
1193
- <ReqoreButton
1194
- className='options-readfirst-clear'
1195
- size='small'
1196
- flat
1197
- minimal
1198
- icon='DeleteBinLine'
1199
- intent='danger'
1200
- aria-label='Clear value'
1201
- tooltip='Clear value'
1202
- onClick={confirmClearValue}
1203
- />
1204
- : null;
1205
- // Batched commit: a changed row is a draft until Save — mark it with the
1206
- // product's Draft chip (always visible, unlike the hover-revealed revert).
1207
- const draftChip =
1208
- commitMode === 'batched' && changed ?
1209
- <ReqoreTag
1210
- className='options-readfirst-draft'
1211
- label='Draft'
1212
- intent='warning'
1213
- icon='EditLine'
1214
- size='tiny'
1215
- minimal
1216
- fixed
1217
- flat
1218
- compact
1219
- effect={calm ? undefined : { uppercase: true, spaced: 1 }}
1220
- />
1221
- : null;
1222
- const isPassiveCloseAction = fixedAllowedValueOption;
1223
- // Cancel — the counterpart to Done, and the answer to "how do I get out of
1224
- // here without keeping this". Done was the only way to leave an open field,
1225
- // so every way out committed: the check, clicking away, and Escape (which
1226
- // closed without discarding, so it committed too, silently).
1227
- //
1228
- // Shown only while there is something to discard. On an untouched field
1229
- // Cancel and Done would do exactly the same thing, and two buttons that
1230
- // differ in name but not in effect are worse than one.
1231
- //
1232
- // Not offered when the close action is passive (a fixed choice) —
1233
- // nothing can have been typed, so there is nothing to cancel.
1234
- const cancelEditButton =
1235
- !isPassiveCloseAction && editedSinceOpened ?
1236
- <ReqoreButton
1237
- className='options-readfirst-cancel'
1238
- size='small'
1239
- flat
1240
- minimal
1241
- fixed
1242
- icon='CloseLine'
1243
- intent={calm ? undefined : 'warning'}
1244
- // "Cancel edit", not "Cancel": a bare Cancel collides with the one
1245
- // in Reqore's confirmation modal, and this one is specifically about
1246
- // abandoning the edit rather than dismissing a dialog.
1247
- aria-label='Cancel edit'
1248
- tooltip='Cancel edit — discard the changes made since this field was opened'
1249
- onClick={(event: React.MouseEvent) => {
1250
- event.stopPropagation();
1251
- cancelEdit();
1252
- }}
1253
- />
1254
- : null;
1255
- // See `revertButton` above for why this is conditional in the edit row.
1256
- const openedWithFormLoadValue =
1257
- !editEntryValue.current ||
1258
- isEqual(editEntryValue.current.value, originalValue.current?.[optionName]?.value);
1259
- const editRowRevertButton = openedWithFormLoadValue ? null : revertButton;
1260
- const closeExpandedFieldButton = (
1261
- <ReqoreButton
1262
- className='options-readfirst-done'
1263
- size='small'
1264
- // Calm: a neutral button. Closing a field is not a healthy figure and
1265
- // not the page's one primary action, so it takes neither colour.
1266
- intent={!isPassiveCloseAction && !calm ? 'success' : undefined}
1267
- flat={isPassiveCloseAction}
1268
- minimal={isPassiveCloseAction}
1269
- fixed
1270
- icon={isPassiveCloseAction ? 'CloseLine' : 'CheckLine'}
1271
- aria-label={isPassiveCloseAction ? 'Close field' : 'Done'}
1272
- tooltip={isPassiveCloseAction ? 'Close field' : 'Done'}
1273
- onClick={() => toggleExpandedOption(optionName)}
1274
- />
1275
- );
1276
-
1277
- // Info tiers: Tier 1 (danger/warning + dependency hints) must be visible
1278
- // without interaction; Tier 2 (info/success, default notes) sits behind ⓘ.
1279
- const infoActive = !hidden;
1280
- type TInfoMsg = { intent?: string; title?: string; content: string };
1281
- const schemaMessages: TInfoMsg[] =
1282
- infoActive ?
1283
- (getShownSchemaMessages((schema as any)?.messages as any[], availableOptions, options).map(
1284
- (m) => ({
1285
- intent: m.intent,
1286
- title: m.title,
1287
- content: m.content,
1288
- })
1289
- ) as TInfoMsg[])
1290
- : [];
1291
- // A row states what is wrong with what it holds, read-only or not: "This
1292
- // field is required" under an empty required field, a value that is not a
1293
- // choice, an unmet dependency. A read-only reader is often the one who has
1294
- // to get the record fixed, and a view that hides why it is incomplete
1295
- // leaves them guessing. The value slot of an empty field keeps its plain
1296
- // "—" (or the default it falls back to), so the message is the only place
1297
- // the requirement is said: once, and the same way in both modes.
1298
- const fieldMessages: TInfoMsg[] =
1299
- infoActive ?
1300
- getOptionFieldMessages({
1301
- schema: options || {},
1302
- option: optionField || ({} as IQorusFormField),
1303
- name: optionName,
1304
- allOptions: availableOptions,
1305
- getType: getTypeForOption,
1306
- isRendererOnly,
1307
- calm,
1308
- readOnly,
1309
- // Until the person has been to the field, its requirement is stated plainly.
1310
- requirementPending: !visited,
1311
- }).map((m) => ({ intent: m.intent as string, content: String(m.label) }))
1312
- : [];
1313
- // TWO display channels, matching the Focus prototype:
1314
- // • dedicated schema `messages` → prominent coloured PANELS below the row;
1315
- // • everything else (validation reasons, the unmet-dependency hint, the
1316
- // default-value note) → a compact INLINE reason strip — not a panel.
1317
- const panelMessages = schemaMessages;
1318
- const inlineMessages: TInfoMsg[] = [
1319
- ...fieldMessages,
1320
- ...(infoActive && schema?.default_value_desc ?
1321
- [
1322
- {
1323
- content:
1324
- `Default: ${schema.default_value_display_name || ''} — ${schema.default_value_desc}`.trim(),
1325
- },
1326
- ]
1327
- : []),
1328
- ];
1329
- const allMsgs = [...panelMessages, ...inlineMessages];
1330
- const worstIntent =
1331
- allMsgs.some((m) => m.intent === 'danger') ? 'danger'
1332
- : allMsgs.some((m) => m.intent === 'warning') ? 'warning'
1333
- : undefined;
1334
- const intentColor =
1335
- worstIntent === 'danger' ? cDanger
1336
- : worstIntent === 'warning' ? cWarning
1337
- : undefined;
1338
- const showStripe = infoActive && !!intentColor;
1339
- const labelShortDesc = schema?.short_desc;
1340
- // The per-row ⓘ is gone: short_desc shows under the name only when the global
1341
- // "descriptions" toggle is engaged (and inside the editor when expanded).
1342
- const showLabelDesc = !!labelShortDesc && showAllDescriptions === true;
1343
-
1344
- // The `?` that opens the long-form help dialog. Shared by the read row AND
1345
- // the inline editor: opening a field used to drop it, leaving an editing row
1346
- // with no route to the field's description at all. Stops propagation because
1347
- // both labels are click targets themselves (the row expands / collapses).
1348
- const helpIcon =
1349
- fieldHasLongDescription(schema) ?
1350
- <ReqoreIcon
1351
- icon='QuestionLine'
1352
- size='12px'
1353
- effect={{ opacity: 0.55 }}
1354
- margin='left'
1355
- marginSize='tiny'
1356
- role='button'
1357
- tabIndex={-1}
1358
- aria-label='Help'
1359
- className='options-readfirst-help'
1360
- style={{ cursor: 'help' }}
1361
- onClick={(event) => {
1362
- event.stopPropagation();
1363
- handleOptionLabelClick(optionName);
1364
- }}
1365
- />
1366
- : null;
1367
-
1368
- // The gap rides on the message itself rather than on the panel around it:
1369
- // the panel is a vertical ReqoreControlGroup, which owns its own layout and
1370
- // stacks children flush, and the class rule that used to carry a 4px gap
1371
- // never reached this element at all — which is why two messages read as one
1372
- // two-tone block with no space between them.
1373
- /* A message is PROSE, so it is drawn by the host's prose renderer.
1374
-
1375
- `markdownRendererContext` says this package renders markdown "in exactly
1376
- one place — a field's description". That was one place short: a schema
1377
- message is written by the same author, in the same dialect, and it landed
1378
- here as a raw string. A Qorus warning reading "Give that step a **Fixture
1379
- Output**" showed the asterisks, and every reference inside it stayed a raw
1380
- `$.` path where the editor two lines above renders it as a named chip.
1381
-
1382
- `compact` for the same reason the row inset asks for it: this is a strip
1383
- inside a row, never a page, and markdown authored as a document opens with
1384
- a heading that would outgrow the field label above it. Without a host
1385
- renderer the string is drawn as it always was. */
1386
- const renderInfoStrip = (m: TInfoMsg, index: number) => (
1387
- <ReqoreMessage
1388
- key={`${m.content}-${index}`}
1389
- size='small'
1390
- opaque={false}
1391
- flat
1392
- // Calm: a note that only informs takes no colour — blue is kept for "a
1393
- // filter is on", green for a healthy figure. A warning or a failure
1394
- // keeps its intent: those are what a person has to act on.
1395
- intent={
1396
- (calm && (!m.intent || m.intent === 'info' || m.intent === 'success') ?
1397
- undefined
1398
- : m.intent) as never
1399
- }
1400
- title={m.title}
1401
- style={{ marginBottom: 8 }}
1402
- >
1403
- {markdownRenderer && typeof m.content === 'string' ?
1404
- markdownRenderer({ value: m.content, compact: true })
1405
- : m.content}
1406
- </ReqoreMessage>
1407
- );
1408
- const reasonColor = (intent?: string) =>
1409
- intent === 'danger' ? cDanger
1410
- : intent === 'warning' ? cWarning
1411
- : intent === 'success' ?
1412
- (theme?.intents as Record<string, string> | undefined)?.success || cInfo
1413
- : `${cMuted}cc`;
1414
-
1415
- const infoToggle = null;
1416
-
1417
- // Cluster (required-group connection) — shared by the read row, its block
1418
- // wrapper AND the inline editor, so the rail/node/highlight persist across
1419
- // view↔edit. The node is opaque (it masks the rail behind it); hovering any
1420
- // member lights up the whole group via the shared highlight state.
1421
- const clusterGroup =
1422
- clustered ? (schema?.required_groups as string[] | undefined)?.[0] : undefined;
1423
- const clusterMembers = clusterGroup ? requiredGroupsInfo.members[clusterGroup] || [] : [];
1424
- // The group is fulfilled once any member is set — the whole rail then reads
1425
- // success (not just the satisfying node).
1426
- const clusterSatisfied = !!(clusterGroup && requiredGroupsInfo.satisfiedBy[clusterGroup]);
1427
- const clusterBlockClass =
1428
- clustered ?
1429
- `readfirst-cluster-rail${clusterFirst ? ' readfirst-cluster-first' : ''}${clusterLast ? ' readfirst-cluster-last' : ''}${clusterSatisfied ? ' readfirst-cluster-satisfied' : ''}`
1430
- : '';
1431
- // The connection rail + status nodes are gone — the "One of the below is
1432
- // required" box (and the Covers / Covered-by chips) carry the grouping now.
1433
- const clusterNode = null;
1434
- const clusterHoverProps =
1435
- clustered && clusterMembers.length ?
1436
- {
1437
- onMouseEnter: () => setHighlightedOptions(clusterMembers),
1438
- onMouseLeave: () => setHighlightedOptions([]),
1439
- }
1440
- : {};
1441
-
1442
- // Items the EDITOR inside this row published — see `rowMenuContext`.
1443
- const { rowMenu, items: editorMenuItems } = useRowMenuRegistry();
1444
-
1445
- // Secondary edit actions tuck into a "More" (⋮) menu so the card header stays
1446
- // calm: Fullscreen always, plus Remove field for a removable option. Rendered
1447
- // just before the Done ✓ in both expanded layouts.
1448
- const moreMenu = (
1449
- <ReqoreDropdown
1450
- className='options-readfirst-more'
1451
- icon='More2Fill'
1452
- flat
1453
- minimal
1454
- fixed
1455
- size='small'
1456
- tooltip='More actions'
1457
- items={[
1458
- {
1459
- label: 'Edit fullscreen',
1460
- icon: 'FullscreenLine',
1461
- onClick: () => setFocusedEditing(optionName),
1462
- } as IReqoreDropdownItem,
1463
- ...(removable && !hidden ?
1464
- [
1465
- {
1466
- label: 'Remove field',
1467
- icon: 'DeleteBinLine',
1468
- intent: 'danger',
1469
- onClick: () =>
1470
- confirmAction({
1471
- title: 'Remove field',
1472
- onConfirm: () => removeSelectedOption(optionName),
1473
- }),
1474
- } as IReqoreDropdownItem,
1475
- ]
1476
- : []),
1477
- // Consumer-injected actions that did not fit inline (or collapsed
1478
- // wholesale on touch) land here — this row already has a menu, so they
1479
- // reuse it rather than adding a second one beside it.
1480
- ...injectedOptionActionMenuItems,
1481
- // ...and for the same reason, so do the editor's own actions. Without
1482
- // this an editor with affordances of its own had to draw a second ⋮
1483
- // inside the value cell, beside this one.
1484
- ...editorMenuItems,
1485
- ]}
1486
- />
1487
- );
1488
-
1489
- // The absorbed siblings' controls, rendered inside this row.
1490
- //
1491
- // Each keeps its own label so it is still identifiable — it is a different
1492
- // field, not a property of this one — but it sits inside this row's
1493
- // container, above the editor, so the pair reads as the single decision it
1494
- // is. `renderOption` is the same function the row uses for its own editor,
1495
- // so an absorbed field renders exactly as it would have on its own row.
1496
- const absorbedNodes =
1497
- absorbedFields?.length ?
1498
- absorbedFields
1499
- .map((absorbedName) => {
1500
- const absorbedField = availableOptions?.[absorbedName] as IQorusFormField | undefined;
1501
- if (!absorbedField) return null;
1502
- return (
1503
- // No label element of its own: the editor now heads itself with
1504
- // the field's name (see FieldAllowedValuesCheckGroup), so a label
1505
- // beside it printed the same word twice.
1506
- <StyledAbsorbedField key={absorbedName} className='options-readfirst-absorbed'>
1507
- <div style={{ minWidth: 0, flex: 1 }}>
1508
- {renderOption(absorbedName, absorbedField, 'small', true)}
1509
- </div>
1510
- </StyledAbsorbedField>
1511
- );
1512
- })
1513
- .filter(Boolean)
1514
- : null;
1515
-
1516
- // Declared here rather than beside the hash rows below: the `isExpanded`
1517
- // branch returns before that point, and its editor needs to know whether it is
1518
- // holding code.
1519
- const valueType = getValueType(optionField, schema);
1520
-
1521
- // How much source there is describes the FIELD, not its value, so the count
1522
- // sits under the field's name — the same rule `lineCountNote` already
1523
- // follows for a multi-line markdown value, and the same reason: the value
1524
- // column carries the value and nothing else. It renders on the read row and
1525
- // the open one alike, because "how much code is here?" is a question that
1526
- // outlives opening the editor.
1527
- //
1528
- // It is a plain monospaced note rather than a tag, and deliberately the same
1529
- // one `lineCountNote` renders: both answer "how big is this field's value?"
1530
- // in the same corner of the same column, so stating one as a chip and the
1531
- // other as text made two spellings of one idea.
1532
- const codeSizeNode =
1533
- (
1534
- valueType === 'code-editor' &&
1535
- typeof optionField?.value === 'string' &&
1536
- optionField.value !== ''
1537
- ) ?
1538
- (() => {
1539
- const { lines, chars } = describeCodeSize(optionField.value as string);
1540
- return (
1541
- <ReqoreP
1542
- className='options-readfirst-label-code-size'
1543
- size='tiny'
1544
- effect={{ opacity: 0.45, textAlign: 'left' }}
1545
- style={{ fontFamily: 'monospace' }}
1546
- >
1547
- {`${formatCodeChars(chars)} ${formatCodeLines(lines)}`}
1548
- </ReqoreP>
1549
- );
1550
- })()
1551
- : null;
1552
-
1553
- if (isExpanded && !readOnly) {
1554
- if (inlineEditable) {
1555
- const collapse = () => toggleExpandedOption(optionName);
1556
- const editingRow = (
1557
- <div
1558
- key={optionName}
1559
- ref={rowRef}
1560
- data-field={optionName}
1561
- className='readfirst-row readfirst-row-editing options-readfirst-inline options-readfirst-value'
1562
- style={
1563
- readRowHeights.current[optionName] ?
1564
- { minHeight: readRowHeights.current[optionName] }
1565
- : undefined
1566
- }
1567
- {...clusterHoverProps}
1568
- >
1569
- <StyledLabelBlock className='options-readfirst-label-block'>
1570
- <StyledRowLabel
1571
- role='button'
1572
- tabIndex={0}
1573
- aria-label={`Collapse ${label}`}
1574
- title={schema?.short_desc || undefined}
1575
- $color={cLabel}
1576
- $pointer
1577
- onClick={collapse}
1578
- onKeyDown={(event) => {
1579
- if (event.key === 'Enter' || event.key === ' ') {
1580
- event.preventDefault();
1581
- collapse();
1582
- }
1583
- }}
1584
- >
1585
- {label}
1586
- {required && !calm ?
1587
- <ReqoreIcon icon='Asterisk' color='danger' size='10px' />
1588
- : null}
1589
- {helpIcon}
1590
- </StyledRowLabel>
1591
- {/* An OPEN field always shows its short_desc — that's where the hint
1592
- is needed and there's no per-row button to reveal it (the edit
1593
- card does the same). The global descriptions toggle governs READ
1594
- rows only, so collapsing the field is what hides it again. */}
1595
- {labelShortDesc ?
1596
- <StyledLabelDesc
1597
- className='options-readfirst-label-desc'
1598
- size='small'
1599
- effect={{ opacity: 0.55 }}
1600
- >
1601
- {labelShortDesc}
1602
- </StyledLabelDesc>
1603
- : null}
1604
- {codeSizeNode}
1605
- </StyledLabelBlock>
1606
- <div
1607
- ref={editorRef}
1608
- className='options-readfirst-row-editor'
1609
- style={{ minWidth: 0 }}
1610
- onKeyDown={(event) => {
1611
- if (event.key === 'Escape') {
1612
- event.stopPropagation();
1613
- // Escape discards. It used to call `collapse()`, which closed
1614
- // the field and KEPT whatever had been typed — the one
1615
- // keystroke every editor treats as "get me out of this
1616
- // without saving" was the quietest way to commit.
1617
- cancelEdit();
1618
- }
1619
- }}
1620
- >
1621
- {/* The size chip survives into the editor.
1622
-
1623
- Opening a field replaces the whole value cell with its editor, which
1624
- for every other type is right — the editor shows the value, so a
1625
- summary of it above would say the same thing twice. Code is the
1626
- exception: the chip counts lines and characters, which the editor
1627
- does not show anywhere. Dropping it lost real information AND moved
1628
- everything under it up by the height of a chip, so the editor
1629
- appeared to jump as it opened.
1630
-
1631
- It renders here, at the top of the value cell, which is exactly where
1632
- it sits on the read row — so the switch moves nothing. */}
1633
- {/* The field's own schema messages follow it into the editor.
1634
-
1635
- A message is guidance about the value, so the moment it matters
1636
- most is while the value is being changed — and a diagnostic is
1637
- useless anywhere else: it names a line the author can only act
1638
- on with the editor in front of them. They used to render on the
1639
- READ row alone, so opening the field to fix what one said was
1640
- what made it disappear. Worse for a host that opens a field on
1641
- arrival: the message was never visible at all.
1642
-
1643
- Above the editor rather than below it, and above the absorbed
1644
- siblings, because it is the reason the author opened the row. */}
1645
- {panelMessages.length ?
1646
- <ReqoreControlGroup
1647
- className='options-readfirst-info-panel'
1648
- vertical
1649
- fluid
1650
- gapSize='normal'
1651
- >
1652
- {panelMessages.map(renderInfoStrip)}
1653
- </ReqoreControlGroup>
1654
- : null}
1655
- {/* Absorbed siblings sit at the TOP of the value cell, above the
1656
- editor they belong to — the language you are writing in is
1657
- read before the code, not after it. */}
1658
- {absorbedNodes}
1659
- {/* The editor may publish into THIS row's ⋮ — see `rowMenuContext`.
1660
- Only the editors rendered in the row are given the channel: the
1661
- fullscreen modal below has no row menu on screen, so an editor
1662
- there keeps drawing its own control. */}
1663
- {/* Scoped to the row's OWN editor: the absorbed siblings above
1664
- are other fields, which this row did not open. */}
1665
- <RowOpenPickerContext.Provider value={openThePicker}>
1666
- <RowMenuContext.Provider value={rowMenu}>
1667
- {renderOption(optionName, optionField, 'small', true)}
1668
- </RowMenuContext.Provider>
1669
- </RowOpenPickerContext.Provider>
1670
- </div>
1671
- <StyledRowActions>
1672
- {draftChip}
1673
- {/* No Required tag here: while editing, the editor's own
1674
- OptionFieldMessages strip below already says it. */}
1675
- {editRowRevertButton}
1676
- {cancelEditButton}
1677
- {clearValueButton}
1678
- {inlineOptionActions.map((action, index) =>
1679
- renderInjectedOptionAction(action, index)
1680
- )}
1681
- {moreMenu}
1682
- {closeExpandedFieldButton}
1683
- </StyledRowActions>
1684
- {/* Cluster node is rendered LAST (it's absolutely positioned, so DOM
1685
- order doesn't move it) — keeping it out of the leading cells lets
1686
- the `> div:nth-child(1/2/3)` editing-row alignment nudges land on
1687
- label / editor / actions instead of being shifted by the node. */}
1688
- {clusterNode}
1689
- </div>
1690
- );
1691
- // ALWAYS wrap the editing row in the same StyledColumn so its parent stays
1692
- // stable (the editor must not remount and lose focus mid-type). The
1693
- // message panel is NOT rendered here: the editor's own OptionFieldMessages
1694
- // strip already shows the field's messages, so a panel would duplicate
1695
- // them. The wrapper carries the cluster rail class so the connection
1696
- // persists while a member is being edited.
1697
- return (
1698
- <StyledColumn
1699
- key={optionName}
1700
- data-field={optionName}
1701
- className={clusterBlockClass || undefined}
1702
- >
1703
- {editingRow}
1704
- {/* Focused editing for an INLINE row. The edit card wraps its editor
1705
- in <FocusedEditing>, which renders the field in place AND in the
1706
- modal; an inline row already renders its editor in the row grid,
1707
- so it mounts the modal alone. Without this the More menu's "Edit
1708
- fullscreen" set the state and nothing appeared — the modal simply
1709
- had no mount point on this branch. */}
1710
- {focusedEditing === optionName ?
1711
- <FocusedEditing
1712
- isFullscreen
1713
- modalOnly
1714
- onClose={() => setFocusedEditing(undefined)}
1715
- description={(schema?.display_name as string) || optionName}
1716
- >
1717
- <Description
1718
- longDescription={schema?.desc}
1719
- shortDescription={schema?.short_desc}
1720
- longDescriptionShownByDefault
1721
- />
1722
- {renderOption(optionName, optionField)}
1723
- </FocusedEditing>
1724
- : null}
1725
- </StyledColumn>
1726
- );
1727
- }
1728
- // Card chrome: badge / actions / tags render here — the row only fits
1729
- // icon/image + the intent stripe.
1730
- const schemaBadge = (schema as { badge?: unknown } | undefined)?.badge;
1731
- const cardBadges =
1732
- schemaBadge !== undefined && schemaBadge !== null ?
1733
- ((Array.isArray(schemaBadge) ? schemaBadge : [schemaBadge]) as unknown[])
1734
- : [];
1735
- const cardActions =
1736
- (schema as { actions?: unknown[] } | undefined)?.actions?.filter(
1737
- (action) => !!action && typeof action === 'object'
1738
- ) || [];
1739
- const cardTags = ((schema as { tags?: unknown[] } | undefined)?.tags || []) as object[];
1740
- const schemaIntentColor =
1741
- schema?.intent ?
1742
- (theme?.intents as Record<string, string> | undefined)?.[schema.intent as string]
1743
- : undefined;
1744
- return (
1745
- <StyledEditCard
1746
- key={optionName}
1747
- ref={rowRef}
1748
- data-field={optionName}
1749
- className={
1750
- COMPACT_SINGLE_VALUE_TYPES.has(editType) ?
1751
- 'options-readfirst-card options-readfirst-card-single'
1752
- : 'options-readfirst-card'
1753
- }
1754
- $bg={cHover}
1755
- $border={schemaIntentColor ? `${schemaIntentColor}66` : `${cInfo}66`}
1756
- $calm={calm}
1757
- >
1758
- <div
1759
- style={{
1760
- display: 'flex',
1761
- alignItems: 'flex-start',
1762
- justifyContent: 'space-between',
1763
- gap: 12,
1764
- }}
1765
- >
1766
- <StyledCardHeading>
1767
- <StyledCardLabel $color={cLabel}>
1768
- {(
1769
- (schema as { icon?: string } | undefined)?.icon ||
1770
- (schema as { image?: string } | undefined)?.image
1771
- ) ?
1772
- <ReqoreIcon
1773
- icon={(schema as { icon?: string } | undefined)?.icon as never}
1774
- image={(schema as { image?: string } | undefined)?.image}
1775
- size='14px'
1776
- />
1777
- : null}
1778
- <span>
1779
- {label}
1780
- {required && !calm ?
1781
- <ReqoreIcon icon='Asterisk' color='danger' size='10px' />
1782
- : null}
1783
- {/* The `?` belongs here too. The read row and the inline
1784
- editor both carry it; the edit CARD did not — and the card
1785
- is the path every field with an `arg_schema` takes, so a
1786
- nested form like an auth profile's Context Mapping opened
1787
- with its long description unreachable. Expanding a field
1788
- is exactly when its explanation is wanted, so losing the
1789
- affordance on the way in is backwards. */}
1790
- {helpIcon}
1791
- </span>
1792
- {cardBadges.map((badge, index) =>
1793
- typeof badge === 'object' ?
1794
- <ReqoreTag
1795
- size='small'
1796
- minimal
1797
- key={index}
1798
- className='options-readfirst-card-badge'
1799
- {...(badge as object)}
1800
- />
1801
- : <ReqoreTag
1802
- size='small'
1803
- minimal
1804
- key={index}
1805
- className='options-readfirst-card-badge'
1806
- label={badge as string | number}
1807
- />
1808
- )}
1809
- </StyledCardLabel>
1810
- {schema?.short_desc ?
1811
- <ReqoreP size='small' effect={{ opacity: 0.6 }} style={{ marginTop: 2 }}>
1812
- {schema.short_desc}
1813
- </ReqoreP>
1814
- : null}
1815
- {cardTags.length ?
1816
- <ReqoreTagGroup size='small' className='options-readfirst-card-tags'>
1817
- {cardTags.map((tag, index) => (
1818
- <ReqoreTag size='small' minimal key={index} {...(tag as object)} />
1819
- ))}
1820
- </ReqoreTagGroup>
1821
- : null}
1822
- </StyledCardHeading>
1823
- <ReqoreControlGroup fixed verticalAlign='center'>
1824
- {cardActions.map((action, index) => {
1825
- const { label: actionLabel, ...actionProps } = action as {
1826
- label?: string;
1827
- } & Record<string, unknown>;
1828
- return (
1829
- <ReqoreButton
1830
- size='small'
1831
- minimal
1832
- flat
1833
- fixed
1834
- key={index}
1835
- className='options-readfirst-card-action'
1836
- {...(actionProps as object)}
1837
- >
1838
- {actionLabel}
1839
- </ReqoreButton>
1840
- );
1841
- })}
1842
- {inlineOptionActions.map((action, index) =>
1843
- renderInjectedOptionAction(action, index)
1844
- )}
1845
- {/* Revert and Cancel, in the same order and with the same meaning
1846
- as the inline row's strip.
1847
-
1848
- They were absent here, and which shape a field opens into is
1849
- decided by its TYPE — so an `any`-typed option (a test
1850
- assertion's Expected Value, a Qog action state option) opened
1851
- into this card and had no way out that discarded: Done
1852
- committed, clicking away committed, and Escape did nothing at
1853
- all because the handler is on the inline row's editor. The
1854
- only exit from a card kept whatever had been typed. */}
1855
- {editRowRevertButton}
1856
- {cancelEditButton}
1857
- {/* Clear-value sits before the More menu — the card analog of the
1858
- inline row's Clear. Empties the value (keeps the field). */}
1859
- {hasValue && !readOnly ?
1860
- <ReqoreButton
1861
- size='small'
1862
- icon='DeleteBinLine'
1863
- minimal
1864
- flat
1865
- fixed
1866
- intent='danger'
1867
- className='options-readfirst-clear'
1868
- aria-label='Clear value'
1869
- tooltip='Clear value'
1870
- onClick={confirmClearValue}
1871
- />
1872
- : null}
1873
- {moreMenu}
1874
- {closeExpandedFieldButton}
1875
- </ReqoreControlGroup>
1876
- </div>
1877
- {/* Absorbed siblings render in the CARD form of an open field too,
1878
- not only the inline row — which shape a field opens into depends
1879
- on its type, and absorbing must not depend on that. */}
1880
- {absorbedNodes}
1881
- {/* Escape discards here for the same reason it does on the inline
1882
- row: it is the one keystroke every editor treats as "get me out
1883
- of this without saving", and a card that ignored it made the
1884
- keystroke a silent commit. Same handler, same `cancelEdit()`, so
1885
- the two shapes cannot diverge. */}
1886
- <div
1887
- // The card's editor, as the inline row's is: an opened card takes the
1888
- // caret too (the tap-to-edit rule above), so opening Description or
1889
- // Source Code by a click or by a blocked submit puts the cursor in it.
1890
- ref={editorRef}
1891
- className='options-readfirst-row-editor'
1892
- style={{ display: 'contents' }}
1893
- onKeyDown={(event) => {
1894
- if (event.key === 'Escape') {
1895
- event.stopPropagation();
1896
- cancelEdit();
1897
- }
1898
- }}
1899
- >
1900
- {/* Same fullscreen focused-editing affordance as the classic cards —
1901
- the modal mounts when this option is focused. */}
1902
- <FocusedEditing
1903
- isFullscreen={focusedEditing === optionName}
1904
- onClose={() => setFocusedEditing(undefined)}
1905
- description={(schema?.display_name as string) || optionName}
1906
- >
1907
- {focusedEditing === optionName ?
1908
- <Description
1909
- longDescription={schema?.desc}
1910
- shortDescription={schema?.short_desc}
1911
- longDescriptionShownByDefault
1912
- />
1913
- : null}
1914
- <RowMenuContext.Provider value={rowMenu}>
1915
- {renderOption(optionName, optionField)}
1916
- </RowMenuContext.Provider>
1917
- </FocusedEditing>
1918
- </div>
1919
- </StyledEditCard>
1920
- );
1921
- }
1922
-
1923
- const formatted = formatOptionValue(optionField, schema);
1924
- const empty = formatted === '';
1925
- // A field with no value of its own but a declared default is not simply
1926
- // unanswered: the default is what the server will use, and an em-dash says
1927
- // the opposite. Render the default in its place, through the same formatter
1928
- // so a list or an enum reads the way a real value would.
1929
- //
1930
- // This does NOT make the field count as answered. `empty` stays true, so the
1931
- // row keeps the dimmed italic treatment that distinguishes a default from an
1932
- // answer, and bucketing — which reads the field's own value, not this — still
1933
- // files it under Optional and leaves it behind the collapse.
1934
- //
1935
- // `hidden` is included deliberately: it marks a field the form has not added
1936
- // yet, which is the case that benefits most — the row is the author's first
1937
- // sight of the field, and the default is what it will contribute if left
1938
- // alone. Both flags only ever route to the em-dash below, so there is no
1939
- // real value being displaced.
1940
- const defaultValue = hidden || empty ? getDefaultValue(schema) : undefined;
1941
- const formattedDefault =
1942
- defaultValue === undefined || defaultValue === null ?
1943
- ''
1944
- : formatOptionValue({ ...optionField, value: defaultValue }, schema);
1945
- // Inline reasons shown on the value line: validation / dependency / one-of /
1946
- // default-value hints, plus a read-only covered-by note (editable covered rows
1947
- // carry that in their chip instead).
1948
- const valueReasons: TInfoMsg[] = [
1949
- ...inlineMessages,
1950
- ...(empty && coveredByLabel ?
1951
- [{ content: `Covered by “${coveredByLabel}”`, intent: calm ? undefined : 'success' }]
1952
- : []),
1953
- ];
1954
- // A hash row reveals its sub-fields as read-only sub-rows under a "view
1955
- // more" disclosure; the row itself still expands the real editor on click.
1956
- /* An EXPRESSION is not a hash, whatever shape it is stored in.
1957
-
1958
- `{is_expression: true, value: {exp, args}}` is hash-shaped, so a field
1959
- holding one earned the structured inset and the row printed the syntax
1960
- tree — `is_expression true / value / exp + / args 1 2` — directly under a
1961
- summary line that already read `1 + 2`. The AST is how an expression is
1962
- STORED, not what it is; the row's line is the whole value, and the
1963
- expression editor is where its parts are looked at. Same exclusion, and
1964
- for the same reason, as `schema-definition` beside it. */
1965
- const isExpression = getExpressionAst(optionField) !== undefined;
1966
- const isPlainHashType =
1967
- (valueType === 'hash' || valueType === 'free-hash') &&
1968
- (schema as { ui_type?: string } | undefined)?.ui_type !== 'schema-definition' &&
1969
- !isExpression;
1970
- // A row of another declared type whose VALUE is an object (a data provider,
1971
- // an options hash) gets the same preview: its summary line names the thing,
1972
- // and the preview shows what it is made of. "11 fields" told the reader
1973
- // neither, and read as a string that happened to say "fields". The
1974
- // expression exclusion holds here too: a value stored as an AST is an
1975
- // expression whatever its declared type.
1976
- const isObjectValuedType =
1977
- OBJECT_VALUED_PREVIEW_TYPES.has(valueType) &&
1978
- !!optionField?.value &&
1979
- typeof optionField.value === 'object' &&
1980
- !Array.isArray(optionField.value) &&
1981
- !isExpression;
1982
- const hashEntries =
1983
- !hidden && (isPlainHashType || isObjectValuedType) ?
1984
- getHashEntries(optionField, schema)
1985
- : [];
1986
- // A LIST OF HASHES/objects gets the same expandable structured preview a hash
1987
- // does — the flat "N items" summary can't convey object contents (and must
1988
- // never print "[object Object]"). StructuredDataView unwraps each item's
1989
- // {type,value} envelope. Plain scalar lists keep just their joined summary.
1990
- const isHashList =
1991
- !hidden &&
1992
- (valueType === 'list' || valueType === 'free-list' || valueType === 'array') &&
1993
- Array.isArray(optionField?.value) &&
1994
- optionField.value.length > 0 &&
1995
- optionField.value.some((item) => {
1996
- if (richtextItemText(item) !== undefined) {
1997
- return false;
1998
- }
1999
- const inner =
2000
- item && typeof item === 'object' && 'value' in (item as Record<string, unknown>) ?
2001
- (item as Record<string, unknown>).value
2002
- : item;
2003
- return inner !== null && typeof inner === 'object';
2004
- });
2005
- const showStructuredPreview = hashEntries.length > 0 || isHashList;
2006
- // Multi-line code preview: a `code-editor` field with a non-empty string
2007
- // value gets a `<pre>`-based code block under the row, capped by a
2008
- // `ReqoreCollapsibleContent` so the collapsed height stays row-sized and
2009
- // "Show more" reveals the rest. Sits in the same inset the hash/list
2010
- // preview uses — mutually exclusive with `showStructuredPreview`.
2011
- const showCodePreview =
2012
- !hidden &&
2013
- valueType === 'code-editor' &&
2014
- typeof optionField?.value === 'string' &&
2015
- optionField.value.length > 0;
2016
- // A richtext value that renders as CHIPS is drawn in full by the row itself:
2017
- // the chips are the value, and the reader is looking straight at it. The
2018
- // native `title` below would then hover the raw source of the very thing the
2019
- // chips replace ("[$fsminput:severity] $fsminput:alert_code") — and, because
2020
- // each chip carries its own Reqore tooltip, hovering one fires BOTH: the chip
2021
- // popover and the row's browser tooltip, in different places, saying almost
2022
- // the same thing. Only the popover shows up in a screenshot, which is what
2023
- // made the pair hard to report.
2024
- const showsTemplateChips =
2025
- !hidden &&
2026
- valueType === 'richtext' &&
2027
- Array.isArray(optionField?.value) &&
2028
- richtextToSegments(optionField.value as never).some((segment) => segment.kind === 'tag');
2029
- /* The same pair, for a value that IS one template rather than prose with
2030
- templates embedded in it. `showsTemplateChips` above closed the richtext
2031
- case and left this one open: a chosen reference renders as
2032
- `ReadOnlyTemplateTag`, which carries its own popover, while the row went
2033
- on setting a native `title` holding the raw reference. Hovering fired
2034
- both — two tooltips in two places, and the one that says something useful
2035
- is not the one a screenshot captures. */
2036
- const showsTemplateTag =
2037
- !hidden &&
2038
- typeof optionField?.value === 'string' &&
2039
- !(optionField as { is_expression?: boolean } | undefined)?.is_expression &&
2040
- (isValueTemplate(optionField.value) ||
2041
- !!findTemplate(
2042
- (schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates ?? {},
2043
- optionField.value
2044
- ));
2045
- /* And once more for a value that only MENTIONS references — a predicate, a
2046
- greeting with a name in it. The row draws those as prose plus chips too,
2047
- so a native `title` would hover the raw spelling of exactly what the chips
2048
- replaced, and each chip's own popover would fire alongside it. */
2049
- const showsTemplateText =
2050
- !hidden &&
2051
- !showsTemplateTag &&
2052
- !!formatted &&
2053
- hasTemplateText(
2054
- formatted,
2055
- (schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates,
2056
- (schema as TFieldWithOwnTemplates | undefined)?.templateToken
2057
- );
2058
- // Markdown reads the same way for the same reason: the row itself can only
2059
- // show a line of text, and for markdown that line is the SOURCE — the reader
2060
- // gets `## ` and `**` where the point of the value is what it looks like
2061
- // rendered. The document renders in the same inset the code preview uses,
2062
- // through the host's renderer, so the row shows the description the way the
2063
- // page it describes will.
2064
- //
2065
- // Only a MULTI-LINE document earns the inset. A one-liner is already fully
2066
- // rendered as the row's own text, and a second copy of it below would make
2067
- // every description row tall for nothing.
2068
- const showMarkdownPreview =
2069
- !hidden &&
2070
- !showCodePreview &&
2071
- !!markdownRenderer &&
2072
- // keyed on the schema's ui_type, not on getValueType(): a markdown value
2073
- // IS a string and the value type says so, which is the whole distinction
2074
- // between how a value is stored and which editor draws it
2075
- (schema as { ui_type?: string } | undefined)?.ui_type === 'markdown' &&
2076
- typeof optionField?.value === 'string' &&
2077
- isMultilineMarkdown(optionField.value);
2078
- // Read-only: a long plain-text value is drawn in FULL under the row, in
2079
- // the same inset and under the same "Show more" cap a code value gets,
2080
- // instead of one line with an ellipsis and a browser tooltip holding the
2081
- // rest. The editable row truncates because its value line is a summary
2082
- // you click to open; a read row is the only rendering the value gets.
2083
- //
2084
- // "Plain" is `formatted === value`: the text was rendered verbatim — not
2085
- // a template resolved to a chip, not an allowed value shown by its label,
2086
- // not a sensitive value masked, not markdown summarised. Only text that
2087
- // would not fit the line moves down: a break in it, or more than a short
2088
- // sentence's worth of characters (a short value wraps in place).
2089
- const showLongTextInset =
2090
- readOnly &&
2091
- !hidden &&
2092
- !showCodePreview &&
2093
- !showMarkdownPreview &&
2094
- !showStructuredPreview &&
2095
- typeof optionField?.value === 'string' &&
2096
- formatted === optionField.value &&
2097
- (optionField.value.includes('\n') || optionField.value.length > 120);
2098
- // Read-only: a choice reads as the option AND what it means —
2099
- // "Authenticated users", then "callers must present a token". The picker
2100
- // showed that description while choosing; a reader has no picker.
2101
- const chosenOption =
2102
- readOnly && !hidden && !empty ?
2103
- (findAllowedValueOption(optionField?.value, schema) as
2104
- { desc?: string; short_desc?: string } | undefined)
2105
- : undefined;
2106
- const readOnlyChoiceDesc = chosenOption?.desc || chosenOption?.short_desc;
2107
-
2108
- // "9 lines" for a multi-line markdown value, shown under the field's name
2109
- // rather than in front of its content: it describes the field, not the value,
2110
- // and the value column is for the value. Rendered whether or not a host
2111
- // renderer is present — the count is the one thing the row cannot show you by
2112
- // showing you the text, and it is what tells you there is more below.
2113
- const lineCountNote =
2114
- (
2115
- !hidden &&
2116
- (schema as { ui_type?: string } | undefined)?.ui_type === 'markdown' &&
2117
- typeof optionField?.value === 'string' &&
2118
- isMultilineMarkdown(optionField.value)
2119
- ) ?
2120
- `${optionField.value.trim().split('\n').length} lines`
2121
- : undefined;
2122
- const typeLabel =
2123
- showFieldTypes ?
2124
- `<${(schema?.ui_type as string) || (schema?.type as string) || 'auto'}${(schema as { ui_element_type?: string } | undefined)?.ui_element_type ? `[${(schema as { ui_element_type?: string }).ui_element_type}]` : ''}>`
2125
- : null;
2126
- // A `readonly` field is one the system owns — an assigned id, a recorded
2127
- // provenance. The server states that on the field itself; only the older
2128
- // compat projection also mapped it to `disabled`, so on this path the form
2129
- // was handed `readonly` alone and rendered an editable control for a value
2130
- // no answer of the user's can change.
2131
- const fieldReadonly = !!(schema as { readonly?: boolean } | undefined)?.readonly;
2132
- // Disabled rows (schema flag, readonly, or unmet deps) can't open — a lock +
2133
- // reason renders instead. A form-level readOnly row never opens at all, and
2134
- // shows no lock either: nothing on a read-only form is locked, it is read.
2135
- const fieldDisabled =
2136
- !hidden &&
2137
- !readOnly &&
2138
- (!!schema?.disabled ||
2139
- fieldReadonly ||
2140
- !hasAllDependenciesFullfilled(schema?.depends_on, availableOptions, options || {}));
2141
- const fieldDisabledReason =
2142
- schema?.disabled ? 'This field is disabled'
2143
- : fieldReadonly ? 'Set by Qorus — not editable'
2144
- : 'Disabled — dependencies are not fulfilled';
2145
- // Dependency contract: top-level entries must ALL hold; a nested array
2146
- // means ANY of its entries; `name=value` requires that exact value, and
2147
- // `name!=value` requires any other answer. Parsed by the same helper the
2148
- // predicate uses, so the lock cannot describe a rule the form does not apply.
2149
- const dependencyEntries =
2150
- fieldDisabled && !schema?.disabled && !fieldReadonly ?
2151
- ((schema?.depends_on || []) as (string | string[])[])
2152
- : [];
2153
- const describeDependency = (dep: string) => {
2154
- const { name: depName, op, value: expected } = parseDependency(dep);
2155
- const depLabel = (options?.[depName]?.display_name as string) || depName;
2156
- const depValue = (availableOptions as TQorusForm)?.[depName]?.value;
2157
- return {
2158
- name: depName,
2159
- exists: !!options?.[depName],
2160
- // `!name` compares against nothing — it is satisfied by the sibling
2161
- // being unanswered — so it says so, rather than going through the
2162
- // comparison template and reading `Name ≠ undefined`.
2163
- label:
2164
- op === '!' ? `${depLabel} unset`
2165
- : !op ? depLabel
2166
- : `${depLabel} ${op === '=' ? '=' : '≠'} ${expected}`,
2167
- fulfilled:
2168
- op === '!' ? isOptionValueEmpty(depValue)
2169
- : !op ? !isOptionValueEmpty(depValue)
2170
- : depValue == null ? false
2171
- : op === '=' ? String(depValue) === expected
2172
- : String(depValue) !== expected,
2173
- };
2174
- };
2175
- const depHighlightNames = (flatten(dependencyEntries as never[]) as string[])
2176
- .map((dep) => describeDependency(dep).name)
2177
- .filter((depName) => !!options?.[depName]);
2178
- // Dependency lock, styled to match the required-group chip: a muted
2179
- // "Depends on" dropdown whose items are the blocking fields (an "any of:"
2180
- // divider for OR-groups), each clickable to flash/locate the blocker.
2181
- const dependsOnChip =
2182
- fieldDisabled && dependencyEntries.length ?
2183
- <span
2184
- style={{ display: 'inline-flex' }}
2185
- role='presentation'
2186
- onClick={(e) => e.stopPropagation()}
2187
- onKeyDown={(e) => e.stopPropagation()}
2188
- onMouseEnter={() => setHighlightedOptions(depHighlightNames)}
2189
- onMouseLeave={() => setHighlightedOptions([])}
2190
- >
2191
- <ReqoreDropdown
2192
- className='options-readfirst-lock-deps'
2193
- size='tiny'
2194
- minimal
2195
- flat
2196
- compact
2197
- icon='LockLine'
2198
- tooltip='Disabled — depends on other fields. Click to locate them.'
2199
- items={[
2200
- { divider: true, label: 'Unlocked by:', dividerAlign: 'left' } as IReqoreDropdownItem,
2201
- ...dependencyEntries.flatMap((entry): IReqoreDropdownItem[] => [
2202
- ...(Array.isArray(entry) ?
2203
- [{ divider: true, label: 'any of:', dividerAlign: 'left' } as IReqoreDropdownItem]
2204
- : []),
2205
- ...(Array.isArray(entry) ? entry : [entry]).flatMap(
2206
- (dep): IReqoreDropdownItem[] => {
2207
- const info = describeDependency(dep);
2208
- return info.exists ?
2209
- [
2210
- {
2211
- label: info.label,
2212
- icon: info.fulfilled ? 'CheckLine' : 'ArrowRightLine',
2213
- intent: info.fulfilled ? 'success' : undefined,
2214
- onClick: () => flashOption(info.name),
2215
- } as IReqoreDropdownItem,
2216
- ]
2217
- : [];
2218
- }
2219
- ),
2220
- ]),
2221
- ]}
2222
- />
2223
- </span>
2224
- : null;
2225
- const activate = (event?: { currentTarget?: Element | null }) => {
2226
- // A read-only row is not a control. It used to open into the editor
2227
- // with `readOnly` handed down (an id in a greyed-out input, a choice
2228
- // that could still be changed); then into a read view of the same
2229
- // value — which showed nothing the row could not show itself once it
2230
- // stopped truncating. So it shows everything, and opens nothing HERE.
2231
- //
2232
- // Where the consumer says the field is editable somewhere else, the row
2233
- // is the way in to that place: a reader clicking a value they want to
2234
- // change has said exactly what they want, and answering with silence
2235
- // teaches them the surface is broken.
2236
- if (fieldDisabled || readOnly) {
2237
- if (readOnly && !fieldDisabled) {
2238
- onReadOnlyActivate?.(optionName);
2239
- }
2240
- return;
2241
- }
2242
- const target = event?.currentTarget as HTMLElement | undefined;
2243
- if (target?.classList?.contains('readfirst-row')) {
2244
- readRowHeights.current[optionName] = Math.round(target.getBoundingClientRect().height);
2245
- }
2246
- // The author asked for this one — so the row opens all the way (a picker
2247
- // opens its list rather than reprinting the value as a closed trigger).
2248
- // Only when it is OPENING: the same handler collapses an open row.
2249
- openedByAuthor.current = !isExpanded;
2250
- // Optional fields aren't "added" — every one is editable in place. Opening a
2251
- // not-yet-set field just expands its editor (with an empty value); it joins
2252
- // the form's output the moment a value is set (and moves to Set / Needs
2253
- // attention), and drops back to Optional when cleared. No explicit add step.
2254
- toggleExpandedOption(optionName);
2255
- };
2256
-
2257
- // Row chrome: icon/image before the label; schema `intent` as the edge
2258
- // stripe (message-severity stripes win when both apply).
2259
- const rowChromeIcon =
2260
- (
2261
- (schema as { icon?: string } | undefined)?.icon ||
2262
- (schema as { image?: string } | undefined)?.image
2263
- ) ?
2264
- <ReqoreIcon
2265
- icon={(schema as { icon?: string } | undefined)?.icon as never}
2266
- image={(schema as { image?: string } | undefined)?.image}
2267
- size='14px'
2268
- className='options-readfirst-row-icon'
2269
- />
2270
- : null;
2271
- const rowSchemaIntentColor =
2272
- schema?.intent ?
2273
- (theme?.intents as Record<string, string> | undefined)?.[schema.intent as string]
2274
- : undefined;
2275
- const rowStripeColor = (showStripe ? intentColor : undefined) || rowSchemaIntentColor;
2276
- // Intent stripe on the block ROOT so it spans the field's whole territory:
2277
- // the info/preview wrapper when one hangs below, else the bare row. It feeds
2278
- // --readfirst-stripe, which StyledGroupBody paints as the value surface's left
2279
- // border (the recessed surface starts at the value column).
2280
- const stripeStyle =
2281
- rowStripeColor ?
2282
- ({ ['--readfirst-stripe']: rowStripeColor } as React.CSSProperties)
2283
- : undefined;
2284
-
2285
- // "Focus" trailing status dot, derived from the SHARED status helper (the
2286
- // same one FormEngine uses to bucket rows into the Needs-attention/Set/Optional
2287
- // boxes — so the dot and the box can never disagree). Hidden (not-yet-added)
2288
- // and dependency-locked rows show no dot (the add/lock affordance speaks for
2289
- // them). worstIntent already folds in schema + validation messages.
2290
- const cSuccess = (theme?.intents as Record<string, string> | undefined)?.success;
2291
- const rowStatus = getReadFirstStatus({
2292
- empty,
2293
- required,
2294
- covered: !!coveredByLabel || groupResolved,
2295
- invalid: worstIntent === 'danger',
2296
- warned: worstIntent === 'warning',
2297
- visited,
2298
- });
2299
- // No dot at all in a read-only form: the dot reports progress on the
2300
- // form, and a read view has no progress to report.
2301
- //
2302
- // Calm: no dot on a healthy row, and none on an empty required one either —
2303
- // its value column already says "Required · add a value" in amber, and one
2304
- // thing is said once. Only a failing value keeps the red dot beside the
2305
- // reason it gives.
2306
- const dotStatus =
2307
- hidden || fieldDisabled || readOnly || (calm && rowStatus !== 'invalid') ?
2308
- undefined
2309
- : rowStatus;
2310
- // Calm: an empty required value is said IN the value column, in amber,
2311
- // where the value would be — instead of a dash plus a red reason beside it.
2312
- // Not on a locked field (its lock and reason speak for it) or one that
2313
- // shows the default it will fall back to.
2314
- const calmRequiredWord =
2315
- calm && empty && !hidden && !!schema?.required && !fieldDisabled && !formattedDefault ?
2316
- readOnly ? CALM_REQUIRED_READ_LABEL
2317
- : CALM_REQUIRED_LABEL
2318
- : undefined;
2319
- const dotColor =
2320
- dotStatus === 'invalid' ? cDanger
2321
- : dotStatus === 'todo' ? cWarning
2322
- : dotStatus === 'set' ? cSuccess || cInfo
2323
- : undefined;
2324
- /* A read-only row is a control only when the consumer gave it somewhere to
2325
- go. Then it takes a button's role, focus and keyboard handling, because
2326
- a thing that acts on a click has to be reachable without a mouse and has
2327
- to announce what it does — and "Edit X", not "X", is what it does. */
2328
- const readOnlyIsAWayIn = !!readOnly && !fieldDisabled && !!onReadOnlyActivate;
2329
- const actsOnClick = !readOnly || readOnlyIsAWayIn;
2330
-
2331
- const row = (
2332
- <div
2333
- key={optionName}
2334
- data-field={optionName}
2335
- role={actsOnClick ? 'button' : undefined}
2336
- tabIndex={actsOnClick ? 0 : undefined}
2337
- aria-label={
2338
- readOnlyIsAWayIn ? `Edit ${label}`
2339
- : readOnly ? undefined
2340
- : label
2341
- }
2342
- className={`readfirst-row options-readfirst-value${readOnly ? ' readfirst-row-read' : ''}${readOnlyIsAWayIn ? ' readfirst-row-way-in' : ''}${hidden ? ' readfirst-row-hidden' : ''}${fieldDisabled ? ' readfirst-row-disabled' : ''}${isHighlighted ? ' readfirst-row-group-highlight' : ''}${isFlashed ? ' readfirst-row-flash' : ''}${showLabelDesc ? ' readfirst-row-info-open' : ''}${panelMessages.length || showStructuredPreview || showCodePreview || showMarkdownPreview || showLongTextInset ? ' readfirst-row-tall' : ''}${clusterBlockClass ? ' ' + clusterBlockClass : ''}`}
2343
- aria-disabled={fieldDisabled || undefined}
2344
- style={readOnlyIsAWayIn ? { ...stripeStyle, cursor: 'pointer' } : stripeStyle}
2345
- onClick={actsOnClick ? activate : undefined}
2346
- onKeyDown={
2347
- !actsOnClick ? undefined : (
2348
- (event) => {
2349
- if (event.key === 'Enter' || event.key === ' ') {
2350
- event.preventDefault();
2351
- activate(event);
2352
- }
2353
- }
2354
- )
2355
- }
2356
- {...clusterHoverProps}
2357
- >
2358
- {clusterNode}
2359
- <StyledLabelBlock className='options-readfirst-label-block'>
2360
- <StyledRowLabel title={schema?.short_desc || undefined} $color={cLabel}>
2361
- {rowChromeIcon}
2362
- {/* label + asterisk + help flow as ONE inline run, so the asterisk
2363
- stays right after the last word even when the name wraps. */}
2364
- <span className='options-readfirst-label-text' style={{ minWidth: 0 }}>
2365
- {label}
2366
- {requiredMark ?
2367
- <ReqoreIcon
2368
- icon='Asterisk'
2369
- color='danger'
2370
- size='10px'
2371
- margin='left'
2372
- marginSize='tiny'
2373
- />
2374
- : null}
2375
- {helpIcon}
2376
- </span>
2377
- {typeLabel ?
2378
- <ReqoreTag size='tiny' minimal label={typeLabel} labelEffect={{ opacity: 0.55 }} />
2379
- : null}
2380
- </StyledRowLabel>
2381
- {showLabelDesc ?
2382
- <StyledLabelDesc
2383
- className='options-readfirst-label-desc'
2384
- size='small'
2385
- effect={{ opacity: 0.55 }}
2386
- >
2387
- {labelShortDesc}
2388
- </StyledLabelDesc>
2389
- : null}
2390
- {codeSizeNode}
2391
- {lineCountNote ?
2392
- <ReqoreP
2393
- className='options-readfirst-label-lines'
2394
- size='tiny'
2395
- effect={{ opacity: 0.45, textAlign: 'left' }}
2396
- style={{ fontFamily: 'monospace' }}
2397
- >
2398
- {lineCountNote}
2399
- </ReqoreP>
2400
- : null}
2401
- </StyledLabelBlock>
2402
- <StyledRowValue
2403
- // A code field draws its whole value in the preview below, with
2404
- // "Show more" for the rest, so a hover carrying the same text is
2405
- // noise -- and a native tooltip holding a few hundred lines is
2406
- // unreadable anyway. A chipped richtext value is the same case: the row
2407
- // already draws it in full, and its chips carry their own tooltips.
2408
- // Every other value keeps its hover, which is the only way to read one
2409
- // that the row had to truncate.
2410
- title={
2411
- (
2412
- !empty &&
2413
- !hidden &&
2414
- // A read row wraps its value, so a hover has nothing to add.
2415
- !readOnly &&
2416
- !showCodePreview &&
2417
- !showMarkdownPreview &&
2418
- !showsTemplateChips &&
2419
- !showsTemplateTag &&
2420
- !showsTemplateText &&
2421
- typeof formatted === 'string'
2422
- ) ?
2423
- formatted
2424
- : undefined
2425
- }
2426
- // A SET value reads at full key brightness so it stands out as the
2427
- // actual data — crucial when stacked under a (muted) description on
2428
- // mobile, where a dim value blends into the prose. The bold name still
2429
- // outranks it; the empty dash stays faint.
2430
- $color={
2431
- calmRequiredWord ?
2432
- rowStatus === 'pending' ?
2433
- `${cMuted}a6`
2434
- : cWarning
2435
- : empty || hidden ? `${cMuted}66`
2436
- : cKey
2437
- }
2438
- $empty={(empty || hidden) && !calmRequiredWord}
2439
- >
2440
- {/* A markdown value with a host renderer draws in full in the inset
2441
- below, so the row prints no value line at all: the stripped-prose
2442
- copy that used to sit above the document was a second rendering of
2443
- the same text with the markers taken out (Qlip review, build #97).
2444
- Without a renderer there is no inset, and the prose IS the value —
2445
- so the line stays, and `renderReadFirstValue` still produces it.
2446
-
2447
- A schema-previewed value is the same situation. The summary is a
2448
- flattened join of the item labels ("Default RBAC, Cookie") sitting
2449
- directly above a preview that names every one of those items and
2450
- its fields — the same facts twice, the shorter version first, which
2451
- reads less like a summary than like a second, disagreeing answer.
2452
- An UNDESCRIBED value keeps its summary: the data tree beneath it
2453
- says nothing about what the value MEANS, so the summary is still
2454
- the only line that does.
2455
-
2456
- Both suppressions are conditional on the replacement ACTUALLY
2457
- rendering. `previewWithSchema` only asks whether the value and its
2458
- `arg_schema` COULD be drawn as a schema preview; whether one is
2459
- drawn is `showStructuredPreview`, and the two disagree for a field
2460
- whose value type is a host ui_type rather than a list or a hash —
2461
- a test's `cases` (`ui_type: "test-cases"`, an array of case hashes
2462
- with an `arg_schema`) could be previewed but is not, because
2463
- `isHashList` only recognises `list`/`free-list`/`array`. The row
2464
- then dropped its summary in favour of a preview that never came
2465
- and rendered nothing at all: a test with cases read as a bare
2466
- "Cases" and looked unset until it was opened. */}
2467
- {(
2468
- showMarkdownPreview || showLongTextInset || (previewWithSchema && showStructuredPreview)
2469
- ) ?
2470
- null
2471
- : <span className='options-readfirst-valuetext'>
2472
- {hidden || empty ?
2473
- calmRequiredWord || formattedDefault || '—'
2474
- : renderReadFirstValue(optionField, schema, formatted, readOnly)}
2475
- </span>}
2476
- {readOnlyChoiceDesc ?
2477
- <span className='options-readfirst-choice-desc'>{readOnlyChoiceDesc}</span>
2478
- : null}
2479
- {(calmRequiredWord ?
2480
- valueReasons.filter((m) => m.content !== calmRequiredWord)
2481
- : valueReasons
2482
- ).map((m, i) => (
2483
- <span
2484
- key={`${m.content}-${i}`}
2485
- className='options-readfirst-reason'
2486
- style={{ color: reasonColor(m.intent) }}
2487
- >
2488
- {m.content}
2489
- </span>
2490
- ))}
2491
- {/* Dedicated schema message PANELS render right here, directly under the
2492
- value (full width of the value column) — never pushed down by the
2493
- label's short_desc. */}
2494
- {panelMessages.length ?
2495
- <ReqoreControlGroup
2496
- className='options-readfirst-info-panel'
2497
- vertical
2498
- fluid
2499
- gapSize='normal'
2500
- >
2501
- {panelMessages.map(renderInfoStrip)}
2502
- </ReqoreControlGroup>
2503
- : null}
2504
- {/* The structured hash/list preview also lives in the value cell, so it
2505
- starts directly under the value summary ("N fields"), not below the
2506
- label's short_desc. */}
2507
- {showStructuredPreview ?
2508
- // The inset lives inside the row now, so a click on a value chip would
2509
- // bubble to the row's onClick and fire activate() a SECOND time (the
2510
- // chip's onItemClick already calls it) — toggling the editor shut again.
2511
- // Stop the bubble here; the chip's own handler still opens the editor.
2512
- <StyledRowInset
2513
- className='options-readfirst-inset'
2514
- onClick={(e) => e.stopPropagation()}
2515
- >
2516
- <ReqoreCollapsibleContent
2517
- // The schema view spends more height per datum than a data tree
2518
- // does — a labelled row rather than a packed chip — but there is
2519
- // far less of it to read, so it gets enough room to show a
2520
- // couple of complete items instead of fading out mid-row. A tree
2521
- // of unknown depth keeps the tighter cap.
2522
- maxCollapsedHeight={previewWithSchema ? 140 : 96}
2523
- buttonProps={{ className: 'options-readfirst-viewmore' }}
2524
- >
2525
- <div className='options-readfirst-structured'>
2526
- {previewWithSchema ?
2527
- // The form already knows this value's shape, names and
2528
- // choices, so the preview says it rather than making the
2529
- // reader decode it: "Scheme Type — Default RBAC", not a data
2530
- // tree announcing "Object · 1 field" over `type: default`.
2531
- <SchemaDataView
2532
- value={optionField?.value}
2533
- schema={previewArgSchema!}
2534
- showTypes={showFieldTypes}
2535
- colors={{
2536
- key: cKey,
2537
- muted: cMuted,
2538
- // Deliberately stronger than `cDivider`. That tier is for
2539
- // decorative hairlines between rows, where being nearly
2540
- // invisible is the point. This rule is load-bearing — it
2541
- // is the only thing saying which fields belong to which
2542
- // numbered item — so it gets the muted tier at partial
2543
- // alpha: quiet, but actually visible.
2544
- border: `${cMuted}66`,
2545
- // The identity accent for each item's heading.
2546
- //
2547
- // `custom1` first: BRAND_DESIGN §1 says apps may register
2548
- // their own named intents and reference them like the
2549
- // built-ins, and that is where a product's own accent
2550
- // lives (qorus-ide registers its brand purple there).
2551
- // Falling back to `info` keeps this working for a
2552
- // consumer that registers nothing — the heading is
2553
- // accented either way, just in the theme's own colour
2554
- // rather than one this library picked.
2555
- accent:
2556
- (theme?.intents as Record<string, string> | undefined)?.custom1 || cInfo,
2557
- }}
2558
- onItemClick={() => activate()}
2559
- />
2560
- : <StructuredDataView
2561
- value={optionField?.value}
2562
- collapsibleRoot={false}
2563
- showTypes={showFieldTypes}
2564
- defaultExpandDepth={2}
2565
- onItemClick={() => activate()}
2566
- />
2567
- }
2568
- </div>
2569
- </ReqoreCollapsibleContent>
2570
- </StyledRowInset>
2571
- : null}
2572
- {/* Code-editor preview: same inset placement as hash/list — a monospace
2573
- `<pre>` block wrapped in `ReqoreCollapsibleContent` so the collapsed
2574
- row stays row-height while multi-line source reveals via "Show more".
2575
- Stopping propagation here (like the structured inset) — a click on
2576
- the collapse button must not also open the field editor. */}
2577
- {showCodePreview ?
2578
- <StyledRowInset
2579
- className='options-readfirst-inset options-readfirst-code-inset'
2580
- onClick={(e) => e.stopPropagation()}
2581
- >
2582
- <ReqoreCollapsibleContent
2583
- maxCollapsedHeight={96}
2584
- buttonProps={{ className: 'options-readfirst-viewmore' }}
2585
- >
2586
- {codePreviewRenderer ?
2587
- codePreviewRenderer({
2588
- value: optionField.value as string,
2589
- name: optionName,
2590
- schema,
2591
- options,
2592
- values: availableOptions,
2593
- })
2594
- : <StyledCodePreview
2595
- className='options-readfirst-code'
2596
- $bg={cHover}
2597
- $border={`${cDivider}88`}
2598
- $fg={cKey}
2599
- >
2600
- {optionField.value as string}
2601
- </StyledCodePreview>
2602
- }
2603
- </ReqoreCollapsibleContent>
2604
- </StyledRowInset>
2605
- : null}
2606
- {/* Markdown preview: same inset, same collapse, drawn by the host's
2607
- renderer so a description reads identically here and on the page it
2608
- belongs to. Only rendered when a host supplied one — there is no
2609
- built-in fallback, because drawing markdown in a dialect nobody
2610
- chose is the problem this exists to avoid. Propagation stops here
2611
- for the reason the code inset does it: a click on "Show more", or
2612
- on a link in the prose, must not also open the field editor. */}
2613
- {showMarkdownPreview ?
2614
- <StyledRowInset
2615
- className='options-readfirst-inset options-readfirst-markdown-inset'
2616
- onClick={(e) => e.stopPropagation()}
2617
- >
2618
- <ReqoreCollapsibleContent
2619
- maxCollapsedHeight={96}
2620
- buttonProps={{ className: 'options-readfirst-viewmore' }}
2621
- >
2622
- <div className='options-readfirst-markdown'>
2623
- {markdownRenderer!({ value: optionField.value as string, compact: true })}
2624
- </div>
2625
- </ReqoreCollapsibleContent>
2626
- </StyledRowInset>
2627
- : null}
2628
- {/* Read-only long text: see `showLongTextInset`. No click guard —
2629
- the read row has no click to guard against. */}
2630
- {showLongTextInset ?
2631
- <StyledRowInset className='options-readfirst-inset options-readfirst-text-inset'>
2632
- <ReqoreCollapsibleContent
2633
- maxCollapsedHeight={96}
2634
- buttonProps={{ className: 'options-readfirst-viewmore' }}
2635
- >
2636
- <div className='options-readfirst-text'>{optionField.value as string}</div>
2637
- </ReqoreCollapsibleContent>
2638
- </StyledRowInset>
2639
- : null}
2640
- </StyledRowValue>
2641
- <StyledRowActions>
2642
- {/* Column discipline (table treatment): variable-width chips lead and
2643
- rag INWARD; the lock/add and info slots are fixed-width and pinned at
2644
- the right, so the ⓘ sits at the same x on every row. Revert is shown
2645
- whenever a field has changed; delete reveals on hover. */}
2646
- {/* No generic invalid/required chip: the intent stripe + the field's
2647
- own message ("This field is required" for an empty required field)
2648
- already flag it. Only the required-GROUP chip (One of / Covers)
2649
- stays — it carries info nothing else does. */}
2650
- {/* Railed (clustered) members: the rail conveys the grouping, so drop
2651
- the "One of"/"Covers" chip — keep only "Covered by <X>", the one fact
2652
- the rail can't show. Non-clustered members (split-across-panels or
2653
- narrow mode, where there's no rail) keep the full chip as a fallback. */}
2654
- {!hidden && !fieldDisabled && !clustered && !coveredByLabel ? requiredGroupChip : null}
2655
- {!hidden ? dependsOnChip : null}
2656
- {draftChip}
2657
- {/* Remove-field lives in the expanded editor's "More" (⋮) menu now — no
2658
- standalone delete button on the read row. */}
2659
- {/* Lock slot BEFORE the info slot so the ⓘ keeps the same far-right x on
2660
- every row — a disabled field's lock sits to the ⓘ's left rather than
2661
- pushing it inward. (Not-yet-set optional fields show NO marker now —
2662
- they're just editable in place, like every other row. Dependency-
2663
- locked fields show the "Depends on" chip in the chips area instead.) */}
2664
- {fieldDisabled && !dependencyEntries.length ?
2665
- <StyledActionSlot className='options-readfirst-trailing-slot' $width={18}>
2666
- <span title={fieldDisabledReason} style={{ display: 'inline-flex', opacity: 0.45 }}>
2667
- <ReqoreIcon className='options-readfirst-locked' icon='LockLine' size='14px' />
2668
- </span>
2669
- </StyledActionSlot>
2670
- : null}
2671
- {infoToggle ?
2672
- <StyledActionSlot className='options-readfirst-info-slot' $width={26}>
2673
- {infoToggle}
2674
- </StyledActionSlot>
2675
- : null}
2676
- {inlineOptionActions.map((action, index) =>
2677
- renderInjectedOptionAction(action, index, 'tiny')
2678
- )}
2679
- {injectedOptionActionMenuItems.length ?
2680
- <StyledActionSlot
2681
- className='options-readfirst-actions-slot'
2682
- $width={26}
2683
- // The read row opens the editor when clicked. Without this the tap
2684
- // that opens this menu also expands the row, which unmounts the
2685
- // menu — on touch that made the collapsed actions unreachable,
2686
- // the exact problem the collapse exists to solve.
2687
- onClick={(event: React.MouseEvent<HTMLElement>) => event.stopPropagation()}
2688
- >
2689
- <ReqoreDropdown
2690
- className='options-injected-actions-menu'
2691
- icon='MoreFill'
2692
- flat
2693
- minimal
2694
- fixed
2695
- size='tiny'
2696
- tooltip='Field actions'
2697
- items={injectedOptionActionMenuItems}
2698
- />
2699
- </StyledActionSlot>
2700
- : null}
2701
- {/* The revert affordance lives in the status-dot column (a changed field
2702
- swaps its dot for the revert icon) so it sits at the same fixed x as
2703
- the dot — out of the value's way instead of floating over it. */}
2704
- <StyledActionSlot className='options-readfirst-statusdot-slot' $width={12}>
2705
- {changed && !readOnly && !hidden ?
2706
- <ReqoreButton
2707
- className='options-readfirst-revert'
2708
- size='tiny'
2709
- flat
2710
- minimal
2711
- compact
2712
- icon='HistoryLine'
2713
- tooltip='Revert changes'
2714
- onClick={(e: React.MouseEvent) => {
2715
- e.stopPropagation();
2716
- handleValueChange(
2717
- optionName,
2718
- originalValue.current?.[optionName]?.value,
2719
- originalValue.current?.[optionName]?.type
2720
- );
2721
- }}
2722
- />
2723
- : dotColor ?
2724
- <StyledStatusDot
2725
- className='options-readfirst-status-dot'
2726
- $color={dotColor}
2727
- $ring={dotStatus !== 'set'}
2728
- aria-hidden
2729
- />
2730
- : null}
2731
- </StyledActionSlot>
2732
- </StyledRowActions>
2733
- </div>
2734
- );
2735
-
2736
- return row;
2737
- }
2738
- );
2739
-
2740
- CompactRow.displayName = 'CompactRow';