@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,24 +1,27 @@
1
1
  <script setup lang="ts">
2
2
  /** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */
3
- import { ref, computed, inject, onMounted, onUnmounted } from 'vue'
3
+ import { ref, computed, inject } from 'vue'
4
4
  import type {
5
- TopBarPage,
6
- TopBarTab,
7
- TopBarTabOption,
5
+ PillNavOption,
8
6
  TopBarSettingsConfig,
9
7
  TopBarVariant,
10
8
  PillNavItem,
9
+ PillNavItemInput,
11
10
  PageSelectorItem,
11
+ PageSelectorItemInput,
12
12
  PluginSwitcherInfo,
13
13
  PluginSwitcherPlugin,
14
14
  AccountMenuItem,
15
+ SettingsTab,
15
16
  } from '../types/components'
17
+ import type { PluginNavItem } from '../types/platform'
18
+ import { normalizeItemInput } from '../utils/items'
16
19
  import ThemeToggle from './ThemeToggle.vue'
17
20
  import SettingsModal from './SettingsModal.vue'
18
21
  import ExperimentPopover from './ExperimentPopover.vue'
19
22
  import ExperimentSelectorModal from './ExperimentSelectorModal.vue'
20
- import AppPageSelector from './AppPageSelector.vue'
21
- import AppPillNav from './AppPillNav.vue'
23
+ import AppPageSelectorInternal from './internal/AppPageSelectorInternal.vue'
24
+ import AppPillNavInternal from './internal/AppPillNavInternal.vue'
22
25
  import AppAvatarMenu from './AppAvatarMenu.vue'
23
26
  import AppPluginSwitcher from './AppPluginSwitcher.vue'
24
27
  import PluginIcon from './PluginIcon.vue'
@@ -26,43 +29,57 @@ import { usePlatformContext } from '../composables/usePlatformContext'
26
29
  import { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'
27
30
 
28
31
  interface Props {
29
- // Classic title & breadcrumb
32
+ /** App or plugin title shown in the left title group when no page selector is present. */
30
33
  title?: string
34
+ /** Secondary title copy shown under the title in title-group layouts. */
31
35
  subtitle?: string
36
+ /** Show the default MINT logo when the icon/logo slot is not provided. */
32
37
  showLogo?: boolean
38
+ /** Top bar visual treatment. */
33
39
  variant?: TopBarVariant
34
- pluginName?: string
35
- pages?: TopBarPage[]
36
- currentPageId?: string
37
- tabs?: TopBarTab[]
38
- currentTabId?: string
40
+ /** Home link used by the brand icon. */
39
41
  homePath?: string
40
42
 
41
- // New layout left page selector / plugin switcher
42
- pageSelector?: PageSelectorItem[]
43
+ /** Preferred route-level page switch entries for plugin and platform pages. Integrated plugins read platform plugin.nav_items metadata automatically when pageSelector is omitted. */
44
+ pageSelector?: PageSelectorItemInput[]
45
+ /** Active id for the preferred page selector. */
43
46
  currentPageSelectorId?: string
47
+ /** Optional plugin switcher shown in the left navigation position instead of pageSelector. */
44
48
  pluginSwitcher?: PluginSwitcherInfo
45
49
 
46
- // New layout centered pill nav
47
- pillNav?: PillNavItem[]
50
+ /** Preferred centered navigation for local modes inside the current route. */
51
+ pillNav?: PillNavItemInput[]
52
+ /** Active id for the preferred centered pill navigation. */
48
53
  currentPillId?: string
49
54
 
50
- // New right cluster avatar menu + optional notifications
55
+ /** Account dropdown entries. Takes precedence over the classic profile button. */
51
56
  accountMenu?: AccountMenuItem[]
57
+ /** Show the notifications icon button. */
52
58
  showNotifications?: boolean
59
+ /** Draw a notification dot on the notifications icon. */
53
60
  hasNotificationDot?: boolean
54
61
 
55
- // Classic right cluster (still works; AvatarMenu takes precedence over Profile)
62
+ /** Show the theme toggle button. */
56
63
  showThemeToggle?: boolean
64
+ /** Show the settings button and modal. */
57
65
  showSettings?: boolean
66
+ /** Built-in SettingsModal configuration. */
58
67
  settingsConfig?: TopBarSettingsConfig
68
+ /** Show the standalone badge when the plugin is not integrated into the platform. */
59
69
  showStandaloneLabel?: boolean
70
+ /** Custom standalone badge label. */
60
71
  standaloneLabel?: string
72
+ /** Show the classic admin shortcut. */
61
73
  showAdmin?: boolean
74
+ /** Route used by the classic admin shortcut. */
62
75
  adminPath?: string
76
+ /** Show the classic profile button when accountMenu is not provided. */
63
77
  showProfile?: boolean
78
+ /** Classic profile display name. */
64
79
  userName?: string
80
+ /** Explicit classic profile initial. */
65
81
  userInitial?: string
82
+ /** Classic profile email, used by avatar/account layouts. */
66
83
  userEmail?: string
67
84
  }
68
85
 
@@ -82,14 +99,11 @@ const props = withDefaults(defineProps<Props>(), {
82
99
  })
83
100
 
84
101
  const emit = defineEmits<{
85
- 'page-select': [page: TopBarPage]
86
- 'tab-select': [tab: TopBarTab]
87
- 'tab-option-select': [option: TopBarTabOption, tab: TopBarTab]
88
102
  'profile-click': []
89
103
  'admin-click': []
90
- // New
91
104
  'page-selector-select': [page: PageSelectorItem]
92
105
  'pill-select': [item: PillNavItem]
106
+ 'pill-option-select': [option: PillNavOption, item: PillNavItem]
93
107
  'plugin-switcher-select': [plugin: PluginSwitcherPlugin]
94
108
  'plugin-switcher-install': []
95
109
  'account-menu-select': [item: AccountMenuItem]
@@ -111,98 +125,87 @@ const profileInitial = computed(() => {
111
125
  return 'U'
112
126
  })
113
127
 
114
- const hasPageSelector = computed(() => !!props.pageSelector?.length)
128
+ const hasPlatformPageSelector = computed(() =>
129
+ props.pageSelector === undefined && !!plugin.value?.nav_items?.length,
130
+ )
131
+ const hasPageSelector = computed(() => !!props.pageSelector?.length || hasPlatformPageSelector.value)
115
132
  const hasPluginSwitcher = computed(() => !!props.pluginSwitcher)
116
133
  const hasPillNav = computed(() => !!props.pillNav?.length)
117
134
  const hasAccountMenu = computed(() => !!props.accountMenu?.length)
118
- const hasLegacyBreadcrumb = computed(
119
- () => !hasPageSelector.value && !hasPluginSwitcher.value && (!!props.pluginName || !!props.pages?.length),
120
- )
121
135
  const hasTitleGroup = computed(
122
136
  () =>
123
137
  !hasPageSelector.value &&
124
138
  !hasPluginSwitcher.value &&
125
139
  !!props.title &&
126
- !!props.subtitle &&
127
- !props.pluginName &&
128
- !props.pages?.length,
140
+ !!props.subtitle
129
141
  )
130
142
  const hasTitleOnly = computed(
131
143
  () =>
132
144
  !hasPageSelector.value &&
133
145
  !hasPluginSwitcher.value &&
134
146
  !hasTitleGroup.value &&
135
- !hasLegacyBreadcrumb.value &&
136
147
  !!props.title,
137
148
  )
149
+ const platformPageSelector = computed<PageSelectorItem[]>(() =>
150
+ hasPlatformPageSelector.value
151
+ ? plugin.value?.nav_items?.map(pluginNavItemToPageSelectorItem) ?? []
152
+ : [],
153
+ )
154
+ const normalizedPageSelector = computed<PageSelectorItem[]>(() =>
155
+ props.pageSelector !== undefined
156
+ ? props.pageSelector.map(normalizeItemInput)
157
+ : platformPageSelector.value,
158
+ )
159
+ const normalizedPillNav = computed<PillNavItemInput[]>(() => props.pillNav ?? [])
160
+ const normalizedSettingsTabs = computed<SettingsTab[]>(() =>
161
+ props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],
162
+ )
163
+ const effectiveCurrentPageSelectorId = computed(() =>
164
+ props.currentPageSelectorId
165
+ ?? (hasPlatformPageSelector.value ? currentItemIdFromLocation(normalizedPageSelector.value) : undefined)
166
+ ?? (hasPlatformPageSelector.value ? normalizedPageSelector.value[0]?.id : undefined),
167
+ )
138
168
 
139
- const showPagesDropdown = ref(false)
140
- const dropdownRef = ref<HTMLElement | null>(null)
141
- const openTabDropdown = ref<string | null>(null)
142
- const tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())
143
-
144
- function togglePagesDropdown() {
145
- showPagesDropdown.value = !showPagesDropdown.value
146
- openTabDropdown.value = null
147
- }
148
-
149
- function handlePageClick(page: TopBarPage) {
150
- if (page.disabled) return
151
- emit('page-select', page)
152
- showPagesDropdown.value = false
169
+ function normalizeNavPath(path?: string): string {
170
+ const raw = path?.trim() || '/'
171
+ const prefixed = raw.startsWith('/') ? raw : `/${raw}`
172
+ return prefixed.replace(/\/+$/, '') || '/'
153
173
  }
154
174
 
155
- function toggleTabDropdown(tabId: string) {
156
- showPagesDropdown.value = false
157
- openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId
175
+ function pageIdFromPath(path: string, fallback: string): string {
176
+ const normalized = normalizeNavPath(path)
177
+ if (normalized === '/') return 'dashboard'
178
+ return normalized.replace(/^\/+/, '').replace(/\/+/g, '-') || fallback
158
179
  }
159
180
 
160
- function handleTabClick(tab: TopBarTab) {
161
- if (tab.disabled) return
162
- if (tab.children?.length) {
163
- toggleTabDropdown(tab.id)
164
- } else {
165
- emit('tab-select', tab)
166
- openTabDropdown.value = null
181
+ function pluginNavItemToPageSelectorItem(item: PluginNavItem, index: number): PageSelectorItem {
182
+ const path = normalizeNavPath(item.path)
183
+ return {
184
+ id: item.id || pageIdFromPath(path, `page-${index + 1}`),
185
+ label: item.label,
186
+ to: path,
187
+ icon: item.icon || plugin.value?.icon,
188
+ hint: item.description || plugin.value?.name,
167
189
  }
168
190
  }
169
191
 
170
- function handleTabOptionClick(option: TopBarTabOption, tab: TopBarTab) {
171
- if (option.disabled) return
172
- emit('tab-option-select', option, tab)
173
- openTabDropdown.value = null
174
- }
192
+ function currentPagePath(): string | undefined {
193
+ if (typeof window === 'undefined') return undefined
194
+ const pathname = normalizeNavPath(window.location.pathname)
195
+ const routePrefix = normalizeNavPath(plugin.value?.route_prefix)
175
196
 
176
- function setTabDropdownRef(el: HTMLElement | null, tabId: string) {
177
- if (el) {
178
- tabDropdownRefs.value.set(tabId, el)
179
- } else {
180
- tabDropdownRefs.value.delete(tabId)
197
+ if (pathname === routePrefix) return '/'
198
+ if (routePrefix !== '/' && pathname.startsWith(`${routePrefix}/`)) {
199
+ return normalizeNavPath(pathname.slice(routePrefix.length))
181
200
  }
201
+ return pathname
182
202
  }
183
203
 
184
- function handleClickOutside(event: MouseEvent) {
185
- const target = event.target as Node
186
-
187
- if (showPagesDropdown.value && dropdownRef.value && !dropdownRef.value.contains(target)) {
188
- showPagesDropdown.value = false
189
- }
190
-
191
- if (openTabDropdown.value !== null) {
192
- const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))
193
- if (!clickedInside) {
194
- openTabDropdown.value = null
195
- }
196
- }
204
+ function currentItemIdFromLocation(pages: Array<Pick<PageSelectorItem, 'id' | 'to' | 'href'>>): string | undefined {
205
+ const path = currentPagePath()
206
+ if (!path) return undefined
207
+ return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id
197
208
  }
198
-
199
- onMounted(() => {
200
- document.addEventListener('click', handleClickOutside)
201
- })
202
-
203
- onUnmounted(() => {
204
- document.removeEventListener('click', handleClickOutside)
205
- })
206
209
  </script>
207
210
 
208
211
  <template>
@@ -292,10 +295,10 @@ onUnmounted(() => {
292
295
  @select="emit('plugin-switcher-select', $event)"
293
296
  @install-click="emit('plugin-switcher-install')"
294
297
  />
295
- <AppPageSelector
296
- v-else-if="hasPageSelector && pageSelector"
297
- :pages="pageSelector"
298
- :current-page-id="currentPageSelectorId"
298
+ <AppPageSelectorInternal
299
+ v-else-if="hasPageSelector"
300
+ :pages="normalizedPageSelector"
301
+ :current-page-id="effectiveCurrentPageSelectorId"
299
302
  @select="emit('page-selector-select', $event)"
300
303
  >
301
304
  <template v-if="$slots['page-selector-icon']" #icon="slotProps">
@@ -304,88 +307,14 @@ onUnmounted(() => {
304
307
  <template v-if="$slots['page-selector-item-icon']" #item-icon="slotProps">
305
308
  <slot name="page-selector-item-icon" v-bind="slotProps" />
306
309
  </template>
307
- </AppPageSelector>
310
+ </AppPageSelectorInternal>
308
311
 
309
- <!-- Left: classic title / breadcrumb (classic prop surface) -->
312
+ <!-- Left: title -->
310
313
  <div v-if="hasTitleGroup" class="mint-topbar-title-group">
311
314
  <span class="mint-topbar-title">{{ title }}</span>
312
315
  <span class="mint-topbar-subtitle">{{ subtitle }}</span>
313
316
  </div>
314
317
 
315
- <div v-else-if="hasLegacyBreadcrumb" ref="dropdownRef" class="mint-topbar-breadcrumb">
316
- <button
317
- v-if="pages?.length"
318
- type="button"
319
- class="mint-topbar-plugin-name"
320
- @click.stop="togglePagesDropdown"
321
- >
322
- {{ pluginName }}
323
- <svg
324
- class="mint-topbar-chevron"
325
- :class="{ 'mint-topbar-chevron--open': showPagesDropdown }"
326
- width="16"
327
- height="16"
328
- viewBox="0 0 24 24"
329
- fill="none"
330
- stroke="currentColor"
331
- stroke-width="2"
332
- stroke-linecap="round"
333
- stroke-linejoin="round"
334
- >
335
- <path d="m6 9 6 6 6-6" />
336
- </svg>
337
- </button>
338
- <span v-else class="mint-topbar-plugin-name--static">{{ pluginName }}</span>
339
-
340
- <svg
341
- v-if="title"
342
- class="mint-topbar-separator"
343
- width="16"
344
- height="16"
345
- viewBox="0 0 24 24"
346
- fill="none"
347
- stroke="currentColor"
348
- stroke-width="2"
349
- stroke-linecap="round"
350
- stroke-linejoin="round"
351
- >
352
- <path d="m9 18 6-6-6-6" />
353
- </svg>
354
- <span v-if="title" class="mint-topbar-current-page">{{ title }}</span>
355
-
356
- <div v-show="showPagesDropdown" class="mint-topbar-dropdown">
357
- <template v-for="page in pages" :key="page.id">
358
- <a
359
- v-if="page.href"
360
- :href="page.href"
361
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
362
- @click="showPagesDropdown = false"
363
- >
364
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
365
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
366
- </a>
367
- <router-link
368
- v-else-if="page.to"
369
- :to="page.to"
370
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
371
- @click="showPagesDropdown = false"
372
- >
373
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
374
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
375
- </router-link>
376
- <button
377
- v-else
378
- type="button"
379
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
380
- @click="handlePageClick(page)"
381
- >
382
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
383
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
384
- </button>
385
- </template>
386
- </div>
387
- </div>
388
-
389
318
  <span v-else-if="hasTitleOnly" class="mint-topbar__title-only">{{ title }}</span>
390
319
 
391
320
  <!-- Nav slot (inline, after brand/selector) -->
@@ -394,135 +323,16 @@ onUnmounted(() => {
394
323
  <!-- Center: pill nav (new) -->
395
324
  <div v-if="hasPillNav || $slots.center" class="mint-topbar__center">
396
325
  <slot name="center">
397
- <AppPillNav
326
+ <AppPillNavInternal
398
327
  v-if="hasPillNav && pillNav"
399
- :items="pillNav"
328
+ :items="normalizedPillNav"
400
329
  :current-item-id="currentPillId"
401
330
  @select="emit('pill-select', $event)"
331
+ @option-select="(option, item) => emit('pill-option-select', option, item)"
402
332
  />
403
333
  </slot>
404
334
  </div>
405
335
 
406
- <!-- Center: classic tabs (when no pillNav) — wrapped in the same centered
407
- container as AppPillNav so classic :tabs consumers get centered pill
408
- layout without migrating to :pill-nav. -->
409
- <div v-if="!hasPillNav && tabs?.length" class="mint-topbar__center">
410
- <div class="mint-topbar__tabs">
411
- <template v-for="tab in tabs" :key="tab.id">
412
- <div
413
- :ref="(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null"
414
- class="mint-topbar-tab-wrapper"
415
- >
416
- <button
417
- v-if="tab.children?.length"
418
- type="button"
419
- :class="[
420
- 'mint-topbar-tab',
421
- { 'mint-topbar-tab--active': tab.id === currentTabId || tab.children.some(c => c.id === currentTabId) },
422
- { 'mint-topbar-tab--disabled': tab.disabled }
423
- ]"
424
- @click.stop="handleTabClick(tab)"
425
- >
426
- {{ tab.label }}
427
- <svg
428
- class="mint-topbar-tab-chevron"
429
- :class="{ 'mint-topbar-tab-chevron--open': openTabDropdown === tab.id }"
430
- width="14"
431
- height="14"
432
- viewBox="0 0 24 24"
433
- fill="none"
434
- stroke="currentColor"
435
- stroke-width="2"
436
- stroke-linecap="round"
437
- stroke-linejoin="round"
438
- >
439
- <path d="m6 9 6 6 6-6" />
440
- </svg>
441
- </button>
442
-
443
- <a
444
- v-else-if="tab.href"
445
- :href="tab.href"
446
- :class="[
447
- 'mint-topbar-tab',
448
- { 'mint-topbar-tab--active': tab.id === currentTabId },
449
- { 'mint-topbar-tab--disabled': tab.disabled }
450
- ]"
451
- >
452
- {{ tab.label }}
453
- </a>
454
- <router-link
455
- v-else-if="tab.to"
456
- :to="tab.to"
457
- :class="[
458
- 'mint-topbar-tab',
459
- { 'mint-topbar-tab--active': tab.id === currentTabId },
460
- { 'mint-topbar-tab--disabled': tab.disabled }
461
- ]"
462
- >
463
- {{ tab.label }}
464
- </router-link>
465
- <button
466
- v-else
467
- type="button"
468
- :class="[
469
- 'mint-topbar-tab',
470
- { 'mint-topbar-tab--active': tab.id === currentTabId },
471
- { 'mint-topbar-tab--disabled': tab.disabled }
472
- ]"
473
- @click="handleTabClick(tab)"
474
- >
475
- {{ tab.label }}
476
- </button>
477
-
478
- <div v-if="tab.children?.length" v-show="openTabDropdown === tab.id" class="mint-topbar-tab-dropdown">
479
- <template v-for="option in tab.children" :key="option.id">
480
- <a
481
- v-if="option.href"
482
- :href="option.href"
483
- :class="[
484
- 'mint-topbar-dropdown-item',
485
- { 'mint-topbar-dropdown-item--active': option.id === currentTabId },
486
- { 'mint-topbar-dropdown-item--disabled': option.disabled }
487
- ]"
488
- @click="openTabDropdown = null"
489
- >
490
- <span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
491
- <span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
492
- </a>
493
- <router-link
494
- v-else-if="option.to"
495
- :to="option.to"
496
- :class="[
497
- 'mint-topbar-dropdown-item',
498
- { 'mint-topbar-dropdown-item--active': option.id === currentTabId },
499
- { 'mint-topbar-dropdown-item--disabled': option.disabled }
500
- ]"
501
- @click="openTabDropdown = null"
502
- >
503
- <span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
504
- <span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
505
- </router-link>
506
- <button
507
- v-else
508
- type="button"
509
- :class="[
510
- 'mint-topbar-dropdown-item',
511
- { 'mint-topbar-dropdown-item--active': option.id === currentTabId },
512
- { 'mint-topbar-dropdown-item--disabled': option.disabled }
513
- ]"
514
- @click="handleTabOptionClick(option, tab)"
515
- >
516
- <span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
517
- <span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
518
- </button>
519
- </template>
520
- </div>
521
- </div>
522
- </template>
523
- </div>
524
- </div>
525
-
526
336
  <!-- Right section -->
527
337
  <div class="mint-topbar__right">
528
338
  <span v-if="showStandaloneLabel && isStandalone && !appExperiment" class="mint-topbar__standalone-badge">
@@ -531,15 +341,7 @@ onUnmounted(() => {
531
341
 
532
342
  <ExperimentPopover
533
343
  v-if="appExperiment && !isStandalone"
534
- :experiment-name="appExperiment.experimentName.value"
535
- :experiment-code="appExperiment.experimentCode.value"
536
- :experiment-status="appExperiment.experimentStatus.value"
537
- :show-save="appExperiment.showSave.value"
538
- :show-detach="appExperiment.showDetach.value"
539
- :save-disabled="appExperiment.saveDisabled.value"
540
- :save-disabled-message="appExperiment.saveDisabledMessage.value"
541
- :save-loading="appExperiment.saveLoading.value"
542
- :save-success-message="appExperiment.saveSuccessMessage.value"
344
+ v-bind="appExperiment.popover.value"
543
345
  @select="appExperiment.openModal()"
544
346
  @save="appExperiment.handleSave()"
545
347
  @detach="appExperiment.handleDetach()"
@@ -634,16 +436,19 @@ onUnmounted(() => {
634
436
  v-if="showSettings"
635
437
  v-model="settingsOpen"
636
438
  :title="settingsConfig?.title"
637
- :tabs="settingsConfig?.tabs"
439
+ :tabs="normalizedSettingsTabs"
638
440
  :show-appearance="settingsConfig?.showAppearance ?? true"
639
441
  :size="settingsConfig?.size"
640
442
  :layout="settingsConfig?.layout"
641
443
  :schema="settingsConfig?.schema"
444
+ :model="settingsConfig?.model"
445
+ :controls="settingsConfig?.controls"
446
+ :control-options="settingsConfig?.controlOptions"
642
447
  :values="settingsConfig?.values"
643
448
  :enhancements="settingsConfig?.enhancements"
644
449
  @update:values="emit('settings-values-change', $event)"
645
450
  >
646
- <template v-for="tab in (settingsConfig?.tabs ?? [])" :key="tab.id" #[`tab-${tab.id}`]>
451
+ <template v-for="tab in normalizedSettingsTabs" :key="tab.id" #[`tab-${tab.id}`]>
647
452
  <slot :name="`settings-tab-${tab.id}`" />
648
453
  </template>
649
454
  <template #appearance>
@@ -653,8 +458,7 @@ onUnmounted(() => {
653
458
 
654
459
  <ExperimentSelectorModal
655
460
  v-if="appExperiment && !isStandalone"
656
- :model-value="appExperiment.showModal.value"
657
- :current-experiment-id="appExperiment.experimentId.value"
461
+ v-bind="appExperiment.selectorModal.value"
658
462
  @update:model-value="$event ? appExperiment.openModal() : appExperiment.closeModal()"
659
463
  @select="appExperiment.handleSelect($event)"
660
464
  @deselect="appExperiment.handleDetach()"
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Modal dialog with backdrop, focus trap, Escape-to-close, and configurable size/variant. */
3
- import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
3
+ import { ref, watch, nextTick, onUnmounted } from 'vue'
4
4
  import type { ModalSize, ModalVariant } from '../types'
5
+ import { useEventListener } from '../composables/useEventListener'
5
6
 
6
7
  interface Props {
7
8
  modelValue: boolean
@@ -101,12 +102,9 @@ watch(() => props.modelValue, async (isOpen) => {
101
102
  }
102
103
  })
103
104
 
104
- onMounted(() => {
105
- document.addEventListener('keydown', handleKeydown)
106
- })
105
+ useEventListener(() => document, 'keydown', handleKeydown)
107
106
 
108
107
  onUnmounted(() => {
109
- document.removeEventListener('keydown', handleKeydown)
110
108
  document.body.style.overflow = ''
111
109
  })
112
110
  </script>
@@ -1,10 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  /** Renders a group of radio buttons in list or tile layout, horizontal or vertical. */
3
- import type { RadioOption } from '../types'
3
+ import { computed } from 'vue'
4
+ import type { RadioOption, RadioOptionInput } from '../types'
5
+ import { normalizeOptionInput } from '../utils/options'
4
6
 
5
7
  interface Props {
6
8
  modelValue?: string | number
7
- options: RadioOption[]
9
+ options: RadioOptionInput[]
8
10
  name: string
9
11
  disabled?: boolean
10
12
  direction?: 'horizontal' | 'vertical'
@@ -23,6 +25,8 @@ const emit = defineEmits<{
23
25
  'update:modelValue': [value: string | number]
24
26
  }>()
25
27
 
28
+ const normalizedOptions = computed<RadioOption[]>(() => props.options.map(normalizeOptionInput))
29
+
26
30
  function handleChange(value: string | number) {
27
31
  emit('update:modelValue', value)
28
32
  }
@@ -34,7 +38,7 @@ function handleChange(value: string | number) {
34
38
  role="radiogroup"
35
39
  >
36
40
  <label
37
- v-for="option in props.options"
41
+ v-for="option in normalizedOptions"
38
42
  :key="String(option.value)"
39
43
  :class="[
40
44
  'mint-radio-option',
@@ -1,10 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  /** Renders a native `<select>` element with size, error, and disabled states. */
3
- import type { SelectOption } from '../types'
3
+ import { computed } from 'vue'
4
+ import type { SelectOption, SelectOptionInput } from '../types'
5
+ import { findOptionByDomValue, normalizeOptionInput } from '../utils/options'
4
6
 
5
7
  interface Props {
6
8
  modelValue?: string | number
7
- options: SelectOption<string | number>[]
9
+ options: SelectOptionInput<string | number>[]
8
10
  placeholder?: string
9
11
  disabled?: boolean
10
12
  error?: boolean
@@ -22,12 +24,14 @@ const emit = defineEmits<{
22
24
  'update:modelValue': [value: string | number]
23
25
  }>()
24
26
 
27
+ const normalizedOptions = computed<SelectOption<string | number>[]>(() =>
28
+ props.options.map(normalizeOptionInput)
29
+ )
30
+
25
31
  function handleChange(event: Event) {
26
32
  const target = event.target as HTMLSelectElement
27
- const value = typeof props.modelValue === 'number'
28
- ? Number(target.value)
29
- : target.value
30
- emit('update:modelValue', value)
33
+ const option = findOptionByDomValue(normalizedOptions.value, target.value)
34
+ emit('update:modelValue', option?.value ?? target.value)
31
35
  }
32
36
  </script>
33
37
 
@@ -50,7 +54,7 @@ function handleChange(event: Event) {
50
54
  {{ placeholder }}
51
55
  </option>
52
56
  <option
53
- v-for="option in options"
57
+ v-for="option in normalizedOptions"
54
58
  :key="String(option.value)"
55
59
  :value="option.value"
56
60
  :disabled="option.disabled"