@qoretechnologies/reqraft 0.10.25 → 0.10.26

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 (310) hide show
  1. package/.claude/CLAUDE.md +52 -23
  2. package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
  3. package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
  4. package/dist/components/FocusedEditing/index.d.ts +9 -1
  5. package/dist/components/FocusedEditing/index.d.ts.map +1 -1
  6. package/dist/components/FocusedEditing/index.js +2 -2
  7. package/dist/components/FocusedEditing/index.js.map +1 -1
  8. package/dist/components/composer/Composer.d.ts +108 -0
  9. package/dist/components/composer/Composer.d.ts.map +1 -0
  10. package/dist/components/composer/Composer.js +238 -0
  11. package/dist/components/composer/Composer.js.map +1 -0
  12. package/dist/components/composer/helpers.d.ts +21 -0
  13. package/dist/components/composer/helpers.d.ts.map +1 -0
  14. package/dist/components/composer/helpers.js +75 -0
  15. package/dist/components/composer/helpers.js.map +1 -0
  16. package/dist/components/composer/index.d.ts +3 -0
  17. package/dist/components/composer/index.d.ts.map +1 -0
  18. package/dist/components/composer/index.js +19 -0
  19. package/dist/components/composer/index.js.map +1 -0
  20. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  21. package/dist/components/form/engine/CompactRow.js +307 -110
  22. package/dist/components/form/engine/CompactRow.js.map +1 -1
  23. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  24. package/dist/components/form/engine/CompactToolbar.js +123 -106
  25. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  26. package/dist/components/form/engine/FormEngine.d.ts +107 -11
  27. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  28. package/dist/components/form/engine/FormEngine.js +618 -179
  29. package/dist/components/form/engine/FormEngine.js.map +1 -1
  30. package/dist/components/form/engine/compactRowContext.d.ts +9 -0
  31. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  32. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  33. package/dist/components/form/engine/compactRowStyles.d.ts +7 -3
  34. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  35. package/dist/components/form/engine/compactRowStyles.js +88 -51
  36. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  37. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  38. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  39. package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
  40. package/dist/components/form/engine/optionActions.d.ts +28 -0
  41. package/dist/components/form/engine/optionActions.d.ts.map +1 -0
  42. package/dist/components/form/engine/optionActions.js +18 -0
  43. package/dist/components/form/engine/optionActions.js.map +1 -0
  44. package/dist/components/form/engine/readFirst.d.ts +63 -2
  45. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  46. package/dist/components/form/engine/readFirst.js +121 -6
  47. package/dist/components/form/engine/readFirst.js.map +1 -1
  48. package/dist/components/form/engine/rendererTypes.d.ts +35 -0
  49. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
  50. package/dist/components/form/engine/rendererTypes.js +63 -0
  51. package/dist/components/form/engine/rendererTypes.js.map +1 -0
  52. package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
  53. package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
  54. package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
  55. package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
  56. package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
  57. package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
  58. package/dist/components/form/engine/variants/VariantCards.js +80 -0
  59. package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
  60. package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
  61. package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
  62. package/dist/components/form/engine/variants/VariantFocus.js +138 -0
  63. package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
  64. package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
  65. package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
  66. package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
  67. package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
  68. package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
  69. package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
  70. package/dist/components/form/engine/variants/focusDemo.js +139 -0
  71. package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
  72. package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
  73. package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
  74. package/dist/components/form/engine/variants/variantModel.js +133 -0
  75. package/dist/components/form/engine/variants/variantModel.js.map +1 -0
  76. package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
  77. package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
  78. package/dist/components/form/engine/variants/variantParts.js +191 -0
  79. package/dist/components/form/engine/variants/variantParts.js.map +1 -0
  80. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  81. package/dist/components/form/expressions/builder/index.js +13 -2
  82. package/dist/components/form/expressions/builder/index.js.map +1 -1
  83. package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
  84. package/dist/components/form/expressions/useExpressions.js +4 -1
  85. package/dist/components/form/expressions/useExpressions.js.map +1 -1
  86. package/dist/components/form/fields/Field.d.ts.map +1 -1
  87. package/dist/components/form/fields/Field.js +5 -3
  88. package/dist/components/form/fields/Field.js.map +1 -1
  89. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
  90. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  91. package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
  92. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  93. package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
  94. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  95. package/dist/components/form/fields/auto/AutoFormField.js +42 -8
  96. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  97. package/dist/components/form/fields/binary/Binary.d.ts +15 -0
  98. package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
  99. package/dist/components/form/fields/binary/Binary.js +74 -0
  100. package/dist/components/form/fields/binary/Binary.js.map +1 -0
  101. package/dist/components/form/fields/boolean/Boolean.js +1 -1
  102. package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
  103. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  104. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
  105. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  106. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  107. package/dist/components/form/fields/select/Select.js +29 -12
  108. package/dist/components/form/fields/select/Select.js.map +1 -1
  109. package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
  110. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  111. package/dist/components/form/fields/select/SelectCollection.js +52 -16
  112. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  113. package/dist/components/form/index.d.ts +2 -0
  114. package/dist/components/form/index.d.ts.map +1 -1
  115. package/dist/components/form/index.js +2 -0
  116. package/dist/components/form/index.js.map +1 -1
  117. package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
  118. package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
  119. package/dist/components/imageLightbox/ImageLightbox.js +169 -0
  120. package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
  121. package/dist/components/imageLightbox/index.d.ts +2 -0
  122. package/dist/components/imageLightbox/index.d.ts.map +1 -0
  123. package/dist/components/imageLightbox/index.js +18 -0
  124. package/dist/components/imageLightbox/index.js.map +1 -0
  125. package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
  126. package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
  127. package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
  128. package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
  129. package/dist/components/searchFilterBar/index.d.ts +2 -0
  130. package/dist/components/searchFilterBar/index.d.ts.map +1 -0
  131. package/dist/components/searchFilterBar/index.js +18 -0
  132. package/dist/components/searchFilterBar/index.js.map +1 -0
  133. package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
  134. package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
  135. package/dist/components/supportTicket/AttachmentChips.js +122 -0
  136. package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
  137. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
  138. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
  139. package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
  140. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
  141. package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
  142. package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
  143. package/dist/components/supportTicket/ReferencePicker.js +295 -0
  144. package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
  145. package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
  146. package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
  147. package/dist/components/supportTicket/TicketHeader.js +149 -0
  148. package/dist/components/supportTicket/TicketHeader.js.map +1 -0
  149. package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
  150. package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
  151. package/dist/components/supportTicket/TicketMetaTags.js +21 -0
  152. package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
  153. package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
  154. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
  155. package/dist/components/supportTicket/TicketReplyBox.js +115 -0
  156. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
  157. package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
  158. package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
  159. package/dist/components/supportTicket/attachmentAccess.js +132 -0
  160. package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
  161. package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
  162. package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
  163. package/dist/components/supportTicket/fileToAttachment.js +28 -0
  164. package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
  165. package/dist/components/supportTicket/index.d.ts +11 -0
  166. package/dist/components/supportTicket/index.d.ts.map +1 -0
  167. package/dist/components/supportTicket/index.js +27 -0
  168. package/dist/components/supportTicket/index.js.map +1 -0
  169. package/dist/components/supportTicket/meta.d.ts +71 -0
  170. package/dist/components/supportTicket/meta.d.ts.map +1 -0
  171. package/dist/components/supportTicket/meta.js +115 -0
  172. package/dist/components/supportTicket/meta.js.map +1 -0
  173. package/dist/components/supportTicket/ticketModel.d.ts +102 -0
  174. package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
  175. package/dist/components/supportTicket/ticketModel.js +3 -0
  176. package/dist/components/supportTicket/ticketModel.js.map +1 -0
  177. package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
  178. package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
  179. package/dist/components/ticketReferences/TicketReferences.js +492 -0
  180. package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
  181. package/dist/components/ticketReferences/index.d.ts +3 -0
  182. package/dist/components/ticketReferences/index.d.ts.map +1 -0
  183. package/dist/components/ticketReferences/index.js +6 -0
  184. package/dist/components/ticketReferences/index.js.map +1 -0
  185. package/dist/components/ticketThread/TicketThread.d.ts +57 -0
  186. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
  187. package/dist/components/ticketThread/TicketThread.js +299 -0
  188. package/dist/components/ticketThread/TicketThread.js.map +1 -0
  189. package/dist/components/ticketThread/index.d.ts +3 -0
  190. package/dist/components/ticketThread/index.d.ts.map +1 -0
  191. package/dist/components/ticketThread/index.js +7 -0
  192. package/dist/components/ticketThread/index.js.map +1 -0
  193. package/dist/helpers/common.d.ts +5 -0
  194. package/dist/helpers/common.d.ts.map +1 -1
  195. package/dist/helpers/common.js +20 -1
  196. package/dist/helpers/common.js.map +1 -1
  197. package/dist/helpers/scrollbar.d.ts +2 -0
  198. package/dist/helpers/scrollbar.d.ts.map +1 -0
  199. package/dist/helpers/scrollbar.js +25 -0
  200. package/dist/helpers/scrollbar.js.map +1 -0
  201. package/dist/index.d.ts +8 -1
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +10 -1
  204. package/dist/index.js.map +1 -1
  205. package/dist/providers/StorageProvider.d.ts +13 -0
  206. package/dist/providers/StorageProvider.d.ts.map +1 -1
  207. package/dist/providers/StorageProvider.js +29 -5
  208. package/dist/providers/StorageProvider.js.map +1 -1
  209. package/package.json +6 -5
  210. package/src/components/FocusedEditing/index.tsx +17 -2
  211. package/src/components/composer/Composer.stories.tsx +238 -0
  212. package/src/components/composer/Composer.tsx +545 -0
  213. package/src/components/composer/helpers.ts +74 -0
  214. package/src/components/composer/index.ts +2 -0
  215. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
  216. package/src/components/form/engine/CompactRow.tsx +561 -283
  217. package/src/components/form/engine/CompactToolbar.tsx +216 -182
  218. package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
  219. package/src/components/form/engine/FormEngine.tsx +886 -170
  220. package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
  221. package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
  222. package/src/components/form/engine/compactRowContext.ts +14 -0
  223. package/src/components/form/engine/compactRowStyles.ts +273 -146
  224. package/src/components/form/engine/compactToolbarContext.ts +1 -0
  225. package/src/components/form/engine/optionActions.ts +40 -0
  226. package/src/components/form/engine/readFirst.ts +169 -10
  227. package/src/components/form/engine/rendererTypes.ts +55 -0
  228. package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
  229. package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
  230. package/src/components/form/engine/variants/VariantCards.tsx +212 -0
  231. package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
  232. package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
  233. package/src/components/form/engine/variants/focusDemo.ts +145 -0
  234. package/src/components/form/engine/variants/variantModel.ts +216 -0
  235. package/src/components/form/engine/variants/variantParts.tsx +313 -0
  236. package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
  237. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
  238. package/src/components/form/expressions/builder/index.tsx +12 -1
  239. package/src/components/form/expressions/useExpressions.ts +4 -1
  240. package/src/components/form/fields/Field.stories.tsx +40 -0
  241. package/src/components/form/fields/Field.tsx +10 -1
  242. package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
  243. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
  244. package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
  245. package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
  246. package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
  247. package/src/components/form/fields/binary/Binary.tsx +81 -0
  248. package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
  249. package/src/components/form/fields/boolean/Boolean.tsx +1 -1
  250. package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
  251. package/src/components/form/fields/color/Color.stories.tsx +30 -0
  252. package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
  253. package/src/components/form/fields/date/Date.stories.tsx +22 -0
  254. package/src/components/form/fields/file/File.stories.tsx +48 -0
  255. package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
  256. package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
  257. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
  258. package/src/components/form/fields/number/Number.stories.tsx +31 -0
  259. package/src/components/form/fields/object/Object.stories.tsx +88 -0
  260. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
  261. package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
  262. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
  263. package/src/components/form/fields/select/Select.stories.tsx +108 -0
  264. package/src/components/form/fields/select/Select.tsx +38 -12
  265. package/src/components/form/fields/select/SelectCollection.tsx +78 -17
  266. package/src/components/form/fields/string/String.stories.tsx +16 -0
  267. package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
  268. package/src/components/form/fields/url/Url.stories.tsx +24 -0
  269. package/src/components/form/index.tsx +2 -0
  270. package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
  271. package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
  272. package/src/components/imageLightbox/index.ts +1 -0
  273. package/src/components/log/Log.stories.tsx +96 -0
  274. package/src/components/menu/Menu.stories.tsx +54 -0
  275. package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
  276. package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
  277. package/src/components/searchFilterBar/index.ts +1 -0
  278. package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
  279. package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
  280. package/src/components/supportTicket/AttachmentChips.tsx +147 -0
  281. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
  282. package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
  283. package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
  284. package/src/components/supportTicket/ReferencePicker.tsx +563 -0
  285. package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
  286. package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
  287. package/src/components/supportTicket/TicketHeader.tsx +247 -0
  288. package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
  289. package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
  290. package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
  291. package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
  292. package/src/components/supportTicket/attachmentAccess.ts +97 -0
  293. package/src/components/supportTicket/fileToAttachment.ts +24 -0
  294. package/src/components/supportTicket/index.ts +10 -0
  295. package/src/components/supportTicket/meta.ts +185 -0
  296. package/src/components/supportTicket/ticketModel.ts +131 -0
  297. package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
  298. package/src/components/ticketReferences/TicketReferences.tsx +886 -0
  299. package/src/components/ticketReferences/index.ts +2 -0
  300. package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
  301. package/src/components/ticketThread/TicketThread.tsx +704 -0
  302. package/src/components/ticketThread/index.ts +6 -0
  303. package/src/helpers/common.ts +19 -0
  304. package/src/helpers/scrollbar.ts +32 -0
  305. package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
  306. package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
  307. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
  308. package/src/index.tsx +8 -1
  309. package/src/providers/StorageProvider.tsx +30 -4
  310. package/src/stores/currentUser/currentUser.stories.tsx +49 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * VARIANT 4 — "Minimal" (Notion / Linear inspired)
3
+ *
4
+ * Direction: the lightest possible treatment. No surfaces, no boxes, no group
5
+ * panels — just a quiet `label · value` list with a hairline status tick on the
6
+ * left edge. TAP a row to reveal its description and edit it inline — NO hover
7
+ * dependency, so it behaves identically on phone. Densest of the four.
8
+ */
9
+ import { ReqoreIcon, ReqoreP } from '@qoretechnologies/reqore';
10
+ import { IQorusFormField, IQorusFormSchema } from '@qoretechnologies/ts-toolkit';
11
+ import React from 'react';
12
+ import styled from 'styled-components';
13
+ import {
14
+ InlineEdit,
15
+ STATUS_COLOR,
16
+ ValueView,
17
+ VariantToolbar,
18
+ useVariantColors,
19
+ useVariantForm,
20
+ } from './variantParts';
21
+
22
+ const Wrap = styled.div<{ $hover: string; $line: string; $faint: string }>`
23
+ display: flex;
24
+ flex-flow: column;
25
+ gap: 4px;
26
+ font-size: 13px;
27
+
28
+ .vm-group {
29
+ font-size: 11px;
30
+ letter-spacing: 1px;
31
+ text-transform: uppercase;
32
+ padding: 16px 0 6px 14px;
33
+ }
34
+ .vm-row {
35
+ display: grid;
36
+ grid-template-columns: minmax(180px, 320px) minmax(0, 1fr);
37
+ gap: 14px;
38
+ align-items: baseline;
39
+ padding: 7px 12px 7px 14px;
40
+ border-left: 2px solid transparent;
41
+ cursor: pointer;
42
+ border-radius: 0 6px 6px 0;
43
+ }
44
+ .vm-row[aria-expanded='true'] {
45
+ background: ${({ $hover }) => $hover};
46
+ }
47
+ @media (hover: hover) {
48
+ .vm-row:hover {
49
+ background: ${({ $hover }) => $hover};
50
+ }
51
+ }
52
+ .vm-label {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 6px;
56
+ min-width: 0;
57
+ }
58
+ .vm-detail {
59
+ grid-column: 1 / -1;
60
+ color: ${({ $faint }) => $faint};
61
+ padding: 4px 0 2px;
62
+ font-size: 12px;
63
+ line-height: 1.5;
64
+ }
65
+ @media (max-width: 620px) {
66
+ .vm-row {
67
+ grid-template-columns: 1fr;
68
+ gap: 2px;
69
+ }
70
+ }
71
+ `;
72
+
73
+ export const VariantMinimal = ({
74
+ options,
75
+ values,
76
+ }: {
77
+ options: IQorusFormSchema;
78
+ values: Record<string, IQorusFormField>;
79
+ }) => {
80
+ const c = useVariantColors();
81
+ const form = useVariantForm(options, values);
82
+ const s = form.summary;
83
+
84
+ return (
85
+ <Wrap $hover={c.hover} $line={c.line} $faint={c.faint}>
86
+ <div
87
+ style={{
88
+ display: 'flex',
89
+ alignItems: 'center',
90
+ gap: 10,
91
+ padding: '0 12px 10px',
92
+ borderBottom: `1px solid ${c.line}`,
93
+ }}
94
+ >
95
+ <span style={{ width: 8, height: 8, borderRadius: '50%', background: s.attention ? c.warning : c.success }} />
96
+ <ReqoreP effect={{ weight: 'bold' }}>{s.pct}%</ReqoreP>
97
+ <span style={{ color: c.muted, fontSize: 12 }}>{s.set}/{s.total} set</span>
98
+ {s.attention ?
99
+ <button
100
+ type='button'
101
+ onClick={form.toggleAttention}
102
+ style={{
103
+ background: 'none',
104
+ border: 'none',
105
+ cursor: 'pointer',
106
+ color: c.warning,
107
+ fontSize: 12,
108
+ textDecoration: 'underline',
109
+ textUnderlineOffset: 3,
110
+ }}
111
+ >
112
+ {form.filter === 'attention' ? '← all' : `${s.attention} to resolve →`}
113
+ </button>
114
+ : null}
115
+ </div>
116
+ <VariantToolbar form={form} />
117
+
118
+ {form.visibleGroups.map((g) => (
119
+ <React.Fragment key={g.name}>
120
+ <div className='vm-group' style={{ color: c.faint }}>
121
+ {g.label}
122
+ </div>
123
+ {g.rows.map((r) => {
124
+ const editing = form.editing === r.name;
125
+ const hasDetail = !!(r.shortDesc || r.longDesc);
126
+ return (
127
+ <div
128
+ key={r.name}
129
+ className='vm-row'
130
+ role='button'
131
+ tabIndex={0}
132
+ aria-expanded={editing}
133
+ style={{ borderLeftColor: r.status === 'set' || r.status === 'unset' ? 'transparent' : STATUS_COLOR(r.status, c) }}
134
+ onClick={() => !r.readOnly && form.startEdit(r.name)}
135
+ >
136
+ <span className='vm-label' style={{ color: c.text, fontWeight: 500 }}>
137
+ {r.label}
138
+ {r.required ?
139
+ <ReqoreIcon icon='Asterisk' size='8px' style={{ color: c.danger }} />
140
+ : null}
141
+ </span>
142
+ <span style={{ color: c.muted, minWidth: 0, display: 'inline-flex', alignItems: 'baseline', gap: 8 }}>
143
+ <ValueView value={r.value} />
144
+ {r.status === 'invalid' || r.status === 'todo' ?
145
+ <span style={{ color: STATUS_COLOR(r.status, c), fontSize: 12 }}>· {r.reason}</span>
146
+ : null}
147
+ </span>
148
+ {editing ?
149
+ <>
150
+ {hasDetail ?
151
+ <div className='vm-detail'>
152
+ {r.shortDesc ? <div>{r.shortDesc}</div> : null}
153
+ {r.longDesc ?
154
+ <div style={{ marginTop: r.shortDesc ? 4 : 0 }}>
155
+ {r.longDesc.replace(/[#`*]/g, '')}
156
+ </div>
157
+ : null}
158
+ </div>
159
+ : null}
160
+ <InlineEdit row={r} onDone={form.stopEdit} />
161
+ </>
162
+ : null}
163
+ </div>
164
+ );
165
+ })}
166
+ </React.Fragment>
167
+ ))}
168
+ </Wrap>
169
+ );
170
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * A richer, realistic demo schema for the Focus-Pro variant — exercises the
3
+ * features the basic fixture lacks: NAMED groups, a one-of REQUIRED GROUP,
4
+ * a complex nested hash (Show-more preview), allowed-values, files/lists, plus
5
+ * a mix of set / invalid / required-unset / optional states. Modelled loosely
6
+ * on a Qorus datasource connection config.
7
+ */
8
+ import { IQorusFormField, IQorusFormSchema } from '@qoretechnologies/ts-toolkit';
9
+
10
+ export const getFocusDemoOptions = (): IQorusFormSchema =>
11
+ ({
12
+ // --- Connection -------------------------------------------------------
13
+ url: {
14
+ type: 'string',
15
+ ui_type: 'string',
16
+ display_name: 'Connection URL',
17
+ short_desc: 'The full datasource URL (driver://user@host:port/db).',
18
+ desc: 'Accepts any Qore datasource URL. Templates like `$config:db-url` are supported.',
19
+ group: 'connection',
20
+ required: true,
21
+ supports_templates: true,
22
+ },
23
+ port: {
24
+ type: 'int',
25
+ ui_type: 'number',
26
+ display_name: 'Port',
27
+ short_desc: 'TCP port of the database server.',
28
+ group: 'connection',
29
+ default_value: { type: 'int', value: 5432 },
30
+ },
31
+ database: {
32
+ type: 'string',
33
+ ui_type: 'string',
34
+ display_name: 'Database name',
35
+ short_desc: 'Name of the schema/database to connect to.',
36
+ group: 'connection',
37
+ required: true,
38
+ },
39
+ // --- Authentication (one-of required group) ---------------------------
40
+ token: {
41
+ type: 'string',
42
+ ui_type: 'string',
43
+ display_name: 'API token',
44
+ short_desc: 'Bearer token — use this OR a username/password.',
45
+ group: 'authentication',
46
+ required_groups: ['auth'],
47
+ supports_templates: true,
48
+ },
49
+ username: {
50
+ type: 'string',
51
+ ui_type: 'string',
52
+ display_name: 'Username',
53
+ short_desc: 'Database user — paired with a password.',
54
+ group: 'authentication',
55
+ required_groups: ['auth'],
56
+ },
57
+ password: {
58
+ type: 'string',
59
+ ui_type: 'string',
60
+ display_name: 'Password',
61
+ short_desc: 'Database password.',
62
+ group: 'authentication',
63
+ required_groups: ['auth'],
64
+ supports_templates: true,
65
+ },
66
+ // --- Advanced ---------------------------------------------------------
67
+ options: {
68
+ type: 'hash',
69
+ ui_type: 'hash',
70
+ display_name: 'Driver options',
71
+ short_desc: 'Extra key/value options passed to the driver.',
72
+ group: 'advanced',
73
+ },
74
+ poolSize: {
75
+ type: 'int',
76
+ ui_type: 'number',
77
+ display_name: 'Pool size',
78
+ short_desc: 'Max simultaneous connections.',
79
+ group: 'advanced',
80
+ default_value: { type: 'int', value: 10 },
81
+ },
82
+ timeout: {
83
+ type: 'int',
84
+ ui_type: 'number',
85
+ display_name: 'Timeout (s)',
86
+ short_desc: 'Connection timeout in seconds.',
87
+ group: 'advanced',
88
+ },
89
+ logLevel: {
90
+ type: 'string',
91
+ ui_type: 'string',
92
+ display_name: 'Log level',
93
+ short_desc: 'Verbosity of the connection log.',
94
+ group: 'advanced',
95
+ allowed_values: [
96
+ { value: 'error', display_name: 'Error' },
97
+ { value: 'info', display_name: 'Info' },
98
+ { value: 'debug', display_name: 'Debug' },
99
+ ],
100
+ },
101
+ // --- Optional (non-preselected — render in the Optional group) ---------
102
+ sslCert: {
103
+ type: 'file',
104
+ ui_type: 'file',
105
+ display_name: 'SSL certificate',
106
+ short_desc: 'Client certificate for mutual TLS.',
107
+ },
108
+ tags: {
109
+ type: 'list',
110
+ ui_type: 'list',
111
+ display_name: 'Tags',
112
+ short_desc: 'Free-form labels for this connection.',
113
+ },
114
+ }) as unknown as IQorusFormSchema;
115
+
116
+ export const focusDemoValue: Record<string, IQorusFormField> = {
117
+ url: { type: 'string', value: 'pgsql://hq.qoretechnologies.com:5432/omq' },
118
+ // database: intentionally unset → required to-do
119
+ // auth group: intentionally none set → one-of unsatisfied → attention
120
+ options: {
121
+ type: 'hash',
122
+ value: {
123
+ sslmode: { type: 'string', value: 'require' },
124
+ application_name: { type: 'string', value: 'qorus-ide' },
125
+ connect_timeout: { type: 'int', value: 8 },
126
+ },
127
+ },
128
+ logLevel: { type: 'string', value: 'info' },
129
+ // timeout: invalid (entered 0)
130
+ timeout: { type: 'int', value: 0 as any },
131
+ };
132
+
133
+ // Demo-only invalid reasons (in the engine these come from the validity pass).
134
+ export const focusDemoInvalid: Record<string, string> = {
135
+ timeout: 'Must be greater than 0',
136
+ };
137
+
138
+ // Display labels for the named groups.
139
+ export const focusDemoGroupLabels: Record<string, string> = {
140
+ connection: 'Connection',
141
+ authentication: 'Authentication',
142
+ advanced: 'Advanced',
143
+ optional: 'Optional',
144
+ general: 'General',
145
+ };
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Shared row model for the FormEngine compact VARIANTS playground.
3
+ *
4
+ * This is a prototyping harness: every variant renders the SAME normalized rows
5
+ * (derived from a real options schema + values via the real `readFirst`
6
+ * formatters) and differs only in layout / chrome / UX. That keeps the
7
+ * comparison about presentation, not data. Editing is intentionally stubbed
8
+ * (expand shows a placeholder) — the point is to compare the read-first view
9
+ * that currently feels crowded, then graft the winner onto the real engine.
10
+ */
11
+ import { IQorusFormField, IQorusFormSchema } from '@qoretechnologies/ts-toolkit';
12
+ import {
13
+ colorToCss,
14
+ formatBytes,
15
+ formatOptionValue,
16
+ getFileSize,
17
+ getOptionGroup,
18
+ getOptionGroupLabel,
19
+ getValueType,
20
+ isOptionValueEmpty,
21
+ } from '../readFirst';
22
+ import { isValueTemplate } from '../../../../helpers/templates';
23
+
24
+ export type TVariantStatus =
25
+ | 'set' // has a valid value
26
+ | 'unset' // empty, not required — calm, no alarm
27
+ | 'todo' // required (or required-group) but unset — gentle amber
28
+ | 'invalid'; // user-entered value fails validation — red (only when surfaced)
29
+
30
+ export type TVariantValueKind =
31
+ | 'text'
32
+ | 'number'
33
+ | 'bool'
34
+ | 'color'
35
+ | 'file'
36
+ | 'template'
37
+ | 'richtext'
38
+ | 'hash'
39
+ | 'empty';
40
+
41
+ export interface IVariantValue {
42
+ kind: TVariantValueKind;
43
+ /** Human-readable summary (already formatted by readFirst). */
44
+ display: string;
45
+ /** color: css color; file: size label; hash: field count; template: label */
46
+ extra?: string;
47
+ }
48
+
49
+ export interface IVariantRow {
50
+ name: string;
51
+ label: string;
52
+ shortDesc?: string;
53
+ longDesc?: string;
54
+ required: boolean;
55
+ readOnly: boolean;
56
+ status: TVariantStatus;
57
+ /** Short reason for todo/invalid, e.g. "Text value is empty". */
58
+ reason?: string;
59
+ value: IVariantValue;
60
+ typeLabel: string;
61
+ /** required_groups this field belongs to (one-of constraints). */
62
+ requiredGroups: string[];
63
+ /** True when this field is a member of an unsatisfied one-of group. */
64
+ inUnmetGroup: boolean;
65
+ /** Raw schema + value object — passed to AutoFormField for REAL editing. */
66
+ schema: any;
67
+ field: any;
68
+ }
69
+
70
+ export interface IVariantGroup {
71
+ name: string;
72
+ label: string;
73
+ rows: IVariantRow[];
74
+ }
75
+
76
+ /** One-of required group: members + whether any is filled. */
77
+ export interface IRequiredGroupInfo {
78
+ key: string;
79
+ members: string[];
80
+ memberLabels: string[];
81
+ satisfied: boolean;
82
+ }
83
+
84
+ export interface IVariantConfig {
85
+ groupLabels?: Record<string, string>;
86
+ /** name → reason; in the engine this comes from the validity pass. */
87
+ invalidReasons?: Record<string, string>;
88
+ }
89
+
90
+ // Default (basic-fixture) invalid set — overridable via config.
91
+ const DEFAULT_INVALID: Record<string, string> = {
92
+ optionWithInvalidValue: 'Text value is empty',
93
+ schemaOption: 'Hash arguments are invalid',
94
+ };
95
+
96
+ function buildValue(field: IQorusFormField | undefined, schema: any): IVariantValue {
97
+ if (!field || isOptionValueEmpty(field.value)) return { kind: 'empty', display: '' };
98
+
99
+ const valueType = getValueType(field, schema);
100
+ const formatted = formatOptionValue(field, schema);
101
+
102
+ if (valueType === 'rgbcolor') {
103
+ return { kind: 'color', display: formatted, extra: colorToCss(field.value) };
104
+ }
105
+ if (valueType === 'file') {
106
+ const size = getFileSize(field.value);
107
+ return {
108
+ kind: 'file',
109
+ display: formatted,
110
+ extra: size !== undefined ? formatBytes(size) : undefined,
111
+ };
112
+ }
113
+ if (valueType === 'bool' || valueType === 'boolean') {
114
+ return { kind: 'bool', display: formatted };
115
+ }
116
+ if (valueType === 'hash' || valueType === 'free-hash') {
117
+ return { kind: 'hash', display: formatted };
118
+ }
119
+ if (typeof field.value === 'string' && isValueTemplate(field.value)) {
120
+ // The real chip resolves the template's display name from the templates
121
+ // list; the prototype shows a friendly label of the key.
122
+ const label =
123
+ field.value.replace(/^\$[a-z]+:/i, '').replace(/[-_]/g, ' ') || field.value;
124
+ return { kind: 'template', display: label.replace(/\b\w/g, (c) => c.toUpperCase()), extra: 'local' };
125
+ }
126
+ if (valueType === 'richtext' && Array.isArray(field.value)) {
127
+ return { kind: 'richtext', display: formatted };
128
+ }
129
+ return { kind: 'text', display: formatted };
130
+ }
131
+
132
+ export function buildVariantGroups(
133
+ options: IQorusFormSchema,
134
+ values: Record<string, IQorusFormField>,
135
+ config: IVariantConfig = {}
136
+ ): { groups: IVariantGroup[]; requiredGroups: Record<string, IRequiredGroupInfo> } {
137
+ const invalidReasons = config.invalidReasons ?? DEFAULT_INVALID;
138
+ const groupMap = new Map<string, IVariantGroup>();
139
+
140
+ // Pass 1: collect one-of required-group membership + satisfaction.
141
+ const reqGroups: Record<string, IRequiredGroupInfo> = {};
142
+ Object.entries(options).forEach(([name, schema]: [string, any]) => {
143
+ (schema?.required_groups || []).forEach((key: string) => {
144
+ if (!reqGroups[key]) reqGroups[key] = { key, members: [], memberLabels: [], satisfied: false };
145
+ reqGroups[key].members.push(name);
146
+ reqGroups[key].memberLabels.push((schema?.display_name as string) || name);
147
+ if (!isOptionValueEmpty(values[name]?.value)) reqGroups[key].satisfied = true;
148
+ });
149
+ });
150
+
151
+ Object.entries(options).forEach(([name, schema]: [string, any]) => {
152
+ const field = values[name];
153
+ const empty = isOptionValueEmpty(field?.value);
154
+ const ownGroups: string[] = schema?.required_groups || [];
155
+ // Member of a one-of group that nobody has satisfied yet.
156
+ const inUnmetGroup = ownGroups.some((k) => reqGroups[k] && !reqGroups[k].satisfied);
157
+ const required = !!schema?.required;
158
+
159
+ let status: TVariantStatus;
160
+ let reason: string | undefined;
161
+ if (invalidReasons[name]) {
162
+ status = 'invalid';
163
+ reason = invalidReasons[name];
164
+ } else if (empty && required) {
165
+ status = 'todo';
166
+ reason = 'Needs a value';
167
+ } else if (empty && inUnmetGroup) {
168
+ status = 'todo';
169
+ reason = `One of ${reqGroups[ownGroups[0]].memberLabels.join(' / ')}`;
170
+ } else if (empty) {
171
+ status = 'unset';
172
+ } else {
173
+ status = 'set';
174
+ }
175
+
176
+ const groupName = getOptionGroup(schema);
177
+ const groupLabel = config.groupLabels?.[groupName] ?? getOptionGroupLabel(groupName, undefined);
178
+ if (!groupMap.has(groupName)) {
179
+ groupMap.set(groupName, { name: groupName, label: groupLabel, rows: [] });
180
+ }
181
+ groupMap.get(groupName)!.rows.push({
182
+ name,
183
+ label: (schema?.display_name as string) || name,
184
+ shortDesc: schema?.short_desc,
185
+ longDesc: schema?.desc,
186
+ required: required || ownGroups.length > 0,
187
+ readOnly: !!schema?.readonly,
188
+ status,
189
+ reason,
190
+ value: buildValue(field, schema),
191
+ typeLabel: `${(schema?.ui_type as string) || (schema?.type as string) || 'auto'}`,
192
+ requiredGroups: ownGroups,
193
+ inUnmetGroup,
194
+ schema,
195
+ field,
196
+ });
197
+ });
198
+
199
+ // Stable group order: 'general' first, then the rest, 'optional' last.
200
+ const groups = Array.from(groupMap.values()).sort((a, b) => {
201
+ const rank = (g: string) => (g === 'general' ? 0 : g === 'optional' ? 2 : 1);
202
+ return rank(a.name) - rank(b.name);
203
+ });
204
+ return { groups, requiredGroups: reqGroups };
205
+ }
206
+
207
+ export function summarize(groups: IVariantGroup[]) {
208
+ const rows = groups.flatMap((g) => g.rows);
209
+ const total = rows.length;
210
+ const set = rows.filter((r) => r.status === 'set').length;
211
+ const todo = rows.filter((r) => r.status === 'todo').length;
212
+ const invalid = rows.filter((r) => r.status === 'invalid').length;
213
+ const attention = todo + invalid;
214
+ const pct = total ? Math.round((set / total) * 100) : 0;
215
+ return { total, set, todo, invalid, attention, pct };
216
+ }