@morscherlab/mint-sdk 1.0.0-beta.1 → 1.0.0-beta.3

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 (421) hide show
  1. package/README.md +218 -6
  2. package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
  3. package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
  6. package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
  7. package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
  8. package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
  9. package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
  10. package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
  11. package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
  12. package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
  13. package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
  14. package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
  15. package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
  16. package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
  17. package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
  18. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  19. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  20. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  21. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  22. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  23. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  24. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  25. package/dist/__tests__/components/FormCompatibility.test.d.ts +1 -0
  26. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  27. package/dist/__tests__/components/GroupingModal.test.d.ts +1 -0
  28. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  29. package/dist/__tests__/components/PluginIcon.test.d.ts +1 -0
  30. package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
  31. package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
  32. package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
  33. package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
  34. package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
  35. package/dist/__tests__/components/SettingsButton.test.d.ts +1 -0
  36. package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
  37. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  38. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  39. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  40. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  41. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  42. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  43. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  44. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  45. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  46. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  47. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  48. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  49. package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
  50. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  51. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  52. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  53. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  54. package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
  55. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  56. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  57. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  58. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  59. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  60. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  61. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  62. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  63. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  64. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  65. package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
  66. package/dist/auth-QQj2kkze.js.map +1 -0
  67. package/dist/components/ActionItem.vue.d.ts +32 -0
  68. package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
  69. package/dist/components/AppPageSelector.vue.d.ts +3 -6
  70. package/dist/components/AppPillNav.vue.d.ts +2 -2
  71. package/dist/components/AppSidebar.vue.d.ts +56 -3
  72. package/dist/components/AppToastContainer.vue.d.ts +2 -0
  73. package/dist/components/AppTopBar.vue.d.ts +43 -10
  74. package/dist/components/BaseButton.vue.d.ts +2 -2
  75. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  76. package/dist/components/BaseInput.vue.d.ts +2 -2
  77. package/dist/components/BasePill.vue.d.ts +3 -3
  78. package/dist/components/BaseRadioGroup.vue.d.ts +4 -4
  79. package/dist/components/BaseSelect.vue.d.ts +4 -4
  80. package/dist/components/BaseSlider.vue.d.ts +1 -1
  81. package/dist/components/BaseTabs.vue.d.ts +2 -2
  82. package/dist/components/BaseTextarea.vue.d.ts +2 -2
  83. package/dist/components/BaseToggle.vue.d.ts +1 -1
  84. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +117 -0
  85. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +92 -0
  86. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +82 -0
  87. package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
  88. package/dist/components/Breadcrumb.vue.d.ts +2 -2
  89. package/dist/components/Calendar.vue.d.ts +1 -1
  90. package/dist/components/CalendarGridPanel.vue.d.ts +25 -0
  91. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  92. package/dist/components/ColorSlider.vue.d.ts +1 -1
  93. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  94. package/dist/components/ConfirmDialog.vue.d.ts +2 -2
  95. package/dist/components/ControlWorkspaceView.vue.d.ts +130 -0
  96. package/dist/components/DatePicker.vue.d.ts +2 -2
  97. package/dist/components/DateTimePicker.vue.d.ts +3 -3
  98. package/dist/components/DropdownButton.vue.d.ts +4 -4
  99. package/dist/components/EmptyState.vue.d.ts +1 -2
  100. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  101. package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
  102. package/dist/components/FileUploader.vue.d.ts +2 -2
  103. package/dist/components/FitPanel.vue.d.ts +1 -1
  104. package/dist/components/FormActions.vue.d.ts +4 -4
  105. package/dist/components/FormBuilder.vue.d.ts +22 -8
  106. package/dist/components/FormFieldRenderer.vue.d.ts +7 -10
  107. package/dist/components/FormSection.vue.d.ts +11 -24
  108. package/dist/components/FormulaInput.vue.d.ts +2 -2
  109. package/dist/components/IconButton.vue.d.ts +1 -1
  110. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  111. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  112. package/dist/components/MultiSelect.vue.d.ts +3 -3
  113. package/dist/components/NumberInput.vue.d.ts +2 -2
  114. package/dist/components/PluginIcon.vue.d.ts +11 -0
  115. package/dist/components/ProgressBar.vue.d.ts +2 -2
  116. package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
  117. package/dist/components/RackEditor.vue.d.ts +2 -2
  118. package/dist/components/ReagentEditor.vue.d.ts +1 -1
  119. package/dist/components/ResourceCard.vue.d.ts +1 -1
  120. package/dist/components/SampleLegend.vue.d.ts +2 -2
  121. package/dist/components/SampleSelector.vue.d.ts +1 -1
  122. package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
  123. package/dist/components/ScientificNumber.vue.d.ts +1 -1
  124. package/dist/components/SegmentedControl.vue.d.ts +3 -3
  125. package/dist/components/SequenceInput.vue.d.ts +3 -3
  126. package/dist/components/SettingsButton.vue.d.ts +2 -2
  127. package/dist/components/SettingsModal.vue.d.ts +32 -4
  128. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  129. package/dist/components/TagsInput.vue.d.ts +3 -2
  130. package/dist/components/TimePicker.vue.d.ts +3 -3
  131. package/dist/components/TimeRangeInput.vue.d.ts +2 -2
  132. package/dist/components/UnitInput.vue.d.ts +2 -2
  133. package/dist/components/WellPlate.vue.d.ts +8 -8
  134. package/dist/components/index.d.ts +12 -1
  135. package/dist/components/index.js +3 -3
  136. package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +31 -0
  137. package/dist/components/internal/FormSectionRenderer.vue.d.ts +43 -0
  138. package/dist/{components-CzbQQPCb.js → components-D_Sr0adg.js} +9629 -8647
  139. package/dist/components-D_Sr0adg.js.map +1 -0
  140. package/dist/composables/index.d.ts +21 -2
  141. package/dist/composables/index.js +4 -3
  142. package/dist/composables/platformContextHelpers.d.ts +14 -0
  143. package/dist/composables/useBioTemplateComponents.d.ts +20 -0
  144. package/dist/composables/useBioTemplateControls.d.ts +6 -0
  145. package/dist/composables/useBioTemplatePackWorkspace.d.ts +45 -0
  146. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +74 -0
  147. package/dist/composables/useBioTemplateWorkspace.d.ts +50 -0
  148. package/dist/composables/useCalendarGrid.d.ts +26 -0
  149. package/dist/composables/useControlSchema.d.ts +321 -0
  150. package/dist/composables/useDebouncedWatch.d.ts +20 -0
  151. package/dist/composables/useDropdownState.d.ts +19 -0
  152. package/dist/composables/useEventListener.d.ts +13 -0
  153. package/dist/composables/useExpansionSet.d.ts +21 -0
  154. package/dist/composables/useExperimentData.d.ts +10 -0
  155. package/dist/composables/useExperimentSave.d.ts +31 -2
  156. package/dist/composables/useExperimentSelector.d.ts +20 -0
  157. package/dist/composables/useForm.d.ts +2 -0
  158. package/dist/composables/useGroupAssignment.d.ts +31 -0
  159. package/dist/composables/useListSelection.d.ts +35 -0
  160. package/dist/composables/usePlatformContext.d.ts +24 -3
  161. package/dist/composables/usePluginApi.d.ts +7 -14
  162. package/dist/composables/usePluginClient.d.ts +109 -0
  163. package/dist/composables/usePluginConfig.d.ts +12 -0
  164. package/dist/composables/useRequestSyncState.d.ts +34 -0
  165. package/dist/composables/useSampleGroups.d.ts +32 -0
  166. package/dist/composables/useSelectionLimit.d.ts +17 -0
  167. package/dist/composables/useSortedItems.d.ts +32 -0
  168. package/dist/composables/useTemplateCollection.d.ts +58 -0
  169. package/dist/composables/useTextSearch.d.ts +18 -0
  170. package/dist/composables/useTimeUtils.d.ts +8 -0
  171. package/dist/{composables-BXklV5ii.js → composables-C3dpXQN5.js} +228 -146
  172. package/dist/composables-C3dpXQN5.js.map +1 -0
  173. package/dist/index.d.ts +12 -3
  174. package/dist/index.js +6 -5
  175. package/dist/install.d.ts +7 -2
  176. package/dist/install.js +2 -2
  177. package/dist/install.js.map +1 -1
  178. package/dist/stores/auth.d.ts +1 -1
  179. package/dist/stores/index.js +1 -1
  180. package/dist/stores/settings.d.ts +4 -1
  181. package/dist/styles.css +5255 -5654
  182. package/dist/templates/adapters.d.ts +43 -0
  183. package/dist/templates/builders.d.ts +63 -0
  184. package/dist/templates/catalog.d.ts +188 -0
  185. package/dist/templates/componentBindings.d.ts +58 -0
  186. package/dist/templates/controlSchemas.d.ts +25 -0
  187. package/dist/templates/index.d.ts +15 -0
  188. package/dist/templates/index.js +2 -0
  189. package/dist/templates/lookup.d.ts +4 -0
  190. package/dist/templates/packs.d.ts +18 -0
  191. package/dist/templates/presets.d.ts +90 -0
  192. package/dist/templates/types.d.ts +531 -0
  193. package/dist/templates-50NPjaxL.js +9333 -0
  194. package/dist/templates-50NPjaxL.js.map +1 -0
  195. package/dist/types/components.d.ts +62 -1
  196. package/dist/types/form-builder.d.ts +6 -8
  197. package/dist/types/index.d.ts +2 -2
  198. package/dist/types/platform.d.ts +8 -1
  199. package/dist/useScheduleDrag-D4oWdh41.js +4371 -0
  200. package/dist/useScheduleDrag-D4oWdh41.js.map +1 -0
  201. package/dist/utils/formModelSync.d.ts +5 -0
  202. package/dist/utils/items.d.ts +8 -0
  203. package/dist/utils/options.d.ts +6 -0
  204. package/dist/utils/pluginIcon.d.ts +9 -0
  205. package/package.json +7 -2
  206. package/src/__tests__/components/ActionItem.test.ts +99 -0
  207. package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
  208. package/src/__tests__/components/AppPageSelector.test.ts +134 -0
  209. package/src/__tests__/components/AppPillNav.test.ts +78 -0
  210. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  211. package/src/__tests__/components/AppSidebar.test.ts +370 -0
  212. package/src/__tests__/components/AppToastContainer.test.ts +48 -0
  213. package/src/__tests__/components/AppTopBar.test.ts +414 -13
  214. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  215. package/src/__tests__/components/BaseSelect.test.ts +21 -0
  216. package/src/__tests__/components/BaseTabs.test.ts +25 -0
  217. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  218. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +153 -0
  219. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +161 -0
  220. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +281 -0
  221. package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
  222. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  223. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  224. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  225. package/src/__tests__/components/ControlWorkspaceView.test.ts +1031 -0
  226. package/src/__tests__/components/DataFrame.test.ts +11 -0
  227. package/src/__tests__/components/DatePicker.test.ts +45 -0
  228. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  229. package/src/__tests__/components/DropdownButton.test.ts +23 -0
  230. package/src/__tests__/components/EmptyState.test.ts +23 -0
  231. package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
  232. package/src/__tests__/components/FormBuilder.test.ts +296 -0
  233. package/src/__tests__/components/FormCompatibility.test.ts +94 -0
  234. package/src/__tests__/components/GroupAssigner.test.ts +30 -0
  235. package/src/__tests__/components/GroupingModal.test.ts +73 -0
  236. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  237. package/src/__tests__/components/PluginIcon.test.ts +119 -0
  238. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  239. package/src/__tests__/components/ReagentList.test.ts +82 -0
  240. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  241. package/src/__tests__/components/SampleSelector.test.ts +60 -0
  242. package/src/__tests__/components/SegmentedControl.test.ts +24 -0
  243. package/src/__tests__/components/SettingsButton.test.ts +44 -0
  244. package/src/__tests__/components/SettingsModal.test.ts +296 -0
  245. package/src/__tests__/components/TagsInput.test.ts +75 -0
  246. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  247. package/src/__tests__/components/TimePicker.test.ts +38 -0
  248. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +122 -0
  249. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  250. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +99 -0
  251. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  252. package/src/__tests__/composables/useControlSchema.test.ts +919 -0
  253. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  254. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  255. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  256. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  257. package/src/__tests__/composables/useExperimentData.test.ts +4 -0
  258. package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
  259. package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
  260. package/src/__tests__/composables/useForm.test.ts +58 -0
  261. package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
  262. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  263. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  264. package/src/__tests__/composables/usePluginClient.test.ts +444 -0
  265. package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
  266. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  267. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  268. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  269. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  270. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  271. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  272. package/src/__tests__/composables/useTheme.test.ts +91 -0
  273. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  274. package/src/__tests__/docs/frontendDocsCatalog.test.ts +229 -0
  275. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  276. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  277. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  278. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  279. package/src/__tests__/templates/templates.test.ts +1043 -0
  280. package/src/components/ActionItem.vue +82 -0
  281. package/src/components/AppAvatarMenu.vue +15 -69
  282. package/src/components/AppLayout.story.vue +25 -25
  283. package/src/components/AppPageSelector.vue +63 -94
  284. package/src/components/AppPillNav.vue +44 -39
  285. package/src/components/AppPluginSwitcher.vue +41 -145
  286. package/src/components/AppSidebar.story.vue +94 -0
  287. package/src/components/AppSidebar.vue +187 -12
  288. package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
  289. package/src/components/AppToastContainer.vue +62 -0
  290. package/src/components/AppTopBar.story.vue +7 -30
  291. package/src/components/AppTopBar.vue +283 -84
  292. package/src/components/BaseModal.vue +3 -5
  293. package/src/components/BaseRadioGroup.vue +7 -3
  294. package/src/components/BaseSelect.vue +11 -7
  295. package/src/components/BaseTabs.vue +6 -4
  296. package/src/components/BatchProgressList.vue +5 -8
  297. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  298. package/src/components/BioTemplateExperimentWorkspaceView.vue +337 -0
  299. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  300. package/src/components/BioTemplatePackWorkspaceView.vue +176 -0
  301. package/src/components/BioTemplatePresetWorkspaceView.story.vue +151 -0
  302. package/src/components/BioTemplatePresetWorkspaceView.vue +392 -0
  303. package/src/components/BioTemplateRenderer.story.vue +57 -0
  304. package/src/components/BioTemplateRenderer.vue +269 -0
  305. package/src/components/Breadcrumb.vue +14 -8
  306. package/src/components/CalendarGridPanel.vue +120 -0
  307. package/src/components/ConcentrationInput.vue +27 -64
  308. package/src/components/ControlWorkspaceView.story.vue +336 -0
  309. package/src/components/ControlWorkspaceView.vue +347 -0
  310. package/src/components/DataFrame.vue +34 -50
  311. package/src/components/DatePicker.vue +59 -192
  312. package/src/components/DateTimePicker.vue +50 -171
  313. package/src/components/DropdownButton.vue +14 -32
  314. package/src/components/EmptyState.vue +4 -2
  315. package/src/components/ExperimentPopover.vue +5 -22
  316. package/src/components/FormBuilder.vue +124 -27
  317. package/src/components/FormFieldRenderer.vue +15 -38
  318. package/src/components/FormSection.vue +20 -73
  319. package/src/components/GroupAssigner.vue +24 -56
  320. package/src/components/GroupingModal.story.vue +3 -3
  321. package/src/components/GroupingModal.vue +30 -391
  322. package/src/components/MultiSelect.vue +17 -12
  323. package/src/components/PlateMapEditor.vue +3 -8
  324. package/src/components/PluginIcon.story.vue +71 -0
  325. package/src/components/PluginIcon.vue +68 -0
  326. package/src/components/ProtocolStepEditor.vue +13 -22
  327. package/src/components/ReagentList.vue +25 -33
  328. package/src/components/SampleHierarchyTree.vue +12 -23
  329. package/src/components/SampleSelector.vue +42 -122
  330. package/src/components/SegmentedControl.vue +7 -3
  331. package/src/components/SettingsButton.story.vue +1 -1
  332. package/src/components/SettingsButton.vue +15 -27
  333. package/src/components/SettingsModal.story.vue +337 -45
  334. package/src/components/SettingsModal.vue +344 -66
  335. package/src/components/TagsInput.vue +29 -14
  336. package/src/components/ThemeToggle.vue +9 -7
  337. package/src/components/TimePicker.vue +19 -41
  338. package/src/components/ToastNotification.vue +4 -57
  339. package/src/components/Tooltip.vue +7 -12
  340. package/src/components/WellEditPopup.vue +3 -8
  341. package/src/components/WellPlate.vue +4 -10
  342. package/src/components/index.ts +12 -1
  343. package/src/components/internal/FormFieldRendererInternal.vue +50 -0
  344. package/src/components/internal/FormSectionRenderer.vue +78 -0
  345. package/src/composables/index.ts +212 -0
  346. package/src/composables/platformContextHelpers.ts +74 -0
  347. package/src/composables/useBioTemplateComponents.ts +93 -0
  348. package/src/composables/useBioTemplateControls.ts +41 -0
  349. package/src/composables/useBioTemplatePackWorkspace.ts +181 -0
  350. package/src/composables/useBioTemplatePresetWorkspace.ts +337 -0
  351. package/src/composables/useBioTemplateWorkspace.ts +139 -0
  352. package/src/composables/useCalendarGrid.ts +140 -0
  353. package/src/composables/useControlSchema.ts +1274 -0
  354. package/src/composables/useDebouncedWatch.ts +119 -0
  355. package/src/composables/useDropdownState.ts +83 -0
  356. package/src/composables/useEventListener.ts +111 -0
  357. package/src/composables/useExpansionSet.ts +117 -0
  358. package/src/composables/useExperimentData.ts +20 -11
  359. package/src/composables/useExperimentSave.ts +202 -50
  360. package/src/composables/useExperimentSelector.ts +86 -72
  361. package/src/composables/useForm.ts +49 -4
  362. package/src/composables/useFormBuilder.ts +93 -42
  363. package/src/composables/useGroupAssignment.ts +148 -0
  364. package/src/composables/useListSelection.ts +158 -0
  365. package/src/composables/usePluginApi.ts +7 -14
  366. package/src/composables/usePluginClient.ts +425 -0
  367. package/src/composables/usePluginConfig.ts +34 -13
  368. package/src/composables/useRequestSyncState.ts +126 -0
  369. package/src/composables/useSampleGroups.ts +126 -0
  370. package/src/composables/useSelectionLimit.ts +57 -0
  371. package/src/composables/useSortedItems.ts +118 -0
  372. package/src/composables/useTemplateCollection.ts +229 -0
  373. package/src/composables/useTextSearch.ts +60 -0
  374. package/src/composables/useTheme.ts +2 -28
  375. package/src/composables/useTimeUtils.ts +26 -2
  376. package/src/composables/useWellPlateEditor.ts +13 -9
  377. package/src/index.ts +228 -4
  378. package/src/install.ts +11 -4
  379. package/src/stores/settings.ts +13 -9
  380. package/src/styles/components/app-page-selector.css +23 -0
  381. package/src/styles/components/app-pill-nav.css +8 -2
  382. package/src/styles/components/app-top-bar.css +35 -2
  383. package/src/styles/components/button.css +3 -7
  384. package/src/styles/components/concentration-input.css +3 -142
  385. package/src/styles/components/dropdown-button.css +4 -4
  386. package/src/styles/components/empty-state.css +0 -16
  387. package/src/styles/components/input.css +4 -5
  388. package/src/styles/components/number-input.css +3 -3
  389. package/src/styles/components/plugin-icon.css +38 -0
  390. package/src/styles/components/segmented-control.css +4 -7
  391. package/src/styles/components/settings-button.css +3 -66
  392. package/src/styles/components/settings-modal.css +184 -0
  393. package/src/styles/components/tabs.css +1 -2
  394. package/src/styles/components/textarea.css +4 -5
  395. package/src/styles/components/theme-toggle.css +3 -66
  396. package/src/styles/components/unit-input.css +3 -3
  397. package/src/styles/index.css +0 -1
  398. package/src/templates/adapters.ts +785 -0
  399. package/src/templates/builders.ts +2149 -0
  400. package/src/templates/catalog.ts +245 -0
  401. package/src/templates/componentBindings.ts +615 -0
  402. package/src/templates/controlSchemas.ts +718 -0
  403. package/src/templates/index.ts +314 -0
  404. package/src/templates/lookup.ts +18 -0
  405. package/src/templates/packs.ts +156 -0
  406. package/src/templates/presets.ts +146 -0
  407. package/src/templates/types.ts +668 -0
  408. package/src/types/components.ts +80 -1
  409. package/src/types/form-builder.ts +7 -2
  410. package/src/types/index.ts +17 -0
  411. package/src/types/platform.ts +8 -1
  412. package/src/utils/formModelSync.ts +52 -0
  413. package/src/utils/items.ts +28 -0
  414. package/src/utils/options.ts +23 -0
  415. package/src/utils/pluginIcon.ts +30 -0
  416. package/dist/auth-DsI0rQ7_.js.map +0 -1
  417. package/dist/components-CzbQQPCb.js.map +0 -1
  418. package/dist/composables-BXklV5ii.js.map +0 -1
  419. package/dist/useScheduleDrag-CxBeqYcu.js +0 -7181
  420. package/dist/useScheduleDrag-CxBeqYcu.js.map +0 -1
  421. package/src/styles/components/grouping-modal.css +0 -323
@@ -2,43 +2,75 @@
2
2
  /**
3
3
  * Schema-driven form component that renders flat or multi-step wizard forms.
4
4
  *
5
- * Pass a `FormSchema` with either `sections` (flat) or `steps` (wizard).
5
+ * Pass a `FormSchema` with either `sections` (flat) or `steps` (wizard), or
6
+ * pass a compact `controls` schema for standard generated forms.
6
7
  * Use `v-model` for two-way binding of the form data; the `submit` event
7
8
  * carries only the data for currently-visible fields. Supply `enhancements`
8
9
  * for dynamic options, custom validators, a submit handler, and field-change
9
10
  * callbacks that cannot be expressed in JSON.
11
+ * Compact control schemas can be bound directly:
12
+ * `<FormBuilder :controls="controls" v-model="values" />`.
13
+ * Complete control models can also be bound directly:
14
+ * `<FormBuilder :model="workspaceModel" v-model="values" />`.
10
15
  *
11
- * Exposes `form`, `validate`, `reset`, `goNext`, `goBack`, `goToStep`, and
12
- * `builder` for imperative control via template refs.
16
+ * Exposes `form`, `validate`, `reset`, `updateSchema`, `goNext`, `goBack`,
17
+ * `goToStep`, and `builder` for imperative control via template refs.
13
18
  *
14
19
  * Slots:
15
20
  * - `field:<name>` — override a single field's input component
16
21
  * - `section:<id>` — replace an entire section body
17
22
  * - `section:<id>:after` — inject content after a section
18
- * - `actions` — replace the default FormActions bar
23
+ * - `actions` — replace the default FormActions bar when `showActions` is true
19
24
  */
20
25
  import { ref, computed, watch } from 'vue'
21
26
  import type { FormSchema, FormEnhancements, UseFormBuilderReturn } from '../types/form-builder'
22
27
  import type { WizardStep } from '../types'
28
+ import {
29
+ controlsToFormSchema,
30
+ defineControlModel,
31
+ getControlDefaults,
32
+ mergeControlWorkspaceOptions,
33
+ type ControlModel,
34
+ type ControlModelBinding,
35
+ type ControlSchema,
36
+ type ControlWorkspaceOptions,
37
+ } from '../composables/useControlSchema'
23
38
  import { useFormBuilder } from '../composables/useFormBuilder'
39
+ import {
40
+ formSchemaFieldNames,
41
+ pickExistingRecordKeys,
42
+ recordValuesEqualForKeys,
43
+ } from '../utils/formModelSync'
24
44
  import StepWizard from './StepWizard.vue'
25
- import FormSection from './FormSection.vue'
45
+ import FormSectionRenderer from './internal/FormSectionRenderer.vue'
26
46
  import FormActions from './FormActions.vue'
27
47
 
28
48
  interface Props {
29
- schema: FormSchema
49
+ /** Full form or wizard schema. Takes precedence when `controls` is also provided. */
50
+ schema?: FormSchema
51
+ /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step form generation. */
52
+ model?: ControlModel | ControlModelBinding
53
+ /** Compact control schema used to generate a flat FormSchema. */
54
+ controls?: ControlSchema
55
+ /** Options passed to compact control schema generation, including shared initialValues. */
56
+ controlOptions?: ControlWorkspaceOptions
30
57
  modelValue?: Record<string, unknown>
31
58
  enhancements?: FormEnhancements<Record<string, unknown>>
32
59
  loading?: boolean
33
60
  disabled?: boolean
34
61
  size?: 'sm' | 'md' | 'lg'
35
62
  readonly?: boolean
63
+ /** Show the default or slotted form actions. */
64
+ showActions?: boolean
36
65
  }
37
66
 
38
67
  const props = withDefaults(defineProps<Props>(), {
68
+ model: undefined,
69
+ controlOptions: () => ({}),
39
70
  loading: false,
40
71
  disabled: false,
41
72
  readonly: false,
73
+ showActions: true,
42
74
  })
43
75
 
44
76
  const emit = defineEmits<{
@@ -47,12 +79,68 @@ const emit = defineEmits<{
47
79
  cancel: []
48
80
  }>()
49
81
 
82
+ const resolvedModel = computed<ControlModelBinding | undefined>(() => {
83
+ if (props.model === undefined) return undefined
84
+ return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)
85
+ })
86
+
87
+ const resolvedControls = computed<ControlSchema | undefined>(() =>
88
+ props.controls ?? resolvedModel.value?.controls,
89
+ )
90
+
91
+ const resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>
92
+ mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)
93
+ )
94
+
95
+ const resolvedSchema = computed<FormSchema>(() => {
96
+ if (props.schema) return props.schema
97
+ if (resolvedControls.value) return controlsToFormSchema(resolvedControls.value, resolvedControlOptions.value)
98
+ return { sections: [] }
99
+ })
100
+
101
+ const resolvedModelValue = computed<Record<string, unknown>>(() => ({
102
+ ...(resolvedControls.value ? getControlDefaults(resolvedControls.value) : {}),
103
+ ...(resolvedControlOptions.value.initialValues ?? {}),
104
+ ...(props.modelValue ?? {}),
105
+ }))
106
+
50
107
  const builder = useFormBuilder(
51
- props.schema,
52
- props.modelValue,
108
+ resolvedSchema.value,
109
+ resolvedModelValue.value,
53
110
  props.enhancements,
54
111
  )
55
112
 
113
+ // Sync schema/control changes into the internal builder so generated forms stay coherent.
114
+ watch(
115
+ resolvedSchema,
116
+ (schema) => {
117
+ const fieldNames = formSchemaFieldNames(schema)
118
+ const schemaValues = pickExistingRecordKeys(resolvedModelValue.value, fieldNames)
119
+ const existingValues = pickExistingRecordKeys(
120
+ builder.form.data as Record<string, unknown>,
121
+ fieldNames,
122
+ )
123
+ const nextValues = props.modelValue === undefined
124
+ ? { ...schemaValues, ...existingValues }
125
+ : schemaValues
126
+
127
+ builder.updateSchema(schema, nextValues)
128
+ },
129
+ { deep: true },
130
+ )
131
+
132
+ // Sync external modelValue changes into the internal form without resetting on no-op echo updates.
133
+ watch(
134
+ () => props.modelValue,
135
+ () => {
136
+ const data = resolvedModelValue.value
137
+ const fieldNames = builderFieldNames()
138
+ if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return
139
+ builder.reset(pickExistingRecordKeys(data, fieldNames))
140
+ },
141
+ { deep: true },
142
+ )
143
+
56
144
  // Sync modelValue changes back to parent
57
145
  watch(
58
146
  () => ({ ...builder.form.data }),
@@ -61,12 +149,12 @@ watch(
61
149
  )
62
150
 
63
151
  // Wizard support
64
- const isWizard = computed(() => !!props.schema.steps)
152
+ const isWizard = computed(() => !!resolvedSchema.value.steps)
65
153
  const wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)
66
154
 
67
155
  const wizardSteps = computed<WizardStep[]>(() => {
68
- if (!props.schema.steps) return []
69
- return props.schema.steps.map((step) => ({
156
+ if (!resolvedSchema.value.steps) return []
157
+ return resolvedSchema.value.steps.map((step) => ({
70
158
  id: step.id,
71
159
  label: step.label,
72
160
  description: step.description,
@@ -105,10 +193,19 @@ function handleCancel() {
105
193
  emit('cancel')
106
194
  }
107
195
 
196
+ function builderFieldNames(): string[] {
197
+ return builder.fields.map(field => field.name)
198
+ }
199
+
200
+ function isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {
201
+ return 'controls' in model && 'controlOptions' in model
202
+ }
203
+
108
204
  defineExpose({
109
205
  form: builder.form,
110
206
  validate: builder.validate,
111
207
  reset: builder.reset,
208
+ updateSchema: builder.updateSchema,
112
209
  goNext: builder.goNext,
113
210
  goBack: builder.goBack,
114
211
  goToStep: builder.goToStep,
@@ -119,17 +216,17 @@ defineExpose({
119
216
  <template>
120
217
  <div :class="['mint-form-builder', size ? `mint-form-builder--${size}` : '']">
121
218
  <!-- Wizard mode -->
122
- <template v-if="isWizard && schema.steps">
219
+ <template v-if="isWizard && resolvedSchema.steps">
123
220
  <StepWizard
124
221
  ref="wizardRef"
125
222
  :steps="wizardSteps"
126
223
  :model-value="builder.currentStep.value"
127
224
  @update:model-value="builder.goToStep($event)"
128
225
  >
129
- <template v-for="step in schema.steps" :key="step.id" #[`step-${step.id}`]>
226
+ <template v-for="step in resolvedSchema.steps" :key="step.id" #[`step-${step.id}`]>
130
227
  <div class="mint-form-builder__step">
131
228
  <template v-for="section in step.sections" :key="section.id">
132
- <FormSection
229
+ <FormSectionRenderer
133
230
  v-if="builder.isSectionVisible(section.id)"
134
231
  :section="section"
135
232
  :builder="(builder as UseFormBuilderReturn<Record<string, unknown>>)"
@@ -145,12 +242,12 @@ defineExpose({
145
242
  <template #[`section:${section.id}:after`]="slotProps">
146
243
  <slot :name="`section:${section.id}:after`" v-bind="slotProps" />
147
244
  </template>
148
- </FormSection>
245
+ </FormSectionRenderer>
149
246
  </template>
150
247
  </div>
151
248
  </template>
152
249
 
153
- <template #navigation="{ isFirst, isLast }">
250
+ <template v-if="showActions" #navigation="{ isFirst, isLast }">
154
251
  <slot name="actions" :form="builder.form" :builder="builder">
155
252
  <FormActions
156
253
  is-wizard
@@ -159,9 +256,9 @@ defineExpose({
159
256
  :can-proceed="builder.isCurrentStepValid.value"
160
257
  :loading="loading || builder.form.isSubmitting.value"
161
258
  :disabled="disabled"
162
- :submit-label="schema.submitLabel ?? 'Submit'"
163
- :cancel-label="schema.cancelLabel ?? 'Cancel'"
164
- :show-cancel="schema.showCancel ?? false"
259
+ :submit-label="resolvedSchema.submitLabel ?? 'Submit'"
260
+ :cancel-label="resolvedSchema.cancelLabel ?? 'Cancel'"
261
+ :show-cancel="resolvedSchema.showCancel ?? false"
165
262
  @next="handleNext"
166
263
  @back="builder.goBack"
167
264
  @submit="handleSubmit"
@@ -173,9 +270,9 @@ defineExpose({
173
270
  </template>
174
271
 
175
272
  <!-- Flat mode -->
176
- <template v-else-if="schema.sections">
177
- <template v-for="section in schema.sections" :key="section.id">
178
- <FormSection
273
+ <template v-else-if="resolvedSchema.sections">
274
+ <template v-for="section in resolvedSchema.sections" :key="section.id">
275
+ <FormSectionRenderer
179
276
  v-if="builder.isSectionVisible(section.id)"
180
277
  :section="section"
181
278
  :builder="(builder as UseFormBuilderReturn<Record<string, unknown>>)"
@@ -191,16 +288,16 @@ defineExpose({
191
288
  <template #[`section:${section.id}:after`]="slotProps">
192
289
  <slot :name="`section:${section.id}:after`" v-bind="slotProps" />
193
290
  </template>
194
- </FormSection>
291
+ </FormSectionRenderer>
195
292
  </template>
196
293
 
197
- <slot name="actions" :form="builder.form" :builder="builder">
294
+ <slot v-if="showActions" name="actions" :form="builder.form" :builder="builder">
198
295
  <FormActions
199
296
  :loading="loading || builder.form.isSubmitting.value"
200
297
  :disabled="disabled"
201
- :submit-label="schema.submitLabel ?? 'Submit'"
202
- :cancel-label="schema.cancelLabel ?? 'Cancel'"
203
- :show-cancel="schema.showCancel ?? false"
298
+ :submit-label="resolvedSchema.submitLabel ?? 'Submit'"
299
+ :cancel-label="resolvedSchema.cancelLabel ?? 'Cancel'"
300
+ :show-cancel="resolvedSchema.showCancel ?? false"
204
301
  @submit="handleSubmit"
205
302
  @cancel="handleCancel"
206
303
  />
@@ -1,17 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Renders a single form field inside a FormField wrapper.
3
+ * @deprecated Use FormBuilder with schema/controls and field slots instead.
4
4
  *
5
- * Resolves the SDK component for the field type from the registry, shows
6
- * validation errors only after the field has been touched, and handles the
7
- * FileUploader's event-based upload pattern via `handleUpload`. Consumers can
8
- * override rendering via the `field:<name>` slot.
5
+ * Compatibility wrapper around the internal FormBuilder field renderer.
9
6
  */
10
- import { computed } from 'vue'
11
7
  import type { FormFieldSchema } from '../types/form-builder'
12
8
  import type { UseFormReturn } from '../composables/useForm'
13
- import { getFieldRegistryEntry } from '../composables/formBuilderRegistry'
14
- import FormField from './FormField.vue'
9
+ import FormFieldRendererInternal from './internal/FormFieldRendererInternal.vue'
15
10
 
16
11
  interface Props {
17
12
  field: FormFieldSchema
@@ -21,38 +16,20 @@ interface Props {
21
16
 
22
17
  const props = defineProps<Props>()
23
18
 
24
- const entry = computed(() => getFieldRegistryEntry(props.field.type))
25
-
26
- const errorMessage = computed(() => {
27
- const name = props.field.name
28
- return props.form.touched[name] ? props.form.errors[name] : null
29
- })
30
-
31
- function handleUpload(files: File[]) {
32
- props.form.setFieldValue(props.field.name, files)
33
- }
19
+ defineSlots<{
20
+ '`field:${field.name}`'?: (props: Record<string, unknown>) => unknown
21
+ [name: string]: ((props: Record<string, unknown>) => unknown) | undefined
22
+ }>()
34
23
  </script>
35
24
 
36
25
  <template>
37
- <FormField
38
- :label="field.label"
39
- :error="errorMessage ?? undefined"
40
- :hint="field.hint"
41
- :required="!!field.validation?.required"
42
- :html-for="field.name"
26
+ <FormFieldRendererInternal
27
+ :field="props.field"
28
+ :resolved-props="props.resolvedProps"
29
+ :form="props.form"
43
30
  >
44
- <slot :name="`field:${field.name}`" :field="field" :form="form" :field-props="form.getFieldProps(field.name)">
45
- <component
46
- :is="entry.component"
47
- v-if="entry.vModel"
48
- v-bind="resolvedProps"
49
- />
50
- <component
51
- :is="entry.component"
52
- v-else
53
- v-bind="resolvedProps"
54
- @upload="handleUpload"
55
- />
56
- </slot>
57
- </FormField>
31
+ <template #[`field:${props.field.name}`]="slotProps">
32
+ <slot :name="`field:${props.field.name}`" v-bind="slotProps" />
33
+ </template>
34
+ </FormFieldRendererInternal>
58
35
  </template>
@@ -1,18 +1,11 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Renders a single `FormSectionSchema` inside a FormBuilder.
3
+ * @deprecated Use FormBuilder with schema/controls and section slots instead.
4
4
  *
5
- * Filters out hidden fields before rendering, applies the section's column
6
- * grid and per-field `colSpan`, and switches between a collapsible
7
- * (`CollapsibleCard`) and a static layout based on `section.collapsible`.
8
- * The entire section is hidden when all its fields are hidden. Consumers can
9
- * replace the section body via `section:<id>` or inject content after it via
10
- * `section:<id>:after`, and override individual fields via `field:<name>`.
5
+ * Compatibility wrapper around the internal FormBuilder section renderer.
11
6
  */
12
- import { computed } from 'vue'
13
7
  import type { FormSectionSchema, UseFormBuilderReturn } from '../types/form-builder'
14
- import CollapsibleCard from './CollapsibleCard.vue'
15
- import FormFieldRenderer from './FormFieldRenderer.vue'
8
+ import FormSectionRenderer from './internal/FormSectionRenderer.vue'
16
9
 
17
10
  interface Props {
18
11
  section: FormSectionSchema
@@ -21,70 +14,24 @@ interface Props {
21
14
 
22
15
  const props = defineProps<Props>()
23
16
 
24
- const visibleFields = computed(() =>
25
- props.section.fields.filter((f) => props.builder.isFieldVisible(f.name)),
26
- )
27
-
28
- const gridStyle = computed(() => ({
29
- gridTemplateColumns: `repeat(${props.section.columns ?? 1}, 1fr)`,
30
- }))
17
+ defineSlots<{
18
+ '`section:${section.id}`'?: (props: Record<string, unknown>) => unknown
19
+ '`field:${field.name}`'?: (props: Record<string, unknown>) => unknown
20
+ '`section:${section.id}:after`'?: (props: Record<string, unknown>) => unknown
21
+ [name: string]: ((props: Record<string, unknown>) => unknown) | undefined
22
+ }>()
31
23
  </script>
32
24
 
33
25
  <template>
34
- <div v-if="visibleFields.length > 0" class="mint-form-section">
35
- <slot :name="`section:${section.id}`" :section="section" :form="builder.form">
36
- <!-- Collapsible variant -->
37
- <CollapsibleCard
38
- v-if="section.collapsible"
39
- :title="section.title"
40
- :subtitle="section.description"
41
- :default-open="section.defaultOpen ?? true"
42
- >
43
- <div class="mint-form-section__grid" :style="gridStyle">
44
- <div
45
- v-for="field in visibleFields"
46
- :key="field.name"
47
- :style="field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined"
48
- >
49
- <slot :name="`field:${field.name}`" :field="field" :form="builder.form" :field-props="builder.form.getFieldProps(field.name)">
50
- <FormFieldRenderer
51
- :field="field"
52
- :resolved-props="builder.getResolvedFieldProps(field)"
53
- :form="builder.form"
54
- />
55
- </slot>
56
- </div>
57
- </div>
58
- </CollapsibleCard>
59
-
60
- <!-- Static variant -->
61
- <div v-else class="mint-form-section--static">
62
- <div v-if="section.title || section.description" class="mint-form-section__header">
63
- <h3 v-if="section.title" class="mint-form-section__title">{{ section.title }}</h3>
64
- <p v-if="section.description" class="mint-form-section__description">{{ section.description }}</p>
65
- </div>
66
- <div class="mint-form-section__grid" :style="gridStyle">
67
- <div
68
- v-for="field in visibleFields"
69
- :key="field.name"
70
- :style="field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined"
71
- >
72
- <slot :name="`field:${field.name}`" :field="field" :form="builder.form" :field-props="builder.form.getFieldProps(field.name)">
73
- <FormFieldRenderer
74
- :field="field"
75
- :resolved-props="builder.getResolvedFieldProps(field)"
76
- :form="builder.form"
77
- />
78
- </slot>
79
- </div>
80
- </div>
81
- </div>
82
- </slot>
83
-
84
- <slot :name="`section:${section.id}:after`" :form="builder.form" />
85
- </div>
26
+ <FormSectionRenderer :section="props.section" :builder="props.builder">
27
+ <template #[`section:${props.section.id}`]="slotProps">
28
+ <slot :name="`section:${props.section.id}`" v-bind="slotProps" />
29
+ </template>
30
+ <template v-for="field in props.section.fields" :key="field.name" #[`field:${field.name}`]="slotProps">
31
+ <slot :name="`field:${field.name}`" v-bind="slotProps" />
32
+ </template>
33
+ <template #[`section:${props.section.id}:after`]="slotProps">
34
+ <slot :name="`section:${props.section.id}:after`" v-bind="slotProps" />
35
+ </template>
36
+ </FormSectionRenderer>
86
37
  </template>
87
-
88
- <style>
89
- @import '../styles/components/form-builder.css';
90
- </style>
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Drag-and-drop two-zone assigner for splitting sample groups into control and treatment arms. */
3
- import { computed, ref } from 'vue'
3
+ import { ref } from 'vue'
4
4
  import type { GroupItem } from '../types'
5
+ import { useGroupAssignment, type GroupAssignmentState, type GroupAssignmentZone } from '../composables/useGroupAssignment'
5
6
 
6
7
  interface Props {
7
8
  groups: GroupItem[]
@@ -30,42 +31,15 @@ const emit = defineEmits<{
30
31
  const dragOverZone = ref<'zone1' | 'zone2' | null>(null)
31
32
  const draggingGroup = ref<string | null>(null)
32
33
 
33
- const unassignedGroups = computed(() =>
34
- props.groups.filter(
35
- g => !props.group1.includes(g.name) && !props.group2.includes(g.name)
36
- )
37
- )
38
-
39
- const zone1Groups = computed(() =>
40
- props.groups.filter(g => props.group1.includes(g.name))
41
- )
42
-
43
- const zone2Groups = computed(() =>
44
- props.groups.filter(g => props.group2.includes(g.name))
45
- )
46
-
47
- const zone1Count = computed(() =>
48
- zone1Groups.value.reduce((sum, g) => sum + g.count, 0)
49
- )
50
-
51
- const zone2Count = computed(() =>
52
- zone2Groups.value.reduce((sum, g) => sum + g.count, 0)
53
- )
54
-
55
- const isValid = computed(() =>
56
- props.group1.length >= props.minPerGroup &&
57
- props.group2.length >= props.minPerGroup
58
- )
59
-
60
- const validationMessage = computed(() => {
61
- if (isValid.value) return null
62
- const missing1 = Math.max(0, props.minPerGroup - props.group1.length)
63
- const missing2 = Math.max(0, props.minPerGroup - props.group2.length)
64
- const parts: string[] = []
65
- if (missing1 > 0) parts.push(`${missing1} more to ${props.label1}`)
66
- if (missing2 > 0) parts.push(`${missing2} more to ${props.label2}`)
67
- return `Add ${parts.join(' and ')}`
34
+ const assignment = useGroupAssignment({
35
+ groups: () => props.groups,
36
+ group1: () => props.group1,
37
+ group2: () => props.group2,
38
+ label1: () => props.label1,
39
+ label2: () => props.label2,
40
+ minPerGroup: () => props.minPerGroup,
68
41
  })
42
+ const { unassignedGroups, zone1Groups, zone2Groups, zone1Count, zone2Count, validationMessage } = assignment
69
43
 
70
44
  function handleDragStart(event: DragEvent, groupName: string) {
71
45
  draggingGroup.value = groupName
@@ -78,7 +52,7 @@ function handleDragEnd() {
78
52
  dragOverZone.value = null
79
53
  }
80
54
 
81
- function handleDragOver(event: DragEvent, zone: 'zone1' | 'zone2') {
55
+ function handleDragOver(event: DragEvent, zone: GroupAssignmentZone) {
82
56
  event.preventDefault()
83
57
  event.dataTransfer!.dropEffect = 'move'
84
58
  dragOverZone.value = zone
@@ -88,7 +62,7 @@ function handleDragLeave() {
88
62
  dragOverZone.value = null
89
63
  }
90
64
 
91
- function handleDrop(event: DragEvent, zone: 'zone1' | 'zone2') {
65
+ function handleDrop(event: DragEvent, zone: GroupAssignmentZone) {
92
66
  event.preventDefault()
93
67
  dragOverZone.value = null
94
68
 
@@ -98,32 +72,26 @@ function handleDrop(event: DragEvent, zone: 'zone1' | 'zone2') {
98
72
  assignToZone(groupName, zone)
99
73
  }
100
74
 
101
- function assignToZone(groupName: string, zone: 'zone1' | 'zone2') {
102
- // Remove from both zones first
103
- const newGroup1 = props.group1.filter(n => n !== groupName)
104
- const newGroup2 = props.group2.filter(n => n !== groupName)
105
-
106
- // Add to target zone
107
- if (zone === 'zone1') {
108
- emit('update:group1', [...newGroup1, groupName])
109
- emit('update:group2', newGroup2)
110
- } else {
111
- emit('update:group1', newGroup1)
112
- emit('update:group2', [...newGroup2, groupName])
113
- }
75
+ function assignToZone(groupName: string, zone: GroupAssignmentZone) {
76
+ emitAssignment(assignment.assignToZone(groupName, zone))
114
77
  }
115
78
 
116
- function removeFromZone(groupName: string, zone: 'zone1' | 'zone2') {
79
+ function removeFromZone(groupName: string, zone: GroupAssignmentZone) {
80
+ const state = assignment.removeFromZone(groupName, zone)
117
81
  if (zone === 'zone1') {
118
- emit('update:group1', props.group1.filter(n => n !== groupName))
82
+ emit('update:group1', state.group1)
119
83
  } else {
120
- emit('update:group2', props.group2.filter(n => n !== groupName))
84
+ emit('update:group2', state.group2)
121
85
  }
122
86
  }
123
87
 
124
88
  function clearAll() {
125
- emit('update:group1', [])
126
- emit('update:group2', [])
89
+ emitAssignment(assignment.clearAll())
90
+ }
91
+
92
+ function emitAssignment(state: GroupAssignmentState) {
93
+ emit('update:group1', state.group1)
94
+ emit('update:group2', state.group2)
127
95
  }
128
96
  </script>
129
97
 
@@ -12,8 +12,8 @@ const isOpen = ref(false)
12
12
  </script>
13
13
 
14
14
  <template>
15
- <Story title="Lab/GroupingModal">
16
- <Variant title="Playground">
15
+ <Story title="Lab/Deprecated/GroupingModal">
16
+ <Variant title="Compatibility Preview">
17
17
  <div style="padding: 2rem;">
18
18
  <button
19
19
  type="button"
@@ -27,7 +27,7 @@ const isOpen = ref(false)
27
27
  "
28
28
  @click="isOpen = true"
29
29
  >
30
- Open Grouping Modal
30
+ Open Legacy Grouping Modal
31
31
  </button>
32
32
  <GroupingModal
33
33
  :open="isOpen"