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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/README.md +218 -6
  2. package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
  3. package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
  6. package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
  7. package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
  8. package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
  9. package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
  10. package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
  11. package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
  12. package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
  13. package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
  14. package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
  15. package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
  16. package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
  17. package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
  18. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  19. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  20. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  21. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  22. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  23. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  24. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  25. package/dist/__tests__/components/FormCompatibility.test.d.ts +1 -0
  26. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  27. package/dist/__tests__/components/GroupingModal.test.d.ts +1 -0
  28. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  29. package/dist/__tests__/components/PluginIcon.test.d.ts +1 -0
  30. package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
  31. package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
  32. package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
  33. package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
  34. package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
  35. package/dist/__tests__/components/SettingsButton.test.d.ts +1 -0
  36. package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
  37. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  38. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  39. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  40. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  41. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  42. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  43. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  44. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  45. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  46. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  47. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  48. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  49. package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
  50. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  51. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  52. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  53. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  54. package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
  55. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  56. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  57. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  58. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  59. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  60. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  61. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  62. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  63. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  64. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  65. package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
  66. package/dist/auth-QQj2kkze.js.map +1 -0
  67. package/dist/components/ActionItem.vue.d.ts +32 -0
  68. package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
  69. package/dist/components/AppPageSelector.vue.d.ts +3 -6
  70. package/dist/components/AppPillNav.vue.d.ts +2 -2
  71. package/dist/components/AppSidebar.vue.d.ts +56 -3
  72. package/dist/components/AppToastContainer.vue.d.ts +2 -0
  73. package/dist/components/AppTopBar.vue.d.ts +43 -10
  74. package/dist/components/BaseButton.vue.d.ts +2 -2
  75. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  76. package/dist/components/BaseInput.vue.d.ts +2 -2
  77. package/dist/components/BasePill.vue.d.ts +3 -3
  78. package/dist/components/BaseRadioGroup.vue.d.ts +4 -4
  79. package/dist/components/BaseSelect.vue.d.ts +4 -4
  80. package/dist/components/BaseSlider.vue.d.ts +1 -1
  81. package/dist/components/BaseTabs.vue.d.ts +2 -2
  82. package/dist/components/BaseTextarea.vue.d.ts +2 -2
  83. package/dist/components/BaseToggle.vue.d.ts +1 -1
  84. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +117 -0
  85. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +92 -0
  86. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +82 -0
  87. package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
  88. package/dist/components/Breadcrumb.vue.d.ts +2 -2
  89. package/dist/components/Calendar.vue.d.ts +1 -1
  90. package/dist/components/CalendarGridPanel.vue.d.ts +25 -0
  91. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  92. package/dist/components/ColorSlider.vue.d.ts +1 -1
  93. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  94. package/dist/components/ConfirmDialog.vue.d.ts +2 -2
  95. package/dist/components/ControlWorkspaceView.vue.d.ts +130 -0
  96. package/dist/components/DatePicker.vue.d.ts +2 -2
  97. package/dist/components/DateTimePicker.vue.d.ts +3 -3
  98. package/dist/components/DropdownButton.vue.d.ts +4 -4
  99. package/dist/components/EmptyState.vue.d.ts +1 -2
  100. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  101. package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
  102. package/dist/components/FileUploader.vue.d.ts +2 -2
  103. package/dist/components/FitPanel.vue.d.ts +1 -1
  104. package/dist/components/FormActions.vue.d.ts +4 -4
  105. package/dist/components/FormBuilder.vue.d.ts +22 -8
  106. package/dist/components/FormFieldRenderer.vue.d.ts +7 -10
  107. package/dist/components/FormSection.vue.d.ts +11 -24
  108. package/dist/components/FormulaInput.vue.d.ts +2 -2
  109. package/dist/components/IconButton.vue.d.ts +1 -1
  110. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  111. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  112. package/dist/components/MultiSelect.vue.d.ts +3 -3
  113. package/dist/components/NumberInput.vue.d.ts +2 -2
  114. package/dist/components/PluginIcon.vue.d.ts +11 -0
  115. package/dist/components/ProgressBar.vue.d.ts +2 -2
  116. package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
  117. package/dist/components/RackEditor.vue.d.ts +2 -2
  118. package/dist/components/ReagentEditor.vue.d.ts +1 -1
  119. package/dist/components/ResourceCard.vue.d.ts +1 -1
  120. package/dist/components/SampleLegend.vue.d.ts +2 -2
  121. package/dist/components/SampleSelector.vue.d.ts +1 -1
  122. package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
  123. package/dist/components/ScientificNumber.vue.d.ts +1 -1
  124. package/dist/components/SegmentedControl.vue.d.ts +3 -3
  125. package/dist/components/SequenceInput.vue.d.ts +3 -3
  126. package/dist/components/SettingsButton.vue.d.ts +2 -2
  127. package/dist/components/SettingsModal.vue.d.ts +32 -4
  128. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  129. package/dist/components/TagsInput.vue.d.ts +3 -2
  130. package/dist/components/TimePicker.vue.d.ts +3 -3
  131. package/dist/components/TimeRangeInput.vue.d.ts +2 -2
  132. package/dist/components/UnitInput.vue.d.ts +2 -2
  133. package/dist/components/WellPlate.vue.d.ts +8 -8
  134. package/dist/components/index.d.ts +12 -1
  135. package/dist/components/index.js +3 -3
  136. package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +31 -0
  137. package/dist/components/internal/FormSectionRenderer.vue.d.ts +43 -0
  138. package/dist/{components-CzbQQPCb.js → components-D_Sr0adg.js} +9629 -8647
  139. package/dist/components-D_Sr0adg.js.map +1 -0
  140. package/dist/composables/index.d.ts +21 -2
  141. package/dist/composables/index.js +4 -3
  142. package/dist/composables/platformContextHelpers.d.ts +14 -0
  143. package/dist/composables/useBioTemplateComponents.d.ts +20 -0
  144. package/dist/composables/useBioTemplateControls.d.ts +6 -0
  145. package/dist/composables/useBioTemplatePackWorkspace.d.ts +45 -0
  146. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +74 -0
  147. package/dist/composables/useBioTemplateWorkspace.d.ts +50 -0
  148. package/dist/composables/useCalendarGrid.d.ts +26 -0
  149. package/dist/composables/useControlSchema.d.ts +321 -0
  150. package/dist/composables/useDebouncedWatch.d.ts +20 -0
  151. package/dist/composables/useDropdownState.d.ts +19 -0
  152. package/dist/composables/useEventListener.d.ts +13 -0
  153. package/dist/composables/useExpansionSet.d.ts +21 -0
  154. package/dist/composables/useExperimentData.d.ts +10 -0
  155. package/dist/composables/useExperimentSave.d.ts +31 -2
  156. package/dist/composables/useExperimentSelector.d.ts +20 -0
  157. package/dist/composables/useForm.d.ts +2 -0
  158. package/dist/composables/useGroupAssignment.d.ts +31 -0
  159. package/dist/composables/useListSelection.d.ts +35 -0
  160. package/dist/composables/usePlatformContext.d.ts +24 -3
  161. package/dist/composables/usePluginApi.d.ts +7 -14
  162. package/dist/composables/usePluginClient.d.ts +109 -0
  163. package/dist/composables/usePluginConfig.d.ts +12 -0
  164. package/dist/composables/useRequestSyncState.d.ts +34 -0
  165. package/dist/composables/useSampleGroups.d.ts +32 -0
  166. package/dist/composables/useSelectionLimit.d.ts +17 -0
  167. package/dist/composables/useSortedItems.d.ts +32 -0
  168. package/dist/composables/useTemplateCollection.d.ts +58 -0
  169. package/dist/composables/useTextSearch.d.ts +18 -0
  170. package/dist/composables/useTimeUtils.d.ts +8 -0
  171. package/dist/{composables-BXklV5ii.js → composables-C3dpXQN5.js} +228 -146
  172. package/dist/composables-C3dpXQN5.js.map +1 -0
  173. package/dist/index.d.ts +12 -3
  174. package/dist/index.js +6 -5
  175. package/dist/install.d.ts +7 -2
  176. package/dist/install.js +2 -2
  177. package/dist/install.js.map +1 -1
  178. package/dist/stores/auth.d.ts +1 -1
  179. package/dist/stores/index.js +1 -1
  180. package/dist/stores/settings.d.ts +4 -1
  181. package/dist/styles.css +5255 -5654
  182. package/dist/templates/adapters.d.ts +43 -0
  183. package/dist/templates/builders.d.ts +63 -0
  184. package/dist/templates/catalog.d.ts +188 -0
  185. package/dist/templates/componentBindings.d.ts +58 -0
  186. package/dist/templates/controlSchemas.d.ts +25 -0
  187. package/dist/templates/index.d.ts +15 -0
  188. package/dist/templates/index.js +2 -0
  189. package/dist/templates/lookup.d.ts +4 -0
  190. package/dist/templates/packs.d.ts +18 -0
  191. package/dist/templates/presets.d.ts +90 -0
  192. package/dist/templates/types.d.ts +531 -0
  193. package/dist/templates-50NPjaxL.js +9333 -0
  194. package/dist/templates-50NPjaxL.js.map +1 -0
  195. package/dist/types/components.d.ts +62 -1
  196. package/dist/types/form-builder.d.ts +6 -8
  197. package/dist/types/index.d.ts +2 -2
  198. package/dist/types/platform.d.ts +8 -1
  199. package/dist/useScheduleDrag-D4oWdh41.js +4371 -0
  200. package/dist/useScheduleDrag-D4oWdh41.js.map +1 -0
  201. package/dist/utils/formModelSync.d.ts +5 -0
  202. package/dist/utils/items.d.ts +8 -0
  203. package/dist/utils/options.d.ts +6 -0
  204. package/dist/utils/pluginIcon.d.ts +9 -0
  205. package/package.json +7 -2
  206. package/src/__tests__/components/ActionItem.test.ts +99 -0
  207. package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
  208. package/src/__tests__/components/AppPageSelector.test.ts +134 -0
  209. package/src/__tests__/components/AppPillNav.test.ts +78 -0
  210. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  211. package/src/__tests__/components/AppSidebar.test.ts +370 -0
  212. package/src/__tests__/components/AppToastContainer.test.ts +48 -0
  213. package/src/__tests__/components/AppTopBar.test.ts +414 -13
  214. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  215. package/src/__tests__/components/BaseSelect.test.ts +21 -0
  216. package/src/__tests__/components/BaseTabs.test.ts +25 -0
  217. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  218. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +153 -0
  219. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +161 -0
  220. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +281 -0
  221. package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
  222. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  223. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  224. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  225. package/src/__tests__/components/ControlWorkspaceView.test.ts +1031 -0
  226. package/src/__tests__/components/DataFrame.test.ts +11 -0
  227. package/src/__tests__/components/DatePicker.test.ts +45 -0
  228. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  229. package/src/__tests__/components/DropdownButton.test.ts +23 -0
  230. package/src/__tests__/components/EmptyState.test.ts +23 -0
  231. package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
  232. package/src/__tests__/components/FormBuilder.test.ts +296 -0
  233. package/src/__tests__/components/FormCompatibility.test.ts +94 -0
  234. package/src/__tests__/components/GroupAssigner.test.ts +30 -0
  235. package/src/__tests__/components/GroupingModal.test.ts +73 -0
  236. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  237. package/src/__tests__/components/PluginIcon.test.ts +119 -0
  238. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  239. package/src/__tests__/components/ReagentList.test.ts +82 -0
  240. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  241. package/src/__tests__/components/SampleSelector.test.ts +60 -0
  242. package/src/__tests__/components/SegmentedControl.test.ts +24 -0
  243. package/src/__tests__/components/SettingsButton.test.ts +44 -0
  244. package/src/__tests__/components/SettingsModal.test.ts +296 -0
  245. package/src/__tests__/components/TagsInput.test.ts +75 -0
  246. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  247. package/src/__tests__/components/TimePicker.test.ts +38 -0
  248. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +122 -0
  249. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  250. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +99 -0
  251. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  252. package/src/__tests__/composables/useControlSchema.test.ts +919 -0
  253. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  254. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  255. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  256. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  257. package/src/__tests__/composables/useExperimentData.test.ts +4 -0
  258. package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
  259. package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
  260. package/src/__tests__/composables/useForm.test.ts +58 -0
  261. package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
  262. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  263. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  264. package/src/__tests__/composables/usePluginClient.test.ts +444 -0
  265. package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
  266. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  267. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  268. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  269. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  270. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  271. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  272. package/src/__tests__/composables/useTheme.test.ts +91 -0
  273. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  274. package/src/__tests__/docs/frontendDocsCatalog.test.ts +229 -0
  275. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  276. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  277. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  278. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  279. package/src/__tests__/templates/templates.test.ts +1043 -0
  280. package/src/components/ActionItem.vue +82 -0
  281. package/src/components/AppAvatarMenu.vue +15 -69
  282. package/src/components/AppLayout.story.vue +25 -25
  283. package/src/components/AppPageSelector.vue +63 -94
  284. package/src/components/AppPillNav.vue +44 -39
  285. package/src/components/AppPluginSwitcher.vue +41 -145
  286. package/src/components/AppSidebar.story.vue +94 -0
  287. package/src/components/AppSidebar.vue +187 -12
  288. package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
  289. package/src/components/AppToastContainer.vue +62 -0
  290. package/src/components/AppTopBar.story.vue +7 -30
  291. package/src/components/AppTopBar.vue +283 -84
  292. package/src/components/BaseModal.vue +3 -5
  293. package/src/components/BaseRadioGroup.vue +7 -3
  294. package/src/components/BaseSelect.vue +11 -7
  295. package/src/components/BaseTabs.vue +6 -4
  296. package/src/components/BatchProgressList.vue +5 -8
  297. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  298. package/src/components/BioTemplateExperimentWorkspaceView.vue +337 -0
  299. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  300. package/src/components/BioTemplatePackWorkspaceView.vue +176 -0
  301. package/src/components/BioTemplatePresetWorkspaceView.story.vue +151 -0
  302. package/src/components/BioTemplatePresetWorkspaceView.vue +392 -0
  303. package/src/components/BioTemplateRenderer.story.vue +57 -0
  304. package/src/components/BioTemplateRenderer.vue +269 -0
  305. package/src/components/Breadcrumb.vue +14 -8
  306. package/src/components/CalendarGridPanel.vue +120 -0
  307. package/src/components/ConcentrationInput.vue +27 -64
  308. package/src/components/ControlWorkspaceView.story.vue +336 -0
  309. package/src/components/ControlWorkspaceView.vue +347 -0
  310. package/src/components/DataFrame.vue +34 -50
  311. package/src/components/DatePicker.vue +59 -192
  312. package/src/components/DateTimePicker.vue +50 -171
  313. package/src/components/DropdownButton.vue +14 -32
  314. package/src/components/EmptyState.vue +4 -2
  315. package/src/components/ExperimentPopover.vue +5 -22
  316. package/src/components/FormBuilder.vue +124 -27
  317. package/src/components/FormFieldRenderer.vue +15 -38
  318. package/src/components/FormSection.vue +20 -73
  319. package/src/components/GroupAssigner.vue +24 -56
  320. package/src/components/GroupingModal.story.vue +3 -3
  321. package/src/components/GroupingModal.vue +30 -391
  322. package/src/components/MultiSelect.vue +17 -12
  323. package/src/components/PlateMapEditor.vue +3 -8
  324. package/src/components/PluginIcon.story.vue +71 -0
  325. package/src/components/PluginIcon.vue +68 -0
  326. package/src/components/ProtocolStepEditor.vue +13 -22
  327. package/src/components/ReagentList.vue +25 -33
  328. package/src/components/SampleHierarchyTree.vue +12 -23
  329. package/src/components/SampleSelector.vue +42 -122
  330. package/src/components/SegmentedControl.vue +7 -3
  331. package/src/components/SettingsButton.story.vue +1 -1
  332. package/src/components/SettingsButton.vue +15 -27
  333. package/src/components/SettingsModal.story.vue +337 -45
  334. package/src/components/SettingsModal.vue +344 -66
  335. package/src/components/TagsInput.vue +29 -14
  336. package/src/components/ThemeToggle.vue +9 -7
  337. package/src/components/TimePicker.vue +19 -41
  338. package/src/components/ToastNotification.vue +4 -57
  339. package/src/components/Tooltip.vue +7 -12
  340. package/src/components/WellEditPopup.vue +3 -8
  341. package/src/components/WellPlate.vue +4 -10
  342. package/src/components/index.ts +12 -1
  343. package/src/components/internal/FormFieldRendererInternal.vue +50 -0
  344. package/src/components/internal/FormSectionRenderer.vue +78 -0
  345. package/src/composables/index.ts +212 -0
  346. package/src/composables/platformContextHelpers.ts +74 -0
  347. package/src/composables/useBioTemplateComponents.ts +93 -0
  348. package/src/composables/useBioTemplateControls.ts +41 -0
  349. package/src/composables/useBioTemplatePackWorkspace.ts +181 -0
  350. package/src/composables/useBioTemplatePresetWorkspace.ts +337 -0
  351. package/src/composables/useBioTemplateWorkspace.ts +139 -0
  352. package/src/composables/useCalendarGrid.ts +140 -0
  353. package/src/composables/useControlSchema.ts +1274 -0
  354. package/src/composables/useDebouncedWatch.ts +119 -0
  355. package/src/composables/useDropdownState.ts +83 -0
  356. package/src/composables/useEventListener.ts +111 -0
  357. package/src/composables/useExpansionSet.ts +117 -0
  358. package/src/composables/useExperimentData.ts +20 -11
  359. package/src/composables/useExperimentSave.ts +202 -50
  360. package/src/composables/useExperimentSelector.ts +86 -72
  361. package/src/composables/useForm.ts +49 -4
  362. package/src/composables/useFormBuilder.ts +93 -42
  363. package/src/composables/useGroupAssignment.ts +148 -0
  364. package/src/composables/useListSelection.ts +158 -0
  365. package/src/composables/usePluginApi.ts +7 -14
  366. package/src/composables/usePluginClient.ts +425 -0
  367. package/src/composables/usePluginConfig.ts +34 -13
  368. package/src/composables/useRequestSyncState.ts +126 -0
  369. package/src/composables/useSampleGroups.ts +126 -0
  370. package/src/composables/useSelectionLimit.ts +57 -0
  371. package/src/composables/useSortedItems.ts +118 -0
  372. package/src/composables/useTemplateCollection.ts +229 -0
  373. package/src/composables/useTextSearch.ts +60 -0
  374. package/src/composables/useTheme.ts +2 -28
  375. package/src/composables/useTimeUtils.ts +26 -2
  376. package/src/composables/useWellPlateEditor.ts +13 -9
  377. package/src/index.ts +228 -4
  378. package/src/install.ts +11 -4
  379. package/src/stores/settings.ts +13 -9
  380. package/src/styles/components/app-page-selector.css +23 -0
  381. package/src/styles/components/app-pill-nav.css +8 -2
  382. package/src/styles/components/app-top-bar.css +35 -2
  383. package/src/styles/components/button.css +3 -7
  384. package/src/styles/components/concentration-input.css +3 -142
  385. package/src/styles/components/dropdown-button.css +4 -4
  386. package/src/styles/components/empty-state.css +0 -16
  387. package/src/styles/components/input.css +4 -5
  388. package/src/styles/components/number-input.css +3 -3
  389. package/src/styles/components/plugin-icon.css +38 -0
  390. package/src/styles/components/segmented-control.css +4 -7
  391. package/src/styles/components/settings-button.css +3 -66
  392. package/src/styles/components/settings-modal.css +184 -0
  393. package/src/styles/components/tabs.css +1 -2
  394. package/src/styles/components/textarea.css +4 -5
  395. package/src/styles/components/theme-toggle.css +3 -66
  396. package/src/styles/components/unit-input.css +3 -3
  397. package/src/styles/index.css +0 -1
  398. package/src/templates/adapters.ts +785 -0
  399. package/src/templates/builders.ts +2149 -0
  400. package/src/templates/catalog.ts +245 -0
  401. package/src/templates/componentBindings.ts +615 -0
  402. package/src/templates/controlSchemas.ts +718 -0
  403. package/src/templates/index.ts +314 -0
  404. package/src/templates/lookup.ts +18 -0
  405. package/src/templates/packs.ts +156 -0
  406. package/src/templates/presets.ts +146 -0
  407. package/src/templates/types.ts +668 -0
  408. package/src/types/components.ts +80 -1
  409. package/src/types/form-builder.ts +7 -2
  410. package/src/types/index.ts +17 -0
  411. package/src/types/platform.ts +8 -1
  412. package/src/utils/formModelSync.ts +52 -0
  413. package/src/utils/items.ts +28 -0
  414. package/src/utils/options.ts +23 -0
  415. package/src/utils/pluginIcon.ts +30 -0
  416. package/dist/auth-DsI0rQ7_.js.map +0 -1
  417. package/dist/components-CzbQQPCb.js.map +0 -1
  418. package/dist/composables-BXklV5ii.js.map +0 -1
  419. package/dist/useScheduleDrag-CxBeqYcu.js +0 -7181
  420. package/dist/useScheduleDrag-CxBeqYcu.js.map +0 -1
  421. package/src/styles/components/grouping-modal.css +0 -323
@@ -1,18 +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
5
  TopBarPage,
6
+ TopBarPageInput,
6
7
  TopBarTab,
8
+ TopBarTabInput,
7
9
  TopBarTabOption,
10
+ TopBarTabOptionInput,
8
11
  TopBarSettingsConfig,
9
12
  TopBarVariant,
10
13
  PillNavItem,
14
+ PillNavItemInput,
11
15
  PageSelectorItem,
16
+ PageSelectorItemInput,
12
17
  PluginSwitcherInfo,
13
18
  PluginSwitcherPlugin,
14
19
  AccountMenuItem,
20
+ SettingsTab,
15
21
  } from '../types/components'
22
+ import type { PluginNavItem } from '../types/platform'
23
+ import { normalizeItemInput } from '../utils/items'
24
+ import { isPluginIconFormat } from '../utils/pluginIcon'
16
25
  import ThemeToggle from './ThemeToggle.vue'
17
26
  import SettingsModal from './SettingsModal.vue'
18
27
  import ExperimentPopover from './ExperimentPopover.vue'
@@ -21,49 +30,75 @@ import AppPageSelector from './AppPageSelector.vue'
21
30
  import AppPillNav from './AppPillNav.vue'
22
31
  import AppAvatarMenu from './AppAvatarMenu.vue'
23
32
  import AppPluginSwitcher from './AppPluginSwitcher.vue'
33
+ import PluginIcon from './PluginIcon.vue'
24
34
  import { usePlatformContext } from '../composables/usePlatformContext'
25
35
  import { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'
26
-
27
- const SVG_PATH_PREFIX = 'M'
36
+ import { useEventListener } from '../composables/useEventListener'
37
+ import { useDropdownState } from '../composables/useDropdownState'
28
38
 
29
39
  interface Props {
30
- // Classic title & breadcrumb
40
+ /** App or plugin title shown in the left title group when no page selector is present. */
31
41
  title?: string
42
+ /** Secondary title copy shown under the title in title-group layouts. */
32
43
  subtitle?: string
44
+ /** Show the default MINT logo when the icon/logo slot is not provided. */
33
45
  showLogo?: boolean
46
+ /** Top bar visual treatment. */
34
47
  variant?: TopBarVariant
35
- pluginName?: string
36
- pages?: TopBarPage[]
37
- currentPageId?: string
38
- tabs?: TopBarTab[]
39
- currentTabId?: string
48
+ /** Home link used by classic breadcrumb layouts. */
40
49
  homePath?: string
41
50
 
42
- // New layout left page selector / plugin switcher
43
- pageSelector?: PageSelectorItem[]
51
+ /** Preferred route-level page switch entries for plugin and platform pages. */
52
+ pageSelector?: PageSelectorItemInput[]
53
+ /** Active id for the preferred page selector. */
44
54
  currentPageSelectorId?: string
55
+ /** Optional plugin switcher shown in the left navigation position instead of pageSelector. */
45
56
  pluginSwitcher?: PluginSwitcherInfo
46
57
 
47
- // New layout centered pill nav
48
- pillNav?: PillNavItem[]
58
+ /** Preferred centered navigation for local modes inside the current route. */
59
+ pillNav?: PillNavItemInput[]
60
+ /** Active id for the preferred centered pill navigation. */
49
61
  currentPillId?: string
50
62
 
51
- // New right cluster avatar menu + optional notifications
63
+ /** Account dropdown entries. Takes precedence over the classic profile button. */
52
64
  accountMenu?: AccountMenuItem[]
65
+ /** Show the notifications icon button. */
53
66
  showNotifications?: boolean
67
+ /** Draw a notification dot on the notifications icon. */
54
68
  hasNotificationDot?: boolean
55
69
 
56
- // Classic right cluster (still works; AvatarMenu takes precedence over Profile)
70
+ /** Compatibility breadcrumb plugin name. Prefer pageSelector for route-level pages. */
71
+ pluginName?: string
72
+ /** Compatibility page dropdown items. Prefer pageSelector for new route-level navigation. */
73
+ pages?: TopBarPageInput[]
74
+ /** Active id for the compatibility page dropdown. */
75
+ currentPageId?: string
76
+ /** Compatibility center tabs. Prefer pillNav for new in-page modes. */
77
+ tabs?: TopBarTabInput[]
78
+ /** Active id for compatibility center tabs. */
79
+ currentTabId?: string
80
+
81
+ /** Show the theme toggle button. */
57
82
  showThemeToggle?: boolean
83
+ /** Show the settings button and modal. */
58
84
  showSettings?: boolean
85
+ /** Built-in SettingsModal configuration. */
59
86
  settingsConfig?: TopBarSettingsConfig
87
+ /** Show the standalone badge when the plugin is not integrated into the platform. */
60
88
  showStandaloneLabel?: boolean
89
+ /** Custom standalone badge label. */
61
90
  standaloneLabel?: string
91
+ /** Show the classic admin shortcut. */
62
92
  showAdmin?: boolean
93
+ /** Route used by the classic admin shortcut. */
63
94
  adminPath?: string
95
+ /** Show the classic profile button when accountMenu is not provided. */
64
96
  showProfile?: boolean
97
+ /** Classic profile display name. */
65
98
  userName?: string
99
+ /** Explicit classic profile initial. */
66
100
  userInitial?: string
101
+ /** Classic profile email, used by avatar/account layouts. */
67
102
  userEmail?: string
68
103
  }
69
104
 
@@ -96,6 +131,7 @@ const emit = defineEmits<{
96
131
  'account-menu-select': [item: AccountMenuItem]
97
132
  'sign-out': []
98
133
  'notifications-click': []
134
+ 'settings-values-change': [data: Record<string, unknown>]
99
135
  }>()
100
136
 
101
137
  const settingsOpen = ref(false)
@@ -103,10 +139,7 @@ const { isIntegrated, plugin } = usePlatformContext()
103
139
  const isStandalone = computed(() => !isIntegrated.value)
104
140
  const appExperiment = inject(APP_EXPERIMENT_KEY, null)
105
141
 
106
- const pluginIcon = computed(() => {
107
- const icon = plugin.value?.icon
108
- return icon && icon.startsWith(SVG_PATH_PREFIX) ? icon : null
109
- })
142
+ const hasPluginIcon = computed(() => !!plugin.value?.icon)
110
143
 
111
144
  const profileInitial = computed(() => {
112
145
  if (props.userInitial) return props.userInitial
@@ -114,21 +147,22 @@ const profileInitial = computed(() => {
114
147
  return 'U'
115
148
  })
116
149
 
150
+ const hasPlatformPages = computed(() => props.pages === undefined && !!plugin.value?.nav_items?.length)
151
+ const effectivePluginName = computed(() => props.pluginName ?? (hasPlatformPages.value ? plugin.value?.name : '') ?? '')
117
152
  const hasPageSelector = computed(() => !!props.pageSelector?.length)
118
153
  const hasPluginSwitcher = computed(() => !!props.pluginSwitcher)
119
154
  const hasPillNav = computed(() => !!props.pillNav?.length)
120
155
  const hasAccountMenu = computed(() => !!props.accountMenu?.length)
121
156
  const hasLegacyBreadcrumb = computed(
122
- () => !hasPageSelector.value && !hasPluginSwitcher.value && (!!props.pluginName || !!props.pages?.length),
157
+ () => !hasPageSelector.value && !hasPluginSwitcher.value && (!!effectivePluginName.value || normalizedPages.value.length > 0),
123
158
  )
124
159
  const hasTitleGroup = computed(
125
160
  () =>
126
161
  !hasPageSelector.value &&
127
162
  !hasPluginSwitcher.value &&
163
+ !hasLegacyBreadcrumb.value &&
128
164
  !!props.title &&
129
- !!props.subtitle &&
130
- !props.pluginName &&
131
- !props.pages?.length,
165
+ !!props.subtitle
132
166
  )
133
167
  const hasTitleOnly = computed(
134
168
  () =>
@@ -138,25 +172,104 @@ const hasTitleOnly = computed(
138
172
  !hasLegacyBreadcrumb.value &&
139
173
  !!props.title,
140
174
  )
175
+ const normalizedPages = computed<TopBarPage[]>(() =>
176
+ props.pages !== undefined
177
+ ? props.pages.map(normalizeItemInput)
178
+ : plugin.value?.nav_items?.map(pluginNavItemToPage) ?? [],
179
+ )
180
+ const normalizedTabs = computed<TopBarTab[]>(() => props.tabs?.map(normalizeTopBarTabInput) ?? [])
181
+ const normalizedPageSelector = computed<PageSelectorItem[]>(() => props.pageSelector?.map(normalizeItemInput) ?? [])
182
+ const normalizedPillNav = computed<PillNavItem[]>(() => props.pillNav?.map(normalizeItemInput) ?? [])
183
+ const normalizedSettingsTabs = computed<SettingsTab[]>(() =>
184
+ props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],
185
+ )
186
+ const effectiveCurrentPageId = computed(() =>
187
+ props.currentPageId
188
+ ?? (hasPlatformPages.value ? currentPageIdFromLocation(normalizedPages.value) : undefined)
189
+ ?? (hasPlatformPages.value ? normalizedPages.value[0]?.id : undefined),
190
+ )
191
+ const currentLegacyPage = computed(() =>
192
+ normalizedPages.value.find((page) => page.id === effectiveCurrentPageId.value),
193
+ )
194
+ const effectiveTitle = computed(() =>
195
+ props.title ?? (hasPlatformPages.value ? currentLegacyPage.value?.label : undefined),
196
+ )
141
197
 
142
- const showPagesDropdown = ref(false)
143
- const dropdownRef = ref<HTMLElement | null>(null)
198
+ const {
199
+ isOpen: showPagesDropdown,
200
+ rootRef: dropdownRef,
201
+ close: closePagesDropdown,
202
+ toggle: togglePagesDropdownBase,
203
+ } = useDropdownState()
144
204
  const openTabDropdown = ref<string | null>(null)
145
205
  const tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())
146
206
 
147
207
  function togglePagesDropdown() {
148
- showPagesDropdown.value = !showPagesDropdown.value
208
+ togglePagesDropdownBase()
149
209
  openTabDropdown.value = null
150
210
  }
151
211
 
212
+ function normalizeTopBarTabInput(tab: TopBarTabInput): TopBarTab {
213
+ const normalized = normalizeItemInput<Omit<TopBarTab, 'children'> & { children?: TopBarTabOptionInput[] }>(tab)
214
+ return {
215
+ ...normalized,
216
+ children: normalized.children?.map(normalizeItemInput),
217
+ }
218
+ }
219
+
220
+ function isSvgTabIcon(icon?: string): icon is string {
221
+ return !!icon && (icon.startsWith('M') || icon.startsWith('m'))
222
+ }
223
+
224
+ function normalizeNavPath(path?: string): string {
225
+ const raw = path?.trim() || '/'
226
+ const prefixed = raw.startsWith('/') ? raw : `/${raw}`
227
+ return prefixed.replace(/\/+$/, '') || '/'
228
+ }
229
+
230
+ function pageIdFromPath(path: string, fallback: string): string {
231
+ const normalized = normalizeNavPath(path)
232
+ if (normalized === '/') return 'dashboard'
233
+ return normalized.replace(/^\/+/, '').replace(/\/+/g, '-') || fallback
234
+ }
235
+
236
+ function pluginNavItemToPage(item: PluginNavItem, index: number): TopBarPage {
237
+ const path = normalizeNavPath(item.path)
238
+ return {
239
+ id: item.id || pageIdFromPath(path, `page-${index + 1}`),
240
+ label: item.label,
241
+ to: path,
242
+ icon: item.icon || plugin.value?.icon,
243
+ description: item.description,
244
+ }
245
+ }
246
+
247
+ function currentPagePath(): string | undefined {
248
+ if (typeof window === 'undefined') return undefined
249
+ const pathname = normalizeNavPath(window.location.pathname)
250
+ const routePrefix = normalizeNavPath(plugin.value?.route_prefix)
251
+
252
+ if (pathname === routePrefix) return '/'
253
+ if (routePrefix !== '/' && pathname.startsWith(`${routePrefix}/`)) {
254
+ return normalizeNavPath(pathname.slice(routePrefix.length))
255
+ }
256
+ return pathname
257
+ }
258
+
259
+ function currentPageIdFromLocation(pages: TopBarPage[]): string | undefined {
260
+ const path = currentPagePath()
261
+ if (!path) return undefined
262
+ return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id
263
+ }
264
+
152
265
  function handlePageClick(page: TopBarPage) {
153
266
  if (page.disabled) return
154
267
  emit('page-select', page)
155
- showPagesDropdown.value = false
268
+ closePagesDropdown()
156
269
  }
157
270
 
158
271
  function toggleTabDropdown(tabId: string) {
159
- showPagesDropdown.value = false
272
+ closePagesDropdown()
160
273
  openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId
161
274
  }
162
275
 
@@ -187,10 +300,6 @@ function setTabDropdownRef(el: HTMLElement | null, tabId: string) {
187
300
  function handleClickOutside(event: MouseEvent) {
188
301
  const target = event.target as Node
189
302
 
190
- if (showPagesDropdown.value && dropdownRef.value && !dropdownRef.value.contains(target)) {
191
- showPagesDropdown.value = false
192
- }
193
-
194
303
  if (openTabDropdown.value !== null) {
195
304
  const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))
196
305
  if (!clickedInside) {
@@ -199,13 +308,7 @@ function handleClickOutside(event: MouseEvent) {
199
308
  }
200
309
  }
201
310
 
202
- onMounted(() => {
203
- document.addEventListener('click', handleClickOutside)
204
- })
205
-
206
- onUnmounted(() => {
207
- document.removeEventListener('click', handleClickOutside)
208
- })
311
+ useEventListener(() => document, 'click', handleClickOutside)
209
312
  </script>
210
313
 
211
314
  <template>
@@ -224,13 +327,14 @@ onUnmounted(() => {
224
327
  class="mint-topbar-home-link"
225
328
  >
226
329
  <slot name="icon">
227
- <div v-if="pluginIcon" class="mint-topbar__logo">
228
- <div class="mint-topbar__logo-icon">
229
- <svg class="mint-topbar__logo-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
230
- <path :d="pluginIcon" />
231
- </svg>
232
- </div>
233
- </div>
330
+ <PluginIcon
331
+ v-if="hasPluginIcon"
332
+ class="mint-topbar__plugin-icon"
333
+ :icon="plugin!.icon"
334
+ :tone="plugin!.color"
335
+ size="md"
336
+ variant="solid"
337
+ />
234
338
  <slot v-else name="logo">
235
339
  <div v-if="showLogo" class="mint-topbar__logo">
236
340
  <div class="mint-topbar__logo-icon">
@@ -246,13 +350,14 @@ onUnmounted(() => {
246
350
  class="mint-topbar-home-link"
247
351
  >
248
352
  <slot name="icon">
249
- <div v-if="pluginIcon" class="mint-topbar__logo">
250
- <div class="mint-topbar__logo-icon">
251
- <svg class="mint-topbar__logo-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
252
- <path :d="pluginIcon" />
253
- </svg>
254
- </div>
255
- </div>
353
+ <PluginIcon
354
+ v-if="hasPluginIcon"
355
+ class="mint-topbar__plugin-icon"
356
+ :icon="plugin!.icon"
357
+ :tone="plugin!.color"
358
+ size="md"
359
+ variant="solid"
360
+ />
256
361
  <slot v-else name="logo">
257
362
  <div v-if="showLogo" class="mint-topbar__logo">
258
363
  <div class="mint-topbar__logo-icon">
@@ -264,13 +369,14 @@ onUnmounted(() => {
264
369
  </router-link>
265
370
  <template v-else>
266
371
  <slot name="icon">
267
- <div v-if="pluginIcon" class="mint-topbar__logo">
268
- <div class="mint-topbar__logo-icon">
269
- <svg class="mint-topbar__logo-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
270
- <path :d="pluginIcon" />
271
- </svg>
272
- </div>
273
- </div>
372
+ <PluginIcon
373
+ v-if="hasPluginIcon"
374
+ class="mint-topbar__plugin-icon"
375
+ :icon="plugin!.icon"
376
+ :tone="plugin!.color"
377
+ size="md"
378
+ variant="solid"
379
+ />
274
380
  <slot v-else name="logo">
275
381
  <div v-if="showLogo" class="mint-topbar__logo">
276
382
  <div class="mint-topbar__logo-icon">
@@ -293,8 +399,8 @@ onUnmounted(() => {
293
399
  @install-click="emit('plugin-switcher-install')"
294
400
  />
295
401
  <AppPageSelector
296
- v-else-if="hasPageSelector && pageSelector"
297
- :pages="pageSelector"
402
+ v-else-if="hasPageSelector"
403
+ :pages="normalizedPageSelector"
298
404
  :current-page-id="currentPageSelectorId"
299
405
  @select="emit('page-selector-select', $event)"
300
406
  >
@@ -314,12 +420,12 @@ onUnmounted(() => {
314
420
 
315
421
  <div v-else-if="hasLegacyBreadcrumb" ref="dropdownRef" class="mint-topbar-breadcrumb">
316
422
  <button
317
- v-if="pages?.length"
423
+ v-if="normalizedPages.length"
318
424
  type="button"
319
425
  class="mint-topbar-plugin-name"
320
426
  @click.stop="togglePagesDropdown"
321
427
  >
322
- {{ pluginName }}
428
+ {{ effectivePluginName }}
323
429
  <svg
324
430
  class="mint-topbar-chevron"
325
431
  :class="{ 'mint-topbar-chevron--open': showPagesDropdown }"
@@ -335,10 +441,10 @@ onUnmounted(() => {
335
441
  <path d="m6 9 6 6 6-6" />
336
442
  </svg>
337
443
  </button>
338
- <span v-else class="mint-topbar-plugin-name--static">{{ pluginName }}</span>
444
+ <span v-else class="mint-topbar-plugin-name--static">{{ effectivePluginName }}</span>
339
445
 
340
446
  <svg
341
- v-if="title"
447
+ v-if="effectiveTitle"
342
448
  class="mint-topbar-separator"
343
449
  width="16"
344
450
  height="16"
@@ -351,36 +457,69 @@ onUnmounted(() => {
351
457
  >
352
458
  <path d="m9 18 6-6-6-6" />
353
459
  </svg>
354
- <span v-if="title" class="mint-topbar-current-page">{{ title }}</span>
460
+ <span v-if="effectiveTitle" class="mint-topbar-current-page">{{ effectiveTitle }}</span>
355
461
 
356
462
  <div v-show="showPagesDropdown" class="mint-topbar-dropdown">
357
- <template v-for="page in pages" :key="page.id">
463
+ <template v-for="page in normalizedPages" :key="page.id">
358
464
  <a
359
465
  v-if="page.href"
360
466
  :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"
467
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
468
+ @click="closePagesDropdown"
363
469
  >
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>
470
+ <span class="mint-topbar-dropdown-item__page">
471
+ <PluginIcon
472
+ v-if="isPluginIconFormat(page.icon)"
473
+ class="mint-topbar-dropdown-item__icon"
474
+ :icon="page.icon"
475
+ size="sm"
476
+ variant="tinted"
477
+ />
478
+ <span class="mint-topbar-dropdown-item__copy">
479
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
480
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
481
+ </span>
482
+ </span>
366
483
  </a>
367
484
  <router-link
368
485
  v-else-if="page.to"
369
486
  :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"
487
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
488
+ @click="closePagesDropdown"
372
489
  >
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>
490
+ <span class="mint-topbar-dropdown-item__page">
491
+ <PluginIcon
492
+ v-if="isPluginIconFormat(page.icon)"
493
+ class="mint-topbar-dropdown-item__icon"
494
+ :icon="page.icon"
495
+ size="sm"
496
+ variant="tinted"
497
+ />
498
+ <span class="mint-topbar-dropdown-item__copy">
499
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
500
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
501
+ </span>
502
+ </span>
375
503
  </router-link>
376
504
  <button
377
505
  v-else
378
506
  type="button"
379
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
507
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
380
508
  @click="handlePageClick(page)"
381
509
  >
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>
510
+ <span class="mint-topbar-dropdown-item__page">
511
+ <PluginIcon
512
+ v-if="isPluginIconFormat(page.icon)"
513
+ class="mint-topbar-dropdown-item__icon"
514
+ :icon="page.icon"
515
+ size="sm"
516
+ variant="tinted"
517
+ />
518
+ <span class="mint-topbar-dropdown-item__copy">
519
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
520
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
521
+ </span>
522
+ </span>
384
523
  </button>
385
524
  </template>
386
525
  </div>
@@ -396,7 +535,7 @@ onUnmounted(() => {
396
535
  <slot name="center">
397
536
  <AppPillNav
398
537
  v-if="hasPillNav && pillNav"
399
- :items="pillNav"
538
+ :items="normalizedPillNav"
400
539
  :current-item-id="currentPillId"
401
540
  @select="emit('pill-select', $event)"
402
541
  />
@@ -406,9 +545,9 @@ onUnmounted(() => {
406
545
  <!-- Center: classic tabs (when no pillNav) — wrapped in the same centered
407
546
  container as AppPillNav so classic :tabs consumers get centered pill
408
547
  layout without migrating to :pill-nav. -->
409
- <div v-if="!hasPillNav && tabs?.length" class="mint-topbar__center">
548
+ <div v-if="!hasPillNav && normalizedTabs.length" class="mint-topbar__center">
410
549
  <div class="mint-topbar__tabs">
411
- <template v-for="tab in tabs" :key="tab.id">
550
+ <template v-for="tab in normalizedTabs" :key="tab.id">
412
551
  <div
413
552
  :ref="(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null"
414
553
  class="mint-topbar-tab-wrapper"
@@ -423,6 +562,19 @@ onUnmounted(() => {
423
562
  ]"
424
563
  @click.stop="handleTabClick(tab)"
425
564
  >
565
+ <svg
566
+ v-if="isSvgTabIcon(tab.icon)"
567
+ class="mint-topbar-tab-icon"
568
+ viewBox="0 0 24 24"
569
+ fill="none"
570
+ stroke="currentColor"
571
+ stroke-width="2"
572
+ stroke-linecap="round"
573
+ stroke-linejoin="round"
574
+ aria-hidden="true"
575
+ >
576
+ <path :d="tab.icon" />
577
+ </svg>
426
578
  {{ tab.label }}
427
579
  <svg
428
580
  class="mint-topbar-tab-chevron"
@@ -449,6 +601,19 @@ onUnmounted(() => {
449
601
  { 'mint-topbar-tab--disabled': tab.disabled }
450
602
  ]"
451
603
  >
604
+ <svg
605
+ v-if="isSvgTabIcon(tab.icon)"
606
+ class="mint-topbar-tab-icon"
607
+ viewBox="0 0 24 24"
608
+ fill="none"
609
+ stroke="currentColor"
610
+ stroke-width="2"
611
+ stroke-linecap="round"
612
+ stroke-linejoin="round"
613
+ aria-hidden="true"
614
+ >
615
+ <path :d="tab.icon" />
616
+ </svg>
452
617
  {{ tab.label }}
453
618
  </a>
454
619
  <router-link
@@ -460,6 +625,19 @@ onUnmounted(() => {
460
625
  { 'mint-topbar-tab--disabled': tab.disabled }
461
626
  ]"
462
627
  >
628
+ <svg
629
+ v-if="isSvgTabIcon(tab.icon)"
630
+ class="mint-topbar-tab-icon"
631
+ viewBox="0 0 24 24"
632
+ fill="none"
633
+ stroke="currentColor"
634
+ stroke-width="2"
635
+ stroke-linecap="round"
636
+ stroke-linejoin="round"
637
+ aria-hidden="true"
638
+ >
639
+ <path :d="tab.icon" />
640
+ </svg>
463
641
  {{ tab.label }}
464
642
  </router-link>
465
643
  <button
@@ -472,6 +650,19 @@ onUnmounted(() => {
472
650
  ]"
473
651
  @click="handleTabClick(tab)"
474
652
  >
653
+ <svg
654
+ v-if="isSvgTabIcon(tab.icon)"
655
+ class="mint-topbar-tab-icon"
656
+ viewBox="0 0 24 24"
657
+ fill="none"
658
+ stroke="currentColor"
659
+ stroke-width="2"
660
+ stroke-linecap="round"
661
+ stroke-linejoin="round"
662
+ aria-hidden="true"
663
+ >
664
+ <path :d="tab.icon" />
665
+ </svg>
475
666
  {{ tab.label }}
476
667
  </button>
477
668
 
@@ -634,11 +825,19 @@ onUnmounted(() => {
634
825
  v-if="showSettings"
635
826
  v-model="settingsOpen"
636
827
  :title="settingsConfig?.title"
637
- :tabs="settingsConfig?.tabs"
828
+ :tabs="normalizedSettingsTabs"
638
829
  :show-appearance="settingsConfig?.showAppearance ?? true"
639
830
  :size="settingsConfig?.size"
831
+ :layout="settingsConfig?.layout"
832
+ :schema="settingsConfig?.schema"
833
+ :model="settingsConfig?.model"
834
+ :controls="settingsConfig?.controls"
835
+ :control-options="settingsConfig?.controlOptions"
836
+ :values="settingsConfig?.values"
837
+ :enhancements="settingsConfig?.enhancements"
838
+ @update:values="emit('settings-values-change', $event)"
640
839
  >
641
- <template v-for="tab in (settingsConfig?.tabs ?? [])" :key="tab.id" #[`tab-${tab.id}`]>
840
+ <template v-for="tab in normalizedSettingsTabs" :key="tab.id" #[`tab-${tab.id}`]>
642
841
  <slot :name="`settings-tab-${tab.id}`" />
643
842
  </template>
644
843
  <template #appearance>
@@ -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',