@qoretechnologies/reqraft 0.10.64 → 0.10.65-pr.137.g62fcecc

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