@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
@@ -1,7 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /** Avatar-triggered dropdown with user name/email header, configurable menu items, dividers, and a sign-out action. */
3
- import { ref, computed, onMounted, onUnmounted } from 'vue'
3
+ import { computed } from 'vue'
4
+ import { useDropdownState } from '../composables/useDropdownState'
4
5
  import type { AccountMenuItem } from '../types/components'
6
+ import ActionItemInternal from './internal/ActionItemInternal.vue'
5
7
 
6
8
  interface Props {
7
9
  userName?: string
@@ -25,8 +27,10 @@ const emit = defineEmits<{
25
27
  'menu-close': []
26
28
  }>()
27
29
 
28
- const isOpen = ref(false)
29
- const rootRef = ref<HTMLElement | null>(null)
30
+ const { isOpen, rootRef, close, toggle } = useDropdownState({
31
+ onOpen: () => emit('menu-open'),
32
+ onClose: () => emit('menu-close'),
33
+ })
30
34
 
31
35
  const initials = computed(() => {
32
36
  if (props.userInitial) return props.userInitial.slice(0, 2).toUpperCase()
@@ -38,53 +42,20 @@ const initials = computed(() => {
38
42
  return 'U'
39
43
  })
40
44
 
41
- function toggle() {
42
- isOpen.value = !isOpen.value
43
- if (isOpen.value) {
44
- emit('menu-open')
45
- } else {
46
- emit('menu-close')
47
- }
45
+ function closeSilently() {
46
+ isOpen.value = false
48
47
  }
49
48
 
50
49
  function handleSelect(item: AccountMenuItem) {
51
50
  if (item.divider) return
52
51
  emit('select', item)
53
- isOpen.value = false
54
- emit('menu-close')
52
+ close()
55
53
  }
56
54
 
57
55
  function handleSignOut() {
58
56
  emit('sign-out')
59
- isOpen.value = false
60
- emit('menu-close')
61
- }
62
-
63
- function handleClickOutside(event: MouseEvent) {
64
- if (!isOpen.value) return
65
- const target = event.target as Node
66
- if (rootRef.value && !rootRef.value.contains(target)) {
67
- isOpen.value = false
68
- emit('menu-close')
69
- }
70
- }
71
-
72
- function handleKeydown(event: KeyboardEvent) {
73
- if (event.key === 'Escape' && isOpen.value) {
74
- isOpen.value = false
75
- emit('menu-close')
76
- }
57
+ close()
77
58
  }
78
-
79
- onMounted(() => {
80
- document.addEventListener('click', handleClickOutside)
81
- document.addEventListener('keydown', handleKeydown)
82
- })
83
-
84
- onUnmounted(() => {
85
- document.removeEventListener('click', handleClickOutside)
86
- document.removeEventListener('keydown', handleKeydown)
87
- })
88
59
  </script>
89
60
 
90
61
  <template>
@@ -112,24 +83,12 @@ onUnmounted(() => {
112
83
  <div v-if="userEmail" class="mint-avatar-menu__user-email">{{ userEmail }}</div>
113
84
  </div>
114
85
 
115
- <slot name="items" :close="() => (isOpen = false)">
86
+ <slot name="items" :close="closeSilently">
116
87
  <template v-for="item in items" :key="item.id">
117
88
  <div v-if="item.divider" class="mint-avatar-menu__divider" role="separator" />
118
- <a
119
- v-else-if="item.href"
89
+ <ActionItemInternal
90
+ v-else
120
91
  :href="item.href"
121
- :class="['mint-avatar-menu__item', { 'mint-avatar-menu__item--danger': item.danger }]"
122
- role="menuitem"
123
- @click="handleSelect(item)"
124
- >
125
- <span v-if="item.icon" class="mint-avatar-menu__item-icon">
126
- <slot name="item-icon" :item="item" />
127
- </span>
128
- <span class="mint-avatar-menu__item-label">{{ item.label }}</span>
129
- <span v-if="item.rightLabel" class="mint-avatar-menu__item-right">{{ item.rightLabel }}</span>
130
- </a>
131
- <router-link
132
- v-else-if="item.to"
133
92
  :to="item.to"
134
93
  :class="['mint-avatar-menu__item', { 'mint-avatar-menu__item--danger': item.danger }]"
135
94
  role="menuitem"
@@ -140,20 +99,7 @@ onUnmounted(() => {
140
99
  </span>
141
100
  <span class="mint-avatar-menu__item-label">{{ item.label }}</span>
142
101
  <span v-if="item.rightLabel" class="mint-avatar-menu__item-right">{{ item.rightLabel }}</span>
143
- </router-link>
144
- <button
145
- v-else
146
- type="button"
147
- :class="['mint-avatar-menu__item', { 'mint-avatar-menu__item--danger': item.danger }]"
148
- role="menuitem"
149
- @click="handleSelect(item)"
150
- >
151
- <span v-if="item.icon" class="mint-avatar-menu__item-icon">
152
- <slot name="item-icon" :item="item" />
153
- </span>
154
- <span class="mint-avatar-menu__item-label">{{ item.label }}</span>
155
- <span v-if="item.rightLabel" class="mint-avatar-menu__item-right">{{ item.rightLabel }}</span>
156
- </button>
102
+ </ActionItemInternal>
157
103
  </template>
158
104
  </slot>
159
105
 
@@ -8,11 +8,11 @@ import BaseSelect from './BaseSelect.vue'
8
8
  import BaseToggle from './BaseToggle.vue'
9
9
  import BaseButton from './BaseButton.vue'
10
10
  import { ExperimentProvider } from '../__stories__/experiment-helpers'
11
- import type { SidebarToolSection, TopBarTab } from '../types'
11
+ import type { PillNavItem, SidebarToolSection } from '../types'
12
12
 
13
- const activeTab = ref('analysis')
13
+ const activeView = ref('analysis')
14
14
 
15
- const tabs: TopBarTab[] = [
15
+ const pillNav: PillNavItem[] = [
16
16
  { id: 'analysis', label: 'Analysis' },
17
17
  { id: 'results', label: 'Results' },
18
18
  { id: 'settings', label: 'Settings' },
@@ -48,22 +48,23 @@ const logScale = ref(false)
48
48
  :sidebar-position="state.sidebarPosition"
49
49
  :floating="state.floating ?? true"
50
50
  :sidebar-width="state.sidebarWidth"
51
+ responsive-sidebar
51
52
  >
52
53
  <template #topbar>
53
54
  <AppTopBar
54
55
  title="My Plugin"
55
56
  subtitle="Laboratory Analysis"
56
- :tabs="tabs"
57
- :current-tab-id="activeTab"
57
+ :pill-nav="pillNav"
58
+ :current-pill-id="activeView"
58
59
  :show-theme-toggle="true"
59
60
  home-path=""
60
- @tab-select="t => activeTab = t.id"
61
+ @pill-select="item => activeView = item.id"
61
62
  />
62
63
  </template>
63
64
  <template #sidebar>
64
65
  <AppSidebar
65
66
  :panels="toolPanels"
66
- :active-view="activeTab"
67
+ :active-view="activeView"
67
68
  :floating="false"
68
69
  :side="state.sidebarPosition"
69
70
  >
@@ -86,11 +87,11 @@ const logScale = ref(false)
86
87
  </template>
87
88
  <div style="padding: 2rem;">
88
89
  <h2 style="margin: 0 0 1rem; color: var(--text-primary, #1e293b);">
89
- {{ activeTab.charAt(0).toUpperCase() + activeTab.slice(1) }}
90
+ {{ activeView.charAt(0).toUpperCase() + activeView.slice(1) }}
90
91
  </h2>
91
92
  <p style="color: var(--text-muted, #94a3b8);">
92
- Main content area. The sidebar shows tools for the "{{ activeTab }}" view.
93
- Switch tabs to see different tool panels (settings tab has none).
93
+ Main content area. The sidebar shows tools for the "{{ activeView }}" view.
94
+ Switch modes to see different tool panels (settings has none).
94
95
  </p>
95
96
  </div>
96
97
  </AppLayout>
@@ -116,19 +117,19 @@ const logScale = ref(false)
116
117
  >
117
118
  <template #topbar>
118
119
  <AppTopBar
119
- plugin-name="Plate Analyzer"
120
- title="Dashboard"
121
- :tabs="tabs"
122
- :current-tab-id="activeTab"
120
+ title="Plate Analyzer"
121
+ subtitle="Dashboard"
122
+ :pill-nav="pillNav"
123
+ :current-pill-id="activeView"
123
124
  :show-theme-toggle="true"
124
125
  home-path=""
125
- @tab-select="t => activeTab = t.id"
126
+ @pill-select="item => activeView = item.id"
126
127
  />
127
128
  </template>
128
129
  <template #sidebar>
129
130
  <AppSidebar
130
131
  :panels="toolPanels"
131
- :active-view="activeTab"
132
+ :active-view="activeView"
132
133
  :floating="false"
133
134
  >
134
135
  <template #header>
@@ -216,25 +217,63 @@ const logScale = ref(false)
216
217
  </div>
217
218
  </Variant>
218
219
 
220
+ <Variant title="Responsive Sidebar">
221
+ <div style="height: 500px; max-width: 760px; border: 1px solid var(--border-color, #e2e8f0);">
222
+ <AppLayout
223
+ floating
224
+ responsive-sidebar
225
+ sidebar-width="320px"
226
+ >
227
+ <template #topbar>
228
+ <AppTopBar title="Responsive Analysis" home-path="" />
229
+ </template>
230
+ <template #sidebar>
231
+ <AppSidebar
232
+ variant="analysis"
233
+ title="Feature Filters"
234
+ subtitle="12 active samples"
235
+ :badge="2"
236
+ :panels="toolPanels"
237
+ active-view="analysis"
238
+ >
239
+ <template #section-parameters>
240
+ <BaseSlider v-model="threshold" label="Threshold" :min="0" :max="100" />
241
+ <BaseSelect v-model="method" label="Method" :options="methods" />
242
+ </template>
243
+ <template #section-filters>
244
+ <BaseToggle v-model="showOutliers" label="Exclude outliers" />
245
+ </template>
246
+ </AppSidebar>
247
+ </template>
248
+ <div style="padding: 2rem;">
249
+ <p style="color: var(--text-muted, #94a3b8);">
250
+ At narrow widths, AppLayout owns the sidebar toggle and backdrop,
251
+ while AppSidebar owns the panel chrome and collapse state.
252
+ </p>
253
+ </div>
254
+ </AppLayout>
255
+ </div>
256
+ </Variant>
257
+
219
258
  <Variant title="With Experiment Selector">
220
259
  <ExperimentProvider>
221
260
  <div style="height: 600px;">
222
261
  <AppLayout :floating="true" sidebar-width="280px">
223
262
  <template #topbar>
224
263
  <AppTopBar
225
- plugin-name="IC50 Calculator"
226
- title="Analysis"
227
- :tabs="tabs"
228
- :current-tab-id="activeTab"
264
+ title="IC50 Calculator"
265
+ subtitle="Analysis"
266
+ :pill-nav="pillNav"
267
+ :current-pill-id="activeView"
229
268
  :show-theme-toggle="true"
230
269
  home-path=""
231
- @tab-select="t => activeTab = t.id"
270
+ @pill-select="item => activeView = item.id"
232
271
  />
233
272
  </template>
234
273
  <template #sidebar>
235
274
  <AppSidebar
236
275
  :panels="toolPanels"
237
- :active-view="activeTab"
276
+ :active-view="activeView"
238
277
  :floating="false"
239
278
  >
240
279
  <template #section-parameters>
@@ -255,7 +294,7 @@ const logScale = ref(false)
255
294
  </template>
256
295
  <div style="padding: 2rem;">
257
296
  <h2 style="margin: 0 0 1rem; color: var(--text-primary, #1e293b);">
258
- {{ activeTab.charAt(0).toUpperCase() + activeTab.slice(1) }}
297
+ {{ activeView.charAt(0).toUpperCase() + activeView.slice(1) }}
259
298
  </h2>
260
299
  <p style="color: var(--text-muted, #94a3b8);">
261
300
  Full layout with experiment selector in the top bar.
@@ -273,8 +312,8 @@ const logScale = ref(false)
273
312
  <AppLayout :floating="true">
274
313
  <template #topbar>
275
314
  <AppTopBar
276
- plugin-name="Plate Analyzer"
277
- title="Dashboard"
315
+ title="Plate Analyzer"
316
+ subtitle="Dashboard"
278
317
  :show-theme-toggle="true"
279
318
  home-path=""
280
319
  />
@@ -3,7 +3,8 @@
3
3
  * AppLayout - Page layout shell with topbar, sidebar, and main content slots
4
4
  *
5
5
  * Provides a responsive application layout structure with optional topbar and sidebar.
6
- * The sidebar slot is a simple pass-through; visibility is controlled by AppSidebar itself.
6
+ * The sidebar slot is a simple pass-through on desktop. When responsiveSidebar is
7
+ * enabled, AppLayout owns the mobile toggle/backdrop shell around the sidebar.
7
8
  *
8
9
  * @example
9
10
  * ```vue
@@ -20,7 +21,7 @@
20
21
  * </AppLayout>
21
22
  * ```
22
23
  */
23
- import { computed } from 'vue'
24
+ import { computed, ref } from 'vue'
24
25
 
25
26
  interface Props {
26
27
  /** Position of sidebar (left or right side of screen) */
@@ -29,23 +30,61 @@ interface Props {
29
30
  sidebarWidth?: string
30
31
  /** When true, topbar/sidebar/main render as floating cards with gaps */
31
32
  floating?: boolean
33
+ /** Convert the sidebar into a mobile overlay with built-in toggle and backdrop below 1024px. */
34
+ responsiveSidebar?: boolean
35
+ /** Controlled mobile sidebar open state. Desktop sidebar remains visible. */
36
+ sidebarOpen?: boolean
37
+ /** Initial mobile sidebar open state when sidebarOpen is uncontrolled. */
38
+ defaultSidebarOpen?: boolean
39
+ /** Accessible label for the mobile sidebar toggle. */
40
+ sidebarToggleLabel?: string
41
+ /** Accessible label used when the mobile sidebar is open. */
42
+ sidebarCloseLabel?: string
32
43
  }
33
44
 
34
45
  const props = withDefaults(defineProps<Props>(), {
35
46
  sidebarPosition: 'left',
36
47
  sidebarWidth: 'auto',
37
48
  floating: false,
49
+ responsiveSidebar: false,
50
+ sidebarOpen: undefined,
51
+ defaultSidebarOpen: false,
52
+ sidebarToggleLabel: 'Open sidebar',
53
+ sidebarCloseLabel: 'Close sidebar',
54
+ })
55
+
56
+ const emit = defineEmits<{
57
+ 'update:sidebarOpen': [value: boolean]
58
+ }>()
59
+
60
+ const internalSidebarOpen = ref(props.defaultSidebarOpen)
61
+ const sidebarOpenModel = computed({
62
+ get: () => props.sidebarOpen ?? internalSidebarOpen.value,
63
+ set: (value: boolean) => {
64
+ internalSidebarOpen.value = value
65
+ emit('update:sidebarOpen', value)
66
+ },
38
67
  })
39
68
 
40
69
  const layoutClasses = computed(() => [
41
70
  'mint-layout',
42
71
  props.sidebarPosition === 'right' ? 'mint-layout--sidebar-right' : '',
43
72
  props.floating ? 'mint-layout--floating' : '',
73
+ props.responsiveSidebar ? 'mint-layout--responsive-sidebar' : '',
74
+ sidebarOpenModel.value ? 'mint-layout--sidebar-open' : '',
44
75
  ])
45
76
 
46
77
  const sidebarStyle = computed(() => {
47
78
  return props.sidebarWidth !== 'auto' ? { width: props.sidebarWidth } : undefined
48
79
  })
80
+
81
+ function toggleSidebar() {
82
+ sidebarOpenModel.value = !sidebarOpenModel.value
83
+ }
84
+
85
+ function closeSidebar() {
86
+ sidebarOpenModel.value = false
87
+ }
49
88
  </script>
50
89
 
51
90
  <template>
@@ -55,6 +94,48 @@ const sidebarStyle = computed(() => {
55
94
  </div>
56
95
 
57
96
  <div class="mint-layout__body">
97
+ <button
98
+ v-if="responsiveSidebar && $slots.sidebar"
99
+ type="button"
100
+ class="mint-layout__sidebar-toggle"
101
+ :aria-label="sidebarOpenModel ? sidebarCloseLabel : sidebarToggleLabel"
102
+ :aria-expanded="sidebarOpenModel"
103
+ @click="toggleSidebar"
104
+ >
105
+ <svg
106
+ v-if="!sidebarOpenModel"
107
+ class="mint-layout__sidebar-toggle-icon"
108
+ viewBox="0 0 24 24"
109
+ fill="none"
110
+ stroke="currentColor"
111
+ stroke-width="2"
112
+ stroke-linecap="round"
113
+ stroke-linejoin="round"
114
+ aria-hidden="true"
115
+ >
116
+ <path d="M4 6h16M4 12h16M4 18h16" />
117
+ </svg>
118
+ <svg
119
+ v-else
120
+ class="mint-layout__sidebar-toggle-icon"
121
+ viewBox="0 0 24 24"
122
+ fill="none"
123
+ stroke="currentColor"
124
+ stroke-width="2"
125
+ stroke-linecap="round"
126
+ stroke-linejoin="round"
127
+ aria-hidden="true"
128
+ >
129
+ <path d="M6 18 18 6M6 6l12 12" />
130
+ </svg>
131
+ </button>
132
+
133
+ <div
134
+ v-if="responsiveSidebar && $slots.sidebar && sidebarOpenModel"
135
+ class="mint-layout__sidebar-backdrop"
136
+ @click="closeSidebar"
137
+ />
138
+
58
139
  <div
59
140
  v-if="$slots.sidebar"
60
141
  class="mint-layout__sidebar"
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Dropdown menu for switching between installed plugins with color swatches, version badges, and an install link. */
3
- import { ref, onMounted, onUnmounted } from 'vue'
3
+ import { useDropdownState } from '../composables/useDropdownState'
4
4
  import type { PluginSwitcherPlugin } from '../types/components'
5
+ import ActionItemInternal from './internal/ActionItemInternal.vue'
5
6
 
6
7
  interface Props {
7
8
  current: PluginSwitcherPlugin
@@ -21,55 +22,26 @@ const emit = defineEmits<{
21
22
  'install-click': []
22
23
  }>()
23
24
 
24
- const isOpen = ref(false)
25
- const rootRef = ref<HTMLElement | null>(null)
25
+ const { isOpen, rootRef, close, toggle } = useDropdownState()
26
26
 
27
27
  function initialsOf(plugin: PluginSwitcherPlugin) {
28
28
  const label = plugin.label || plugin.id
29
29
  return label.slice(0, 2).toUpperCase()
30
30
  }
31
31
 
32
- function toggle() {
33
- isOpen.value = !isOpen.value
34
- }
35
-
36
32
  function handleSelect(plugin: PluginSwitcherPlugin) {
37
33
  if (plugin.id === props.current.id) {
38
- isOpen.value = false
34
+ close()
39
35
  return
40
36
  }
41
37
  emit('select', plugin)
42
- isOpen.value = false
38
+ close()
43
39
  }
44
40
 
45
41
  function handleInstall() {
46
42
  emit('install-click')
47
- isOpen.value = false
48
- }
49
-
50
- function handleClickOutside(event: MouseEvent) {
51
- if (!isOpen.value) return
52
- const target = event.target as Node
53
- if (rootRef.value && !rootRef.value.contains(target)) {
54
- isOpen.value = false
55
- }
43
+ close()
56
44
  }
57
-
58
- function handleKeydown(event: KeyboardEvent) {
59
- if (event.key === 'Escape' && isOpen.value) {
60
- isOpen.value = false
61
- }
62
- }
63
-
64
- onMounted(() => {
65
- document.addEventListener('click', handleClickOutside)
66
- document.addEventListener('keydown', handleKeydown)
67
- })
68
-
69
- onUnmounted(() => {
70
- document.removeEventListener('click', handleClickOutside)
71
- document.removeEventListener('keydown', handleKeydown)
72
- })
73
45
  </script>
74
46
 
75
47
  <template>
@@ -102,124 +74,48 @@ onUnmounted(() => {
102
74
 
103
75
  <div v-if="isOpen" class="mint-plugin-switcher__menu" role="menu">
104
76
  <div class="mint-plugin-switcher__menu-title">Switch plugin</div>
105
- <template v-for="plugin in plugins" :key="plugin.id">
106
- <a
107
- v-if="plugin.href"
108
- :href="plugin.href"
109
- :class="[
110
- 'mint-plugin-switcher__item',
111
- { 'mint-plugin-switcher__item--active': plugin.id === current.id },
112
- ]"
113
- role="menuitem"
114
- @click="handleSelect(plugin)"
77
+ <ActionItemInternal
78
+ v-for="plugin in plugins"
79
+ :key="plugin.id"
80
+ :href="plugin.href"
81
+ :to="plugin.to"
82
+ :class="[
83
+ 'mint-plugin-switcher__item',
84
+ { 'mint-plugin-switcher__item--active': plugin.id === current.id },
85
+ ]"
86
+ role="menuitem"
87
+ @click="handleSelect(plugin)"
88
+ >
89
+ <span
90
+ class="mint-plugin-switcher__item-swatch"
91
+ :style="{ background: plugin.color ?? 'var(--color-primary)' }"
115
92
  >
116
- <span
117
- class="mint-plugin-switcher__item-swatch"
118
- :style="{ background: plugin.color ?? 'var(--color-primary)' }"
119
- >
120
- {{ initialsOf(plugin) }}
121
- </span>
122
- <span class="mint-plugin-switcher__item-label">{{ plugin.label }}</span>
123
- <span v-if="plugin.version" class="mint-plugin-switcher__item-version">v{{ plugin.version }}</span>
124
- <svg
125
- v-if="plugin.id === current.id"
126
- class="mint-plugin-switcher__item-check"
127
- viewBox="0 0 24 24"
128
- fill="none"
129
- stroke="currentColor"
130
- stroke-width="2.5"
131
- stroke-linecap="round"
132
- stroke-linejoin="round"
133
- aria-hidden="true"
134
- >
135
- <path d="M5 13l4 4L19 7" />
136
- </svg>
137
- </a>
138
- <router-link
139
- v-else-if="plugin.to"
140
- :to="plugin.to"
141
- :class="[
142
- 'mint-plugin-switcher__item',
143
- { 'mint-plugin-switcher__item--active': plugin.id === current.id },
144
- ]"
145
- role="menuitem"
146
- @click="handleSelect(plugin)"
147
- >
148
- <span
149
- class="mint-plugin-switcher__item-swatch"
150
- :style="{ background: plugin.color ?? 'var(--color-primary)' }"
151
- >
152
- {{ initialsOf(plugin) }}
153
- </span>
154
- <span class="mint-plugin-switcher__item-label">{{ plugin.label }}</span>
155
- <span v-if="plugin.version" class="mint-plugin-switcher__item-version">v{{ plugin.version }}</span>
156
- <svg
157
- v-if="plugin.id === current.id"
158
- class="mint-plugin-switcher__item-check"
159
- viewBox="0 0 24 24"
160
- fill="none"
161
- stroke="currentColor"
162
- stroke-width="2.5"
163
- stroke-linecap="round"
164
- stroke-linejoin="round"
165
- aria-hidden="true"
166
- >
167
- <path d="M5 13l4 4L19 7" />
168
- </svg>
169
- </router-link>
170
- <button
171
- v-else
172
- type="button"
173
- :class="[
174
- 'mint-plugin-switcher__item',
175
- { 'mint-plugin-switcher__item--active': plugin.id === current.id },
176
- ]"
177
- role="menuitem"
178
- @click="handleSelect(plugin)"
93
+ {{ initialsOf(plugin) }}
94
+ </span>
95
+ <span class="mint-plugin-switcher__item-label">{{ plugin.label }}</span>
96
+ <span v-if="plugin.version" class="mint-plugin-switcher__item-version">v{{ plugin.version }}</span>
97
+ <svg
98
+ v-if="plugin.id === current.id"
99
+ class="mint-plugin-switcher__item-check"
100
+ viewBox="0 0 24 24"
101
+ fill="none"
102
+ stroke="currentColor"
103
+ stroke-width="2.5"
104
+ stroke-linecap="round"
105
+ stroke-linejoin="round"
106
+ aria-hidden="true"
179
107
  >
180
- <span
181
- class="mint-plugin-switcher__item-swatch"
182
- :style="{ background: plugin.color ?? 'var(--color-primary)' }"
183
- >
184
- {{ initialsOf(plugin) }}
185
- </span>
186
- <span class="mint-plugin-switcher__item-label">{{ plugin.label }}</span>
187
- <span v-if="plugin.version" class="mint-plugin-switcher__item-version">v{{ plugin.version }}</span>
188
- <svg
189
- v-if="plugin.id === current.id"
190
- class="mint-plugin-switcher__item-check"
191
- viewBox="0 0 24 24"
192
- fill="none"
193
- stroke="currentColor"
194
- stroke-width="2.5"
195
- stroke-linecap="round"
196
- stroke-linejoin="round"
197
- aria-hidden="true"
198
- >
199
- <path d="M5 13l4 4L19 7" />
200
- </svg>
201
- </button>
202
- </template>
108
+ <path d="M5 13l4 4L19 7" />
109
+ </svg>
110
+ </ActionItemInternal>
203
111
 
204
112
  <template v-if="plugins.length && (installHref || installTo || $slots.install)">
205
113
  <div class="mint-plugin-switcher__divider" role="separator" />
206
114
  </template>
207
115
  <slot name="install">
208
- <a
209
- v-if="installHref"
116
+ <ActionItemInternal
117
+ v-if="installHref || installTo"
210
118
  :href="installHref"
211
- class="mint-plugin-switcher__install"
212
- role="menuitem"
213
- @click="handleInstall"
214
- >
215
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
216
- <line x1="12" y1="5" x2="12" y2="19" />
217
- <line x1="5" y1="12" x2="19" y2="12" />
218
- </svg>
219
- <span>{{ installLabel }}</span>
220
- </a>
221
- <router-link
222
- v-else-if="installTo"
223
119
  :to="installTo"
224
120
  class="mint-plugin-switcher__install"
225
121
  role="menuitem"
@@ -230,7 +126,7 @@ onUnmounted(() => {
230
126
  <line x1="5" y1="12" x2="19" y2="12" />
231
127
  </svg>
232
128
  <span>{{ installLabel }}</span>
233
- </router-link>
129
+ </ActionItemInternal>
234
130
  </slot>
235
131
  </div>
236
132
  </div>