@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.0-beta.4

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 (427) hide show
  1. package/README.md +225 -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/ComponentBindingRenderer.test.d.ts +1 -0
  19. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  20. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  21. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  22. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  23. package/dist/__tests__/components/DoseDesignWorkspaceView.test.d.ts +1 -0
  24. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  25. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  26. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  27. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  28. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  29. package/dist/__tests__/components/PluginWorkspaceView.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/SettingsModal.test.d.ts +1 -0
  36. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  37. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  38. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  39. package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
  40. package/dist/__tests__/composables/useApi.test.d.ts +1 -0
  41. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  42. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  43. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  44. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  45. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  46. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  47. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  48. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  49. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  50. package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
  51. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  52. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  53. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  54. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  55. package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
  56. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  57. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  58. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  59. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  60. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  61. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  62. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  63. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  64. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  65. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  66. package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
  67. package/dist/auth-QQj2kkze.js.map +1 -0
  68. package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
  69. package/dist/components/AppContainer.vue.d.ts +1 -1
  70. package/dist/components/AppLayout.vue.d.ts +20 -1
  71. package/dist/components/AppSidebar.vue.d.ts +111 -6
  72. package/dist/components/AppTopBar.vue.d.ts +35 -22
  73. package/dist/components/BaseButton.vue.d.ts +1 -1
  74. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  75. package/dist/components/BaseInput.vue.d.ts +2 -2
  76. package/dist/components/BasePill.vue.d.ts +2 -2
  77. package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
  78. package/dist/components/BaseSelect.vue.d.ts +3 -3
  79. package/dist/components/BaseTabs.vue.d.ts +2 -2
  80. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  81. package/dist/components/BaseToggle.vue.d.ts +1 -1
  82. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +119 -0
  83. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +93 -0
  84. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +87 -0
  85. package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
  86. package/dist/components/Breadcrumb.vue.d.ts +2 -2
  87. package/dist/components/Calendar.vue.d.ts +1 -1
  88. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  89. package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
  90. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  91. package/dist/components/ConfirmDialog.vue.d.ts +2 -2
  92. package/dist/components/ControlWorkspaceView.vue.d.ts +147 -0
  93. package/dist/components/DatePicker.vue.d.ts +1 -1
  94. package/dist/components/DateTimePicker.vue.d.ts +3 -3
  95. package/dist/components/Divider.vue.d.ts +1 -1
  96. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
  97. package/dist/components/DropdownButton.vue.d.ts +3 -3
  98. package/dist/components/EmptyState.vue.d.ts +1 -2
  99. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  100. package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
  101. package/dist/components/FileUploader.vue.d.ts +1 -1
  102. package/dist/components/FitPanel.vue.d.ts +1 -1
  103. package/dist/components/FormActions.vue.d.ts +4 -4
  104. package/dist/components/FormBuilder.vue.d.ts +31 -17
  105. package/dist/components/FormulaInput.vue.d.ts +2 -2
  106. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  107. package/dist/components/MultiSelect.vue.d.ts +3 -3
  108. package/dist/components/NumberInput.vue.d.ts +1 -1
  109. package/dist/components/PlateMapEditor.vue.d.ts +1 -1
  110. package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
  111. package/dist/components/ProgressBar.vue.d.ts +1 -1
  112. package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
  113. package/dist/components/RackEditor.vue.d.ts +2 -2
  114. package/dist/components/SampleLegend.vue.d.ts +2 -2
  115. package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
  116. package/dist/components/SegmentedControl.vue.d.ts +2 -2
  117. package/dist/components/SequenceInput.vue.d.ts +3 -3
  118. package/dist/components/SettingsModal.vue.d.ts +14 -6
  119. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  120. package/dist/components/TagsInput.vue.d.ts +3 -2
  121. package/dist/components/TimePicker.vue.d.ts +3 -3
  122. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  123. package/dist/components/UnitInput.vue.d.ts +2 -2
  124. package/dist/components/WellPlate.vue.d.ts +6 -6
  125. package/dist/components/index.d.ts +9 -8
  126. package/dist/components/index.js +3 -3
  127. package/dist/components/{SettingsButton.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +11 -9
  128. package/dist/components/{AppPageSelector.vue.d.ts → internal/AppPageSelectorInternal.vue.d.ts} +3 -6
  129. package/dist/components/{AppPillNav.vue.d.ts → internal/AppPillNavInternal.vue.d.ts} +4 -2
  130. package/dist/components/internal/CalendarGridPanelInternal.vue.d.ts +25 -0
  131. package/dist/components/{FormFieldRenderer.vue.d.ts → internal/FormFieldRendererInternal.vue.d.ts} +2 -2
  132. package/dist/components/{FormSection.vue.d.ts → internal/FormSectionRenderer.vue.d.ts} +7 -7
  133. package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
  134. package/dist/{components-_XqPEhP9.js → components-BkGF4B4y.js} +9760 -8471
  135. package/dist/components-BkGF4B4y.js.map +1 -0
  136. package/dist/composables/experiment-utils.d.ts +8 -0
  137. package/dist/composables/index.d.ts +22 -5
  138. package/dist/composables/index.js +4 -3
  139. package/dist/composables/platformContextHelpers.d.ts +14 -0
  140. package/dist/composables/useAppExperiment.d.ts +31 -2
  141. package/dist/composables/useBioTemplateComponents.d.ts +22 -0
  142. package/dist/composables/useBioTemplateControls.d.ts +6 -0
  143. package/dist/composables/useBioTemplatePackWorkspace.d.ts +46 -0
  144. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +75 -0
  145. package/dist/composables/useBioTemplateWorkspace.d.ts +51 -0
  146. package/dist/composables/useCalendarGrid.d.ts +26 -0
  147. package/dist/composables/useControlSchema.d.ts +343 -0
  148. package/dist/composables/useDebouncedWatch.d.ts +20 -0
  149. package/dist/composables/useDropdownState.d.ts +19 -0
  150. package/dist/composables/useEventListener.d.ts +13 -0
  151. package/dist/composables/useExpansionSet.d.ts +21 -0
  152. package/dist/composables/useExperimentData.d.ts +10 -0
  153. package/dist/composables/useExperimentSave.d.ts +31 -2
  154. package/dist/composables/useExperimentSelector.d.ts +20 -0
  155. package/dist/composables/useForm.d.ts +2 -0
  156. package/dist/composables/useGroupAssignment.d.ts +31 -0
  157. package/dist/composables/useListSelection.d.ts +35 -0
  158. package/dist/composables/usePlatformContext.d.ts +21 -3
  159. package/dist/composables/usePluginClient.d.ts +112 -0
  160. package/dist/composables/usePluginConfig.d.ts +12 -0
  161. package/dist/composables/useRequestSyncState.d.ts +34 -0
  162. package/dist/composables/useSampleGroups.d.ts +32 -0
  163. package/dist/composables/useSelectionLimit.d.ts +17 -0
  164. package/dist/composables/useSortedItems.d.ts +32 -0
  165. package/dist/composables/useTemplateCollection.d.ts +58 -0
  166. package/dist/composables/useTextSearch.d.ts +18 -0
  167. package/dist/composables/useTimeUtils.d.ts +8 -0
  168. package/dist/{composables-tiZqLu1M.js → composables-CHsME9H1.js} +240 -146
  169. package/dist/composables-CHsME9H1.js.map +1 -0
  170. package/dist/index.d.ts +6 -4
  171. package/dist/index.js +6 -5
  172. package/dist/install.d.ts +7 -2
  173. package/dist/install.js +2 -2
  174. package/dist/install.js.map +1 -1
  175. package/dist/stores/index.js +1 -1
  176. package/dist/stores/settings.d.ts +4 -1
  177. package/dist/styles.css +4746 -5514
  178. package/dist/templates/adapters.d.ts +43 -0
  179. package/dist/templates/builders.d.ts +63 -0
  180. package/dist/templates/catalog.d.ts +188 -0
  181. package/dist/templates/componentBindings.d.ts +71 -0
  182. package/dist/templates/controlSchemas.d.ts +25 -0
  183. package/dist/templates/index.d.ts +15 -0
  184. package/dist/templates/index.js +2 -0
  185. package/dist/templates/lookup.d.ts +4 -0
  186. package/dist/templates/packs.d.ts +18 -0
  187. package/dist/templates/presets.d.ts +90 -0
  188. package/dist/templates/types.d.ts +531 -0
  189. package/dist/templates-B5jmTWuk.js +9388 -0
  190. package/dist/templates-B5jmTWuk.js.map +1 -0
  191. package/dist/types/components.d.ts +26 -23
  192. package/dist/types/form-builder.d.ts +6 -8
  193. package/dist/types/index.d.ts +2 -2
  194. package/dist/types/platform.d.ts +7 -1
  195. package/dist/useScheduleDrag-BgzpQT53.js +4414 -0
  196. package/dist/useScheduleDrag-BgzpQT53.js.map +1 -0
  197. package/dist/utils/formModelSync.d.ts +5 -0
  198. package/dist/utils/items.d.ts +8 -0
  199. package/dist/utils/options.d.ts +6 -0
  200. package/dist/utils/pluginIcon.d.ts +9 -0
  201. package/package.json +7 -2
  202. package/src/__tests__/components/ActionItem.test.ts +99 -0
  203. package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
  204. package/src/__tests__/components/AppLayout.test.ts +44 -0
  205. package/src/__tests__/components/AppPageSelector.test.ts +134 -0
  206. package/src/__tests__/components/AppPillNav.test.ts +125 -0
  207. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  208. package/src/__tests__/components/AppSidebar.test.ts +496 -0
  209. package/src/__tests__/components/AppToastContainer.test.ts +37 -0
  210. package/src/__tests__/components/AppTopBar.test.ts +455 -9
  211. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  212. package/src/__tests__/components/BaseSelect.test.ts +21 -0
  213. package/src/__tests__/components/BaseTabs.test.ts +25 -0
  214. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  215. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +159 -0
  216. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +175 -0
  217. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +306 -0
  218. package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
  219. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  220. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  221. package/src/__tests__/components/ComponentBindingRenderer.test.ts +161 -0
  222. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  223. package/src/__tests__/components/ControlWorkspaceView.test.ts +1102 -0
  224. package/src/__tests__/components/DataFrame.test.ts +11 -0
  225. package/src/__tests__/components/DatePicker.test.ts +45 -0
  226. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  227. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
  228. package/src/__tests__/components/DropdownButton.test.ts +23 -0
  229. package/src/__tests__/components/EmptyState.test.ts +23 -0
  230. package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
  231. package/src/__tests__/components/FormBuilder.test.ts +296 -0
  232. package/src/__tests__/components/GroupAssigner.test.ts +30 -0
  233. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  234. package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
  235. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  236. package/src/__tests__/components/ReagentList.test.ts +82 -0
  237. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  238. package/src/__tests__/components/SampleSelector.test.ts +60 -0
  239. package/src/__tests__/components/SegmentedControl.test.ts +24 -0
  240. package/src/__tests__/components/SettingsModal.test.ts +296 -0
  241. package/src/__tests__/components/TagsInput.test.ts +75 -0
  242. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  243. package/src/__tests__/components/TimePicker.test.ts +38 -0
  244. package/src/__tests__/composables/experiment-utils.test.ts +30 -0
  245. package/src/__tests__/composables/useApi.test.ts +30 -0
  246. package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
  247. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +125 -0
  248. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  249. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +104 -0
  250. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  251. package/src/__tests__/composables/useControlSchema.test.ts +1033 -0
  252. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  253. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  254. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  255. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  256. package/src/__tests__/composables/useExperimentData.test.ts +4 -0
  257. package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
  258. package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
  259. package/src/__tests__/composables/useForm.test.ts +58 -0
  260. package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
  261. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  262. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  263. package/src/__tests__/composables/usePluginClient.test.ts +541 -0
  264. package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
  265. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  266. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  267. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  268. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  269. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  270. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  271. package/src/__tests__/composables/useTheme.test.ts +91 -0
  272. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  273. package/src/__tests__/docs/frontendDocsCatalog.test.ts +324 -0
  274. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  275. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  276. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  277. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  278. package/src/__tests__/templates/templates.test.ts +1055 -0
  279. package/src/components/AppAvatarMenu.vue +15 -69
  280. package/src/components/AppLayout.story.vue +64 -25
  281. package/src/components/AppLayout.vue +83 -2
  282. package/src/components/AppPluginSwitcher.vue +41 -145
  283. package/src/components/AppSidebar.story.vue +203 -1
  284. package/src/components/AppSidebar.vue +320 -25
  285. package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
  286. package/src/components/{ToastNotification.vue → AppToastContainer.vue} +1 -1
  287. package/src/components/AppTopBar.story.vue +7 -33
  288. package/src/components/AppTopBar.vue +104 -300
  289. package/src/components/BaseModal.vue +3 -5
  290. package/src/components/BaseRadioGroup.vue +7 -3
  291. package/src/components/BaseSelect.vue +11 -7
  292. package/src/components/BaseTabs.vue +6 -4
  293. package/src/components/BatchProgressList.vue +5 -8
  294. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  295. package/src/components/BioTemplateExperimentWorkspaceView.vue +343 -0
  296. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  297. package/src/components/BioTemplatePackWorkspaceView.vue +177 -0
  298. package/src/components/BioTemplatePresetWorkspaceView.story.vue +163 -0
  299. package/src/components/BioTemplatePresetWorkspaceView.vue +401 -0
  300. package/src/components/BioTemplateRenderer.story.vue +57 -0
  301. package/src/components/BioTemplateRenderer.vue +57 -0
  302. package/src/components/Breadcrumb.vue +14 -8
  303. package/src/components/ComponentBindingRenderer.story.vue +57 -0
  304. package/src/components/ComponentBindingRenderer.vue +308 -0
  305. package/src/components/ConcentrationInput.vue +27 -64
  306. package/src/components/ControlWorkspaceView.story.vue +347 -0
  307. package/src/components/ControlWorkspaceView.vue +378 -0
  308. package/src/components/DataFrame.vue +34 -50
  309. package/src/components/DatePicker.vue +59 -192
  310. package/src/components/DateTimePicker.vue +50 -171
  311. package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
  312. package/src/components/DoseDesignWorkspaceView.vue +255 -0
  313. package/src/components/DropdownButton.vue +14 -32
  314. package/src/components/EmptyState.vue +4 -2
  315. package/src/components/ExperimentPopover.vue +7 -28
  316. package/src/components/ExperimentSelectorModal.vue +6 -5
  317. package/src/components/FormBuilder.story.vue +190 -0
  318. package/src/components/FormBuilder.vue +124 -27
  319. package/src/components/GroupAssigner.vue +24 -56
  320. package/src/components/MultiSelect.vue +17 -12
  321. package/src/components/PlateMapEditor.vue +3 -8
  322. package/src/components/PluginIcon.vue +2 -22
  323. package/src/components/PluginWorkspaceView.story.vue +334 -0
  324. package/src/components/PluginWorkspaceView.vue +708 -0
  325. package/src/components/ProtocolStepEditor.vue +13 -22
  326. package/src/components/ReagentList.vue +25 -33
  327. package/src/components/SampleHierarchyTree.vue +12 -23
  328. package/src/components/SampleSelector.vue +42 -122
  329. package/src/components/SegmentedControl.vue +7 -3
  330. package/src/components/SettingsModal.story.vue +88 -1
  331. package/src/components/SettingsModal.vue +120 -29
  332. package/src/components/TagsInput.vue +29 -14
  333. package/src/components/ThemeToggle.vue +9 -7
  334. package/src/components/TimePicker.vue +19 -41
  335. package/src/components/Tooltip.vue +7 -12
  336. package/src/components/WellPlate.vue +6 -12
  337. package/src/components/index.ts +9 -8
  338. package/src/components/internal/ActionItemInternal.vue +82 -0
  339. package/src/components/internal/AppPageSelectorInternal.vue +128 -0
  340. package/src/components/internal/AppPillNavInternal.vue +194 -0
  341. package/src/components/internal/CalendarGridPanelInternal.vue +120 -0
  342. package/src/components/{FormFieldRenderer.vue → internal/FormFieldRendererInternal.vue} +4 -12
  343. package/src/components/{FormSection.vue → internal/FormSectionRenderer.vue} +6 -18
  344. package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +5 -10
  345. package/src/composables/experiment-utils.ts +26 -0
  346. package/src/composables/index.ts +229 -3
  347. package/src/composables/platformContextHelpers.ts +74 -0
  348. package/src/composables/useApi.ts +9 -2
  349. package/src/composables/useAppExperiment.ts +85 -13
  350. package/src/composables/useBioTemplateComponents.ts +105 -0
  351. package/src/composables/useBioTemplateControls.ts +41 -0
  352. package/src/composables/useBioTemplatePackWorkspace.ts +185 -0
  353. package/src/composables/useBioTemplatePresetWorkspace.ts +326 -0
  354. package/src/composables/useBioTemplateWorkspace.ts +141 -0
  355. package/src/composables/useCalendarGrid.ts +140 -0
  356. package/src/composables/useControlSchema.ts +1362 -0
  357. package/src/composables/useDebouncedWatch.ts +119 -0
  358. package/src/composables/useDropdownState.ts +83 -0
  359. package/src/composables/useEventListener.ts +111 -0
  360. package/src/composables/useExpansionSet.ts +117 -0
  361. package/src/composables/useExperimentData.ts +20 -11
  362. package/src/composables/useExperimentSave.ts +202 -50
  363. package/src/composables/useExperimentSelector.ts +86 -72
  364. package/src/composables/useForm.ts +49 -4
  365. package/src/composables/useFormBuilder.ts +93 -42
  366. package/src/composables/useGroupAssignment.ts +148 -0
  367. package/src/composables/useListSelection.ts +158 -0
  368. package/src/composables/usePluginClient.ts +466 -0
  369. package/src/composables/usePluginConfig.ts +34 -13
  370. package/src/composables/useRequestSyncState.ts +126 -0
  371. package/src/composables/useSampleGroups.ts +126 -0
  372. package/src/composables/useSelectionLimit.ts +57 -0
  373. package/src/composables/useSortedItems.ts +118 -0
  374. package/src/composables/useTemplateCollection.ts +229 -0
  375. package/src/composables/useTextSearch.ts +60 -0
  376. package/src/composables/useTheme.ts +2 -28
  377. package/src/composables/useTimeUtils.ts +26 -2
  378. package/src/composables/useWellPlateEditor.ts +13 -9
  379. package/src/index.ts +11 -348
  380. package/src/install.ts +11 -4
  381. package/src/stores/settings.ts +13 -9
  382. package/src/styles/components/app-layout.css +82 -0
  383. package/src/styles/components/app-page-selector.css +23 -0
  384. package/src/styles/components/app-pill-nav.css +77 -0
  385. package/src/styles/components/app-sidebar.css +119 -0
  386. package/src/styles/components/app-top-bar.css +0 -201
  387. package/src/styles/components/concentration-input.css +3 -142
  388. package/src/styles/components/empty-state.css +0 -16
  389. package/src/styles/components/theme-toggle.css +3 -66
  390. package/src/styles/index.css +0 -2
  391. package/src/templates/adapters.ts +785 -0
  392. package/src/templates/builders.ts +2149 -0
  393. package/src/templates/catalog.ts +245 -0
  394. package/src/templates/componentBindings.ts +653 -0
  395. package/src/templates/controlSchemas.ts +718 -0
  396. package/src/templates/index.ts +318 -0
  397. package/src/templates/lookup.ts +18 -0
  398. package/src/templates/packs.ts +156 -0
  399. package/src/templates/presets.ts +146 -0
  400. package/src/templates/types.ts +668 -0
  401. package/src/types/components.ts +39 -27
  402. package/src/types/form-builder.ts +7 -2
  403. package/src/types/index.ts +13 -3
  404. package/src/types/platform.ts +7 -1
  405. package/src/utils/formModelSync.ts +52 -0
  406. package/src/utils/items.ts +28 -0
  407. package/src/utils/options.ts +23 -0
  408. package/src/utils/pluginIcon.ts +30 -0
  409. package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
  410. package/dist/auth-DsI0rQ7_.js.map +0 -1
  411. package/dist/components/GroupingModal.vue.d.ts +0 -12
  412. package/dist/components-_XqPEhP9.js.map +0 -1
  413. package/dist/composables/usePluginApi.d.ts +0 -29
  414. package/dist/composables-tiZqLu1M.js.map +0 -1
  415. package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
  416. package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
  417. package/src/__tests__/composables/usePluginApi.test.ts +0 -81
  418. package/src/components/AppPageSelector.vue +0 -159
  419. package/src/components/AppPillNav.vue +0 -66
  420. package/src/components/GroupingModal.story.vue +0 -52
  421. package/src/components/GroupingModal.vue +0 -422
  422. package/src/components/SettingsButton.story.vue +0 -58
  423. package/src/components/SettingsButton.vue +0 -76
  424. package/src/composables/usePluginApi.ts +0 -39
  425. package/src/styles/components/grouping-modal.css +0 -323
  426. package/src/styles/components/settings-button.css +0 -94
  427. /package/dist/components/{ToastNotification.vue.d.ts → AppToastContainer.vue.d.ts} +0 -0
@@ -0,0 +1,77 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import DoseDesignWorkspaceView from './DoseDesignWorkspaceView.vue'
4
+ import type { DoseDesignControlModelOptions } from '../composables/useControlSchema'
5
+
6
+ type SidebarVariant = 'analysis' | 'default'
7
+
8
+ const initState = () => ({
9
+ title: 'Dose Design Workspace',
10
+ includeMolecularWeight: true,
11
+ plateFormat: 96,
12
+ selectedWells: ['A1', 'A2', 'B1', 'B2'],
13
+ sidebarTitle: 'Dose Controls',
14
+ sidebarSubtitle: 'Plate setup',
15
+ sidebarVariant: 'analysis' as SidebarVariant,
16
+ responsiveSidebar: true,
17
+ showSettings: true,
18
+ })
19
+
20
+ const state = ref(initState())
21
+ const values = ref<Record<string, unknown>>({})
22
+
23
+ function doseOptions(): DoseDesignControlModelOptions {
24
+ return {
25
+ selectedWells: state.value.selectedWells,
26
+ plateFormat: state.value.plateFormat,
27
+ includeMolecularWeight: state.value.includeMolecularWeight,
28
+ }
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <Story title="Workflow/DoseDesignWorkspaceView" :init-state="initState">
34
+ <Variant title="Playground">
35
+ <div style="min-height: 720px; background: var(--bg-secondary, #f8fafc);">
36
+ <DoseDesignWorkspaceView
37
+ v-model="values"
38
+ :title="state.title"
39
+ :dose-design-options="doseOptions()"
40
+ :sidebar-title="state.sidebarTitle"
41
+ :sidebar-subtitle="state.sidebarSubtitle"
42
+ :sidebar-variant="state.sidebarVariant"
43
+ :responsive-sidebar="state.responsiveSidebar"
44
+ :show-settings="state.showSettings"
45
+ :well-plate-props="{
46
+ heatmap: { enabled: true, min: 0, max: 100, colorScale: 'viridis' },
47
+ }"
48
+ />
49
+ </div>
50
+ </Variant>
51
+
52
+ <template #controls>
53
+ <HstText v-model="state.title" title="Title" />
54
+ <HstText v-model="state.sidebarTitle" title="Sidebar title" />
55
+ <HstText v-model="state.sidebarSubtitle" title="Sidebar subtitle" />
56
+ <HstCheckbox v-model="state.includeMolecularWeight" title="Molecular weight" />
57
+ <HstCheckbox v-model="state.responsiveSidebar" title="Responsive sidebar" />
58
+ <HstCheckbox v-model="state.showSettings" title="Settings" />
59
+ <HstSelect
60
+ v-model="state.sidebarVariant"
61
+ title="Sidebar variant"
62
+ :options="[
63
+ { label: 'Analysis', value: 'analysis' },
64
+ { label: 'Default', value: 'default' },
65
+ ]"
66
+ />
67
+ <HstSelect
68
+ v-model="state.plateFormat"
69
+ title="Plate format"
70
+ :options="[
71
+ { label: '96 wells', value: 96 },
72
+ { label: '384 wells', value: 384 },
73
+ ]"
74
+ />
75
+ </template>
76
+ </Story>
77
+ </template>
@@ -0,0 +1,255 @@
1
+ <script setup lang="ts">
2
+ /** Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design. */
3
+ import { computed } from 'vue'
4
+ import type { TopBarVariant } from '../types'
5
+ import type { FormEnhancements } from '../types/form-builder'
6
+ import type {
7
+ ControlModel,
8
+ ControlModelBinding,
9
+ ControlSchema,
10
+ ControlWorkspaceOptions,
11
+ DoseDesignControlModelOptions,
12
+ UseControlWorkspaceReturn,
13
+ } from '../composables/useControlSchema'
14
+ import { defineDoseDesignControlModel } from '../composables/useControlSchema'
15
+ import ControlWorkspaceView from './ControlWorkspaceView.vue'
16
+ import DoseCalculator from './DoseCalculator.vue'
17
+ import WellPlate from './WellPlate.vue'
18
+
19
+ type DoseDesignSidebarVariant = 'default' | 'analysis'
20
+ type ComponentProps = Record<string, unknown>
21
+ type ComponentPropsById = Record<string, ComponentProps>
22
+ type ComponentBinding = { id: string, component: string, props: ComponentProps }
23
+ type ComponentBindingsById = Record<string, ComponentBinding>
24
+ type ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>
25
+
26
+ interface DoseDesignWorkspaceSlotProps {
27
+ workspace: ResolvedControlWorkspace
28
+ bindings: ResolvedControlWorkspace['bindings']
29
+ values: ResolvedControlWorkspace['values']
30
+ componentBindings: ComponentBinding[]
31
+ componentBindingsById: ComponentBindingsById
32
+ componentPropsById: ComponentPropsById
33
+ wellPlateProps: ComponentProps
34
+ doseCalculatorProps: ComponentProps
35
+ }
36
+
37
+ interface Props {
38
+ /** Model returned by defineDoseDesignControlModel(), or a custom compatible ControlWorkspace model. */
39
+ model?: ControlModel | ControlModelBinding
40
+ /** Workspace returned by useControlWorkspace(). Use for full manual control. */
41
+ workspace?: UseControlWorkspaceReturn<ControlSchema>
42
+ /** Options used when this view creates the default dose-design model internally. */
43
+ doseDesignOptions?: DoseDesignControlModelOptions
44
+ /** Options passed to the internally generated ControlWorkspaceView workspace. */
45
+ controlOptions?: ControlWorkspaceOptions
46
+ /** Initial values for the internally generated workspace. */
47
+ initialValues?: Record<string, unknown>
48
+ /** External values for the internally generated workspace. Supports default v-model. */
49
+ modelValue?: Record<string, unknown>
50
+ /** External values for the internally generated workspace. Supports v-model:values. */
51
+ values?: Record<string, unknown>
52
+ /** Named component props id for the generated WellPlate binding. */
53
+ plateId?: string
54
+ /** Named component props id for the generated DoseCalculator binding. */
55
+ doseId?: string
56
+ /** Extra props merged into the generated WellPlate binding. */
57
+ wellPlateProps?: ComponentProps
58
+ /** Extra props merged into the generated DoseCalculator binding. */
59
+ doseCalculatorProps?: ComponentProps
60
+ /** AppTopBar title. */
61
+ title?: string
62
+ /** AppTopBar subtitle. */
63
+ subtitle?: string
64
+ /** AppTopBar visual variant. */
65
+ topBarVariant?: TopBarVariant
66
+ /** AppSidebar/AppLayout sidebar width. */
67
+ sidebarWidth?: string
68
+ /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */
69
+ sidebarVariant?: DoseDesignSidebarVariant
70
+ /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */
71
+ responsiveSidebar?: boolean
72
+ /** Sidebar position in AppLayout. */
73
+ sidebarPosition?: 'left' | 'right'
74
+ /** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */
75
+ sidebarTitle?: string
76
+ /** Optional AppSidebar chrome subtitle for active experiment/run context. */
77
+ sidebarSubtitle?: string
78
+ /** Optional compact badge/count rendered in the AppSidebar chrome header. */
79
+ sidebarBadge?: string | number
80
+ /** Floating AppLayout style. */
81
+ floating?: boolean
82
+ /** Compact AppSidebar density. */
83
+ dense?: boolean
84
+ /** Whether AppTopBar should show generated settings. */
85
+ showSettings?: boolean
86
+ /** Render FormBuilder actions in the default generated forms. */
87
+ showFormActions?: boolean
88
+ /** Runtime FormBuilder enhancements passed to generated forms. */
89
+ formEnhancements?: FormEnhancements<Record<string, unknown>>
90
+ /** Loading/saving state passed to generated forms. */
91
+ formLoading?: boolean
92
+ /** Disabled state passed to generated forms. */
93
+ formDisabled?: boolean
94
+ /** Readonly state passed to generated forms. */
95
+ formReadonly?: boolean
96
+ /** FormBuilder size in generated forms. */
97
+ formSize?: 'sm' | 'md' | 'lg'
98
+ }
99
+
100
+ const props = withDefaults(defineProps<Props>(), {
101
+ model: undefined,
102
+ workspace: undefined,
103
+ doseDesignOptions: () => ({}),
104
+ controlOptions: () => ({}),
105
+ initialValues: undefined,
106
+ modelValue: undefined,
107
+ values: undefined,
108
+ plateId: undefined,
109
+ doseId: undefined,
110
+ wellPlateProps: () => ({}),
111
+ doseCalculatorProps: () => ({}),
112
+ title: 'Dose Design Workspace',
113
+ subtitle: undefined,
114
+ topBarVariant: 'card',
115
+ sidebarWidth: '320px',
116
+ sidebarVariant: 'analysis',
117
+ responsiveSidebar: true,
118
+ sidebarPosition: 'left',
119
+ sidebarTitle: undefined,
120
+ sidebarSubtitle: undefined,
121
+ sidebarBadge: undefined,
122
+ floating: false,
123
+ dense: true,
124
+ showSettings: true,
125
+ showFormActions: false,
126
+ formEnhancements: undefined,
127
+ formLoading: false,
128
+ formDisabled: false,
129
+ formReadonly: false,
130
+ formSize: 'md',
131
+ })
132
+
133
+ const emit = defineEmits<{
134
+ /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */
135
+ 'update:modelValue': [values: Record<string, unknown>]
136
+ /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */
137
+ 'update:values': [values: Record<string, unknown>]
138
+ /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */
139
+ submit: [values: Record<string, unknown>]
140
+ /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */
141
+ cancel: []
142
+ }>()
143
+
144
+ defineSlots<{
145
+ default?: (props: DoseDesignWorkspaceSlotProps) => unknown
146
+ plate?: (props: DoseDesignWorkspaceSlotProps) => unknown
147
+ dose?: (props: DoseDesignWorkspaceSlotProps) => unknown
148
+ }>()
149
+
150
+ const resolvedModel = computed<ControlModel | ControlModelBinding>(() =>
151
+ props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)
152
+ )
153
+
154
+ function resolvedPlateId(): string {
155
+ return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'
156
+ }
157
+
158
+ function resolvedDoseId(): string {
159
+ return props.doseId ?? props.doseDesignOptions.componentProps?.doseId ?? 'dose'
160
+ }
161
+
162
+ function wellPlateBinding(componentPropsById: ComponentPropsById): ComponentProps {
163
+ return {
164
+ size: 'fill',
165
+ selectionMode: 'multiple',
166
+ ...(componentPropsById[resolvedPlateId()] ?? {}),
167
+ ...props.wellPlateProps,
168
+ }
169
+ }
170
+
171
+ function doseCalculatorBinding(componentPropsById: ComponentPropsById): ComponentProps {
172
+ return {
173
+ ...(componentPropsById[resolvedDoseId()] ?? {}),
174
+ ...props.doseCalculatorProps,
175
+ }
176
+ }
177
+
178
+ function slotProps(slotProps: {
179
+ workspace: ResolvedControlWorkspace
180
+ bindings: ResolvedControlWorkspace['bindings']
181
+ values: ResolvedControlWorkspace['values']
182
+ componentBindings: ComponentBinding[]
183
+ componentBindingsById: ComponentBindingsById
184
+ componentPropsById: ComponentPropsById
185
+ }): DoseDesignWorkspaceSlotProps {
186
+ return {
187
+ ...slotProps,
188
+ wellPlateProps: wellPlateBinding(slotProps.componentPropsById),
189
+ doseCalculatorProps: doseCalculatorBinding(slotProps.componentPropsById),
190
+ }
191
+ }
192
+ </script>
193
+
194
+ <template>
195
+ <ControlWorkspaceView
196
+ :model="resolvedModel"
197
+ :workspace="workspace"
198
+ :control-options="controlOptions"
199
+ :initial-values="initialValues"
200
+ :model-value="modelValue"
201
+ :values="values"
202
+ :title="title"
203
+ :subtitle="subtitle"
204
+ :top-bar-variant="topBarVariant"
205
+ :sidebar-width="sidebarWidth"
206
+ :sidebar-variant="sidebarVariant"
207
+ :responsive-sidebar="responsiveSidebar"
208
+ :sidebar-position="sidebarPosition"
209
+ :sidebar-title="sidebarTitle"
210
+ :sidebar-subtitle="sidebarSubtitle"
211
+ :sidebar-badge="sidebarBadge"
212
+ :floating="floating"
213
+ :dense="dense"
214
+ :show-settings="showSettings"
215
+ :show-form-actions="showFormActions"
216
+ :form-enhancements="formEnhancements"
217
+ :form-loading="formLoading"
218
+ :form-disabled="formDisabled"
219
+ :form-readonly="formReadonly"
220
+ :form-size="formSize"
221
+ @update:model-value="emit('update:modelValue', $event)"
222
+ @update:values="emit('update:values', $event)"
223
+ @submit="emit('submit', $event)"
224
+ @cancel="emit('cancel')"
225
+ >
226
+ <template #default="workspaceSlotProps">
227
+ <slot v-bind="slotProps(workspaceSlotProps)">
228
+ <div class="mint-dose-design-workspace">
229
+ <slot name="plate" v-bind="slotProps(workspaceSlotProps)">
230
+ <WellPlate v-bind="wellPlateBinding(workspaceSlotProps.componentPropsById)" />
231
+ </slot>
232
+ <slot name="dose" v-bind="slotProps(workspaceSlotProps)">
233
+ <DoseCalculator v-bind="doseCalculatorBinding(workspaceSlotProps.componentPropsById)" />
234
+ </slot>
235
+ </div>
236
+ </slot>
237
+ </template>
238
+ </ControlWorkspaceView>
239
+ </template>
240
+
241
+ <style scoped>
242
+ .mint-dose-design-workspace {
243
+ display: grid;
244
+ grid-template-columns: minmax(360px, 1fr) minmax(300px, 420px);
245
+ gap: 1rem;
246
+ align-items: start;
247
+ min-width: 0;
248
+ }
249
+
250
+ @media (max-width: 900px) {
251
+ .mint-dose-design-workspace {
252
+ grid-template-columns: minmax(0, 1fr);
253
+ }
254
+ }
255
+ </style>
@@ -1,7 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /** Button that opens a dropdown menu for selecting one option from a list. */
3
- import { ref, computed, onMounted, onUnmounted } from 'vue'
4
- import type { SelectOption, ButtonVariant, ButtonSize } from '../types'
3
+ import { computed } from 'vue'
4
+ import { useDropdownState } from '../composables/useDropdownState'
5
+ import type { SelectOption, SelectOptionInput, ButtonVariant, ButtonSize } from '../types'
6
+ import { normalizeOptionInput } from '../utils/options'
5
7
 
6
8
  /**
7
9
  * DropdownButton - Button-style select with dropdown menu for option selection.
@@ -22,7 +24,7 @@ interface Props {
22
24
  /** Selected option value */
23
25
  modelValue?: string | number
24
26
  /** Available options */
25
- options: SelectOption<string | number>[]
27
+ options: SelectOptionInput<string | number>[]
26
28
  /** Placeholder text when no option is selected */
27
29
  placeholder?: string
28
30
  /** Button style variant */
@@ -51,52 +53,32 @@ const emit = defineEmits<{
51
53
  select: [option: SelectOption<string | number>]
52
54
  }>()
53
55
 
54
- const isOpen = ref(false)
55
- const containerRef = ref<HTMLDivElement>()
56
+ const { isOpen, rootRef, close, toggle: toggleDropdown } = useDropdownState()
57
+ const normalizedOptions = computed<SelectOption<string | number>[]>(() =>
58
+ props.options.map(normalizeOptionInput)
59
+ )
56
60
 
57
61
  const selectedLabel = computed(() => {
58
62
  if (props.modelValue === undefined) return props.placeholder ?? 'Select...'
59
- const found = props.options.find(o => o.value === props.modelValue)
63
+ const found = normalizedOptions.value.find(o => o.value === props.modelValue)
60
64
  return found?.label ?? String(props.modelValue)
61
65
  })
62
66
 
63
67
  function toggle() {
64
68
  if (props.disabled || props.loading) return
65
- isOpen.value = !isOpen.value
69
+ toggleDropdown()
66
70
  }
67
71
 
68
72
  function selectOption(option: SelectOption<string | number>) {
69
73
  if (option.disabled) return
70
74
  emit('update:modelValue', option.value)
71
75
  emit('select', option)
72
- isOpen.value = false
76
+ close()
73
77
  }
74
-
75
- function handleClickOutside(event: MouseEvent) {
76
- if (containerRef.value && !containerRef.value.contains(event.target as Node)) {
77
- isOpen.value = false
78
- }
79
- }
80
-
81
- function handleKeydown(event: KeyboardEvent) {
82
- if (event.key === 'Escape') {
83
- isOpen.value = false
84
- }
85
- }
86
-
87
- onMounted(() => {
88
- document.addEventListener('click', handleClickOutside)
89
- document.addEventListener('keydown', handleKeydown)
90
- })
91
-
92
- onUnmounted(() => {
93
- document.removeEventListener('click', handleClickOutside)
94
- document.removeEventListener('keydown', handleKeydown)
95
- })
96
78
  </script>
97
79
 
98
80
  <template>
99
- <div ref="containerRef" class="mint-dropdown-button">
81
+ <div ref="rootRef" class="mint-dropdown-button">
100
82
  <button
101
83
  type="button"
102
84
  :disabled="disabled || loading"
@@ -139,7 +121,7 @@ onUnmounted(() => {
139
121
  :aria-activedescendant="modelValue !== undefined ? `mint-dropdown-option-${modelValue}` : undefined"
140
122
  >
141
123
  <li
142
- v-for="option in options"
124
+ v-for="option in normalizedOptions"
143
125
  :id="`mint-dropdown-option-${option.value}`"
144
126
  :key="String(option.value)"
145
127
  role="option"
@@ -1,5 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  /** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */
3
+ import BaseButton from './BaseButton.vue'
4
+
3
5
  interface Props {
4
6
  title?: string
5
7
  description?: string
@@ -57,9 +59,9 @@ const defaultIconPaths = [
57
59
  <slot />
58
60
  </div>
59
61
  <div v-if="actionLabel" class="mint-empty-state__action">
60
- <button class="mint-empty-state__button" @click="emit('action')">
62
+ <BaseButton @click="emit('action')">
61
63
  {{ actionLabel }}
62
- </button>
64
+ </BaseButton>
63
65
  </div>
64
66
  </div>
65
67
  </template>
@@ -1,6 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Floating popover showing the active experiment with save, detach, and select actions. */
3
- import { ref, watch, onMounted, onUnmounted } from 'vue'
3
+ import { ref, watch, onUnmounted } from 'vue'
4
+ import { useDropdownState } from '../composables/useDropdownState'
5
+ import { formatExperimentStatus } from '../composables/experiment-utils'
4
6
  import ConfirmDialog from './ConfirmDialog.vue'
5
7
 
6
8
  interface Props {
@@ -32,19 +34,12 @@ const emit = defineEmits<{
32
34
  detach: []
33
35
  }>()
34
36
 
35
- const isOpen = ref(false)
36
- const popoverRef = ref<HTMLElement | null>(null)
37
+ const { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({
38
+ closeOnEscape: false,
39
+ })
37
40
  const showSuccess = ref(false)
38
41
  const showConfirm = ref(false)
39
42
 
40
- function toggle() {
41
- isOpen.value = !isOpen.value
42
- }
43
-
44
- function close() {
45
- isOpen.value = false
46
- }
47
-
48
43
  function handleSelect() {
49
44
  emit('select')
50
45
  close()
@@ -69,12 +64,6 @@ function handleDetach() {
69
64
  close()
70
65
  }
71
66
 
72
- function handleClickOutside(event: MouseEvent) {
73
- if (popoverRef.value && !popoverRef.value.contains(event.target as Node)) {
74
- close()
75
- }
76
- }
77
-
78
67
  let successTimer: ReturnType<typeof setTimeout> | null = null
79
68
 
80
69
  // Show success state when saveSuccessMessage changes from empty to a value
@@ -89,19 +78,9 @@ watch(() => props.saveSuccessMessage, (msg) => {
89
78
  }
90
79
  })
91
80
 
92
- onMounted(() => {
93
- document.addEventListener('click', handleClickOutside)
94
- })
95
-
96
81
  onUnmounted(() => {
97
- document.removeEventListener('click', handleClickOutside)
98
82
  if (successTimer) clearTimeout(successTimer)
99
83
  })
100
-
101
- // Format status for display (e.g., "ready_to_extract" -> "Ready to extract")
102
- function formatStatus(status: string): string {
103
- return status.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase())
104
- }
105
84
  </script>
106
85
 
107
86
  <template>
@@ -208,7 +187,7 @@ function formatStatus(status: string): string {
208
187
  <span v-if="experimentCode" class="mint-experiment-popover__card-code">{{ experimentCode }}</span>
209
188
  <div class="mint-experiment-popover__card-name">{{ experimentName }}</div>
210
189
  <div v-if="experimentStatus" class="mint-experiment-popover__card-status">
211
- {{ formatStatus(experimentStatus) }}
190
+ {{ formatExperimentStatus(experimentStatus) }}
212
191
  </div>
213
192
  </div>
214
193
  </div>
@@ -5,8 +5,9 @@ import type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'
5
5
  import { useExperimentSelector } from '../composables/useExperimentSelector'
6
6
  import {
7
7
  formatExperimentDate,
8
+ formatExperimentStatus,
9
+ getExperimentStatusVariant,
8
10
  EXPERIMENT_STATUS_OPTIONS,
9
- EXPERIMENT_STATUS_VARIANT_MAP,
10
11
  DATE_PRESET_OPTIONS,
11
12
  SORT_OPTIONS,
12
13
  } from '../composables/experiment-utils'
@@ -326,8 +327,8 @@ watch(
326
327
  <span>{{ formatExperimentDate(exp.created_at) }}</span>
327
328
  </div>
328
329
  </div>
329
- <BasePill :variant="EXPERIMENT_STATUS_VARIANT_MAP[exp.status]" size="sm">
330
- {{ exp.status }}
330
+ <BasePill :variant="getExperimentStatusVariant(exp.status)" size="sm">
331
+ {{ formatExperimentStatus(exp.status) }}
331
332
  </BasePill>
332
333
  </div>
333
334
  </template>
@@ -362,8 +363,8 @@ watch(
362
363
  <span>{{ formatExperimentDate(exp.created_at) }}</span>
363
364
  </div>
364
365
  </div>
365
- <BasePill :variant="EXPERIMENT_STATUS_VARIANT_MAP[exp.status]" size="sm">
366
- {{ exp.status }}
366
+ <BasePill :variant="getExperimentStatusVariant(exp.status)" size="sm">
367
+ {{ formatExperimentStatus(exp.status) }}
367
368
  </BasePill>
368
369
  </div>
369
370
  </div>