@morscherlab/mint-sdk 1.2.0-beta.2 → 1.2.0-beta.20

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 (493) hide show
  1. package/README.md +65 -5
  2. package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-CJdNlCZb.js} +24 -15
  3. package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
  5. package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
  6. package/dist/{SettingsModal-BgKY93zh.js → SettingsModal-BYR20I4c.js} +5 -8
  7. package/dist/SettingsModal-BYR20I4c.js.map +1 -0
  8. package/dist/{EmptyState-p90quq8Z.js → Skeleton-BAF3CKY7.js} +111 -93
  9. package/dist/Skeleton-BAF3CKY7.js.map +1 -0
  10. package/dist/__tests__/components/DataStates.test.d.ts +1 -0
  11. package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
  12. package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
  13. package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
  14. package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
  15. package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
  16. package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
  17. package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
  18. package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
  19. package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
  20. package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
  21. package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
  22. package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
  23. package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -0
  24. package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
  25. package/dist/apiErrors.d.ts +26 -0
  26. package/dist/components/ActionMenu.vue.d.ts +1 -1
  27. package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
  28. package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
  29. package/dist/components/AppSidebar.vue.d.ts +1 -1
  30. package/dist/components/AuditTrail.vue.d.ts +13 -0
  31. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  32. package/dist/components/BaseInput.vue.d.ts +1 -1
  33. package/dist/components/BaseSelect.vue.d.ts +1 -1
  34. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  35. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  36. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +4 -4
  37. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
  38. package/dist/components/ConcentrationInput.vue.d.ts +1 -1
  39. package/dist/components/DataFrame.vue.d.ts +14 -2
  40. package/dist/components/DatePicker.vue.d.ts +2 -2
  41. package/dist/components/DateTimePicker.vue.d.ts +3 -1
  42. package/dist/components/DropdownButton.vue.d.ts +2 -0
  43. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  44. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  45. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  46. package/dist/components/FileBrowserModal.vue.d.ts +99 -0
  47. package/dist/components/FilePicker.vue.d.ts +32 -0
  48. package/dist/components/FileUploader.vue.d.ts +5 -2
  49. package/dist/components/FitPanel.vue.d.ts +1 -1
  50. package/dist/components/FormulaInput.vue.d.ts +1 -1
  51. package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
  52. package/dist/components/JobsStatusTray.vue.d.ts +9 -2
  53. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  54. package/dist/components/MobileSupportGate.vue.d.ts +1 -1
  55. package/dist/components/MoleculeInput.vue.d.ts +1 -1
  56. package/dist/components/NumberInput.vue.d.ts +1 -1
  57. package/dist/components/PlotlyChart.vue.d.ts +59 -0
  58. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  59. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  60. package/dist/components/ProgressBar.vue.d.ts +1 -1
  61. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  62. package/dist/components/ReagentList.vue.d.ts +29 -1
  63. package/dist/components/ResourceCard.vue.d.ts +1 -1
  64. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  65. package/dist/components/SampleLegend.vue.d.ts +1 -1
  66. package/dist/components/SampleSelector.drag.d.ts +13 -0
  67. package/dist/components/SampleSelector.tree.d.ts +2 -0
  68. package/dist/components/SampleSelector.vue.d.ts +35 -2
  69. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  70. package/dist/components/SecretInput.vue.d.ts +28 -0
  71. package/dist/components/SequenceInput.vue.d.ts +1 -1
  72. package/dist/components/SettingsModal.vue.d.ts +1 -1
  73. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
  74. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  75. package/dist/components/TagsInput.vue.d.ts +1 -1
  76. package/dist/components/TimePicker.vue.d.ts +2 -1
  77. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  78. package/dist/components/UnitInput.vue.d.ts +1 -1
  79. package/dist/components/WellPlate.geometry.d.ts +15 -0
  80. package/dist/components/WellPlate.interaction.d.ts +13 -0
  81. package/dist/components/WellPlate.vue.d.ts +30 -1
  82. package/dist/components/fileBrowserPresentation.d.ts +10 -0
  83. package/dist/components/index.d.ts +13 -0
  84. package/dist/components/index.js +8 -8
  85. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  86. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  87. package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
  88. package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
  89. package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
  90. package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
  91. package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
  92. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  93. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  94. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  95. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  96. package/dist/{components-DkHddRMl.js → components-DIum5hkx.js} +5882 -3200
  97. package/dist/components-DIum5hkx.js.map +1 -0
  98. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  99. package/dist/composables/filePicker/localFiles.d.ts +3 -0
  100. package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
  101. package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
  102. package/dist/composables/filePicker/validation.d.ts +5 -0
  103. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  104. package/dist/composables/index.d.ts +11 -2
  105. package/dist/composables/index.js +10 -10
  106. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  107. package/dist/composables/useApi.d.ts +14 -7
  108. package/dist/composables/useAsync.d.ts +5 -1
  109. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  110. package/dist/composables/useCommandHistory.d.ts +5 -1
  111. package/dist/composables/useDropdownState.d.ts +18 -2
  112. package/dist/composables/useEventStream.d.ts +9 -0
  113. package/dist/composables/useFileBrowser.d.ts +55 -0
  114. package/dist/composables/useFilePicker.d.ts +189 -0
  115. package/dist/composables/useFocusTrap.d.ts +21 -0
  116. package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
  117. package/dist/composables/useJobsStatusTray.d.ts +25 -1
  118. package/dist/composables/useListReorder.d.ts +90 -0
  119. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  120. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  121. package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
  122. package/dist/composables/usePluginClient.d.ts +2 -0
  123. package/dist/composables/usePluginConfig.d.ts +27 -0
  124. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  125. package/dist/composables/useRequestSyncState.d.ts +42 -2
  126. package/dist/composables/useResourceCrud.d.ts +5 -1
  127. package/dist/composables/useRovingFocus.d.ts +24 -0
  128. package/dist/composables/useScheduleDrag.d.ts +1 -0
  129. package/dist/composables/useWellPainting.d.ts +5 -1
  130. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  131. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  132. package/dist/composables/workspaceRecipes.d.ts +188 -0
  133. package/dist/{composables-C_i90sGS.js → composables-mFXsTA4g.js} +502 -86
  134. package/dist/composables-mFXsTA4g.js.map +1 -0
  135. package/dist/{useApi-J6zFkHmn.js → errors-BLuqMee5.js} +179 -16
  136. package/dist/errors-BLuqMee5.js.map +1 -0
  137. package/dist/{experiment-Cq145oEW.js → experiment-CVDRAnqp.js} +3 -8
  138. package/dist/experiment-CVDRAnqp.js.map +1 -0
  139. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-CV2Wza3z.js} +70 -51
  140. package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
  141. package/dist/index.d.ts +2 -0
  142. package/dist/index.js +15 -15
  143. package/dist/install.js +2 -7
  144. package/dist/install.js.map +1 -1
  145. package/dist/permissions.d.ts +42 -1
  146. package/dist/permissions.js +20 -10
  147. package/dist/permissions.js.map +1 -1
  148. package/dist/stores/auth.d.ts +6 -0
  149. package/dist/stores/index.d.ts +1 -1
  150. package/dist/stores/index.js +3 -3
  151. package/dist/stores/settings.d.ts +31 -1
  152. package/dist/styles.css +15135 -32397
  153. package/dist/templates/builderReadUtils.d.ts +1 -1
  154. package/dist/templates/index.js +2 -4
  155. package/dist/{templates-DsP51i-S.js → templates-CmGfmBRA.js} +303 -106
  156. package/dist/templates-CmGfmBRA.js.map +1 -0
  157. package/dist/types/auth.d.ts +1 -0
  158. package/dist/types/fileBrowserTypes.d.ts +77 -0
  159. package/dist/types/filePicker.d.ts +77 -0
  160. package/dist/types/form-builder.d.ts +1 -1
  161. package/dist/types/index.d.ts +2 -0
  162. package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-DldpxNOP.js} +499 -1162
  163. package/dist/useControlSchema-DldpxNOP.js.map +1 -0
  164. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-C7hRiVQf.js} +22 -6
  165. package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
  166. package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-CEcIaueJ.js} +4 -4
  167. package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
  168. package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-z5L_dUIy.js} +2 -2
  169. package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
  170. package/dist/utils/color.d.ts +13 -0
  171. package/dist/utils/errors.d.ts +9 -0
  172. package/dist/utils/pluginSecrets.d.ts +53 -0
  173. package/dist/utils/records.d.ts +4 -0
  174. package/dist/{useProtocolTemplates-CS_SQl6U.js → validation-uwoJGEb6.js} +811 -212
  175. package/dist/validation-uwoJGEb6.js.map +1 -0
  176. package/package.json +1 -1
  177. package/src/__tests__/apiErrors.test.ts +81 -0
  178. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  179. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  180. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  181. package/src/__tests__/components/DataFrame.test.ts +183 -11
  182. package/src/__tests__/components/DataStates.test.ts +176 -0
  183. package/src/__tests__/components/DatePicker.test.ts +76 -0
  184. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  185. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  186. package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
  187. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  188. package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
  189. package/src/__tests__/components/FilePicker.state.test.ts +287 -0
  190. package/src/__tests__/components/FilePicker.test.ts +487 -0
  191. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
  192. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  193. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  194. package/src/__tests__/components/PlotlyChart.test.ts +305 -0
  195. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  196. package/src/__tests__/components/RackEditor.test.ts +35 -0
  197. package/src/__tests__/components/ReagentList.test.ts +64 -2
  198. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  199. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  200. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  201. package/src/__tests__/components/SecretInput.test.ts +87 -0
  202. package/src/__tests__/components/SegmentedControl.test.ts +107 -0
  203. package/src/__tests__/components/TimePicker.test.ts +61 -0
  204. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  205. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  206. package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
  207. package/src/__tests__/composables/useApi.test.ts +92 -9
  208. package/src/__tests__/composables/useAuth.test.ts +2 -0
  209. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  210. package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
  211. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
  212. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  213. package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
  214. package/src/__tests__/composables/usePluginClient.test.ts +143 -0
  215. package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
  216. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  217. package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
  218. package/src/__tests__/stores/settings.test.ts +71 -1
  219. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  220. package/src/__tests__/utils/permissions.test.ts +68 -0
  221. package/src/apiErrors.ts +85 -0
  222. package/src/components/ActionMenu.vue +45 -88
  223. package/src/components/AlertBox.vue +0 -4
  224. package/src/components/AppAvatarMenu.vue +26 -7
  225. package/src/components/AppLayout.vue +0 -4
  226. package/src/components/AppPluginSwitcher.story.vue +87 -0
  227. package/src/components/AppPluginSwitcher.vue +2 -5
  228. package/src/components/AppSidebar.vue +0 -4
  229. package/src/components/AppToastContainer.vue +6 -8
  230. package/src/components/AppTopBar.vue +0 -4
  231. package/src/components/ArtifactSaveDialog.vue +0 -4
  232. package/src/components/ArtifactSelectorModal.vue +0 -4
  233. package/src/components/AuditTrail.story.vue +18 -0
  234. package/src/components/AuditTrail.vue +27 -7
  235. package/src/components/AutoGroupModal.vue +0 -4
  236. package/src/components/Avatar.vue +0 -4
  237. package/src/components/BaseButton.vue +0 -4
  238. package/src/components/BaseCheckbox.vue +0 -4
  239. package/src/components/BaseInput.vue +0 -4
  240. package/src/components/BaseModal.vue +8 -60
  241. package/src/components/BasePill.vue +0 -4
  242. package/src/components/BaseRadioGroup.vue +0 -4
  243. package/src/components/BaseSelect.vue +0 -4
  244. package/src/components/BaseSlider.vue +0 -4
  245. package/src/components/BaseTabs.vue +26 -5
  246. package/src/components/BaseTextarea.vue +0 -4
  247. package/src/components/BaseToggle.vue +0 -4
  248. package/src/components/BatchProgressList.story.vue +18 -0
  249. package/src/components/BatchProgressList.vue +32 -4
  250. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  251. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  252. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  253. package/src/components/Breadcrumb.vue +0 -4
  254. package/src/components/Calendar.vue +0 -4
  255. package/src/components/ChartContainer.story.vue +1 -1
  256. package/src/components/ChartContainer.vue +0 -4
  257. package/src/components/ChemicalFormula.vue +0 -4
  258. package/src/components/CollapsibleCard.vue +0 -4
  259. package/src/components/ColorSlider.vue +0 -4
  260. package/src/components/ComponentBindingRenderer.vue +1 -3
  261. package/src/components/ConcentrationInput.vue +0 -4
  262. package/src/components/ConfirmDialog.vue +0 -4
  263. package/src/components/ControlWorkspaceView.vue +11 -22
  264. package/src/components/DataFrame.story.vue +14 -0
  265. package/src/components/DataFrame.vue +97 -23
  266. package/src/components/DatePicker.vue +73 -27
  267. package/src/components/DateTimePicker.vue +45 -17
  268. package/src/components/Divider.vue +0 -4
  269. package/src/components/DoseCalculator.vue +0 -4
  270. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  271. package/src/components/DropdownButton.vue +24 -6
  272. package/src/components/EmptyState.vue +0 -4
  273. package/src/components/ExperimentCodeBadge.vue +0 -4
  274. package/src/components/ExperimentDataViewer.story.vue +3 -0
  275. package/src/components/ExperimentDataViewer.vue +33 -13
  276. package/src/components/ExperimentPopover.vue +12 -7
  277. package/src/components/ExperimentSelectorModal.vue +0 -4
  278. package/src/components/ExperimentTimeline.story.vue +18 -0
  279. package/src/components/ExperimentTimeline.vue +81 -75
  280. package/src/components/FileBrowserModal.story.vue +203 -0
  281. package/src/components/FileBrowserModal.vue +384 -0
  282. package/src/components/FilePicker.story.vue +44 -0
  283. package/src/components/FilePicker.vue +284 -0
  284. package/src/components/FileUploader.vue +32 -18
  285. package/src/components/FitPanel.vue +0 -4
  286. package/src/components/FormActions.story.vue +96 -0
  287. package/src/components/FormActions.vue +0 -4
  288. package/src/components/FormBuilder.vue +0 -4
  289. package/src/components/FormField.vue +0 -4
  290. package/src/components/FormulaInput.vue +0 -4
  291. package/src/components/GeneratedPathInput.story.vue +68 -0
  292. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  293. package/src/components/GeneratedPlotlyResult.vue +34 -69
  294. package/src/components/GeneratedPluginView.story.vue +145 -0
  295. package/src/components/GeneratedPluginView.vue +10 -10
  296. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  297. package/src/components/GeneratedResultRenderer.vue +4 -9
  298. package/src/components/GroupAssigner.vue +67 -5
  299. package/src/components/IconButton.vue +0 -4
  300. package/src/components/InstrumentAlertLog.vue +0 -4
  301. package/src/components/InstrumentStateBadge.vue +0 -4
  302. package/src/components/InstrumentStatusCard.vue +0 -4
  303. package/src/components/JobsStatusTray.vue +16 -5
  304. package/src/components/LcmsSequenceTable.vue +81 -50
  305. package/src/components/LoadingSpinner.vue +0 -4
  306. package/src/components/MobileSupportGate.vue +0 -4
  307. package/src/components/MoleculeInput.vue +0 -4
  308. package/src/components/MultiSelect.vue +0 -4
  309. package/src/components/NumberInput.vue +0 -4
  310. package/src/components/PlateMapEditor.vue +3 -14
  311. package/src/components/PlotlyChart.story.vue +61 -0
  312. package/src/components/PlotlyChart.vue +233 -0
  313. package/src/components/PluginIcon.vue +0 -4
  314. package/src/components/PluginWorkspaceView.props.ts +38 -0
  315. package/src/components/PluginWorkspaceView.vue +53 -224
  316. package/src/components/ProgressBar.vue +0 -4
  317. package/src/components/ProtocolStepEditor.vue +2 -4
  318. package/src/components/RackEditor.vue +51 -70
  319. package/src/components/ReagentEditor.vue +0 -4
  320. package/src/components/ReagentList.story.vue +18 -0
  321. package/src/components/ReagentList.vue +127 -82
  322. package/src/components/ResourceCard.vue +0 -4
  323. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  324. package/src/components/RuntimeUpdateNotice.vue +4 -4
  325. package/src/components/SampleHierarchyTree.story.vue +18 -0
  326. package/src/components/SampleHierarchyTree.vue +32 -9
  327. package/src/components/SampleLegend.vue +2 -8
  328. package/src/components/SampleSelector.drag.ts +51 -0
  329. package/src/components/SampleSelector.story.vue +6 -0
  330. package/src/components/SampleSelector.tree.ts +2 -0
  331. package/src/components/SampleSelector.vue +49 -10
  332. package/src/components/SampleSelectorGroupNode.vue +19 -0
  333. package/src/components/ScheduleCalendar.story.vue +6 -0
  334. package/src/components/ScheduleCalendar.vue +82 -7
  335. package/src/components/ScientificNumber.vue +0 -4
  336. package/src/components/SecretInput.story.vue +79 -0
  337. package/src/components/SecretInput.vue +150 -0
  338. package/src/components/SectionCard.vue +0 -4
  339. package/src/components/SegmentedControl.vue +50 -7
  340. package/src/components/SequenceInput.vue +0 -4
  341. package/src/components/SequenceProgressBar.story.vue +110 -0
  342. package/src/components/SequenceProgressBar.vue +0 -4
  343. package/src/components/SettingsModal.vue +0 -4
  344. package/src/components/Skeleton.vue +0 -4
  345. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  346. package/src/components/SmartGroupManual.vue +0 -4
  347. package/src/components/StatusIndicator.vue +0 -4
  348. package/src/components/StepWizard.vue +0 -4
  349. package/src/components/TagsInput.vue +4 -6
  350. package/src/components/ThemeToggle.vue +0 -4
  351. package/src/components/TimePicker.vue +36 -7
  352. package/src/components/TimeRangeInput.vue +0 -4
  353. package/src/components/Tooltip.story.vue +3 -3
  354. package/src/components/Tooltip.vue +0 -4
  355. package/src/components/UnitInput.vue +0 -4
  356. package/src/components/WellPlate.geometry.ts +60 -1
  357. package/src/components/WellPlate.interaction.ts +44 -0
  358. package/src/components/WellPlate.story.vue +8 -0
  359. package/src/components/WellPlate.vue +53 -7
  360. package/src/components/fileBrowserPresentation.ts +39 -0
  361. package/src/components/index.ts +14 -0
  362. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  363. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  364. package/src/components/internal/DataStateInternal.vue +74 -0
  365. package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
  366. package/src/components/internal/FileBrowserListingInternal.vue +197 -0
  367. package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
  368. package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
  369. package/src/components/internal/FilePickerTreeInternal.vue +238 -0
  370. package/src/components/internal/FormSectionRenderer.vue +0 -4
  371. package/src/components/internal/WellEditPopupInternal.vue +20 -5
  372. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  373. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  374. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  375. package/src/composables/controlSchemaTypes.ts +34 -0
  376. package/src/composables/experimentDesignData.ts +1 -3
  377. package/src/composables/filePicker/localFiles.ts +58 -0
  378. package/src/composables/filePicker/usePickerNavigation.ts +220 -0
  379. package/src/composables/filePicker/usePickerSelection.ts +259 -0
  380. package/src/composables/filePicker/validation.ts +41 -0
  381. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  382. package/src/composables/formBuilderRegistry.ts +5 -0
  383. package/src/composables/index.ts +50 -0
  384. package/src/composables/pluginWorkspaceModel.ts +182 -0
  385. package/src/composables/useApi.ts +57 -13
  386. package/src/composables/useAsync.ts +5 -1
  387. package/src/composables/useAuth.ts +3 -0
  388. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  389. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  390. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  391. package/src/composables/useCommandHistory.ts +5 -1
  392. package/src/composables/useDropdownState.ts +35 -5
  393. package/src/composables/useEventStream.ts +14 -2
  394. package/src/composables/useFileBrowser.ts +314 -0
  395. package/src/composables/useFileImport.ts +2 -7
  396. package/src/composables/useFilePicker.ts +366 -0
  397. package/src/composables/useFocusTrap.ts +91 -0
  398. package/src/composables/useGeneratedAnalysis.ts +32 -27
  399. package/src/composables/useJobsStatusTray.ts +184 -17
  400. package/src/composables/useListReorder.story.vue +143 -0
  401. package/src/composables/useListReorder.ts +242 -0
  402. package/src/composables/useMenuKeyboard.ts +90 -0
  403. package/src/composables/useOptimisticMutation.ts +6 -16
  404. package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
  405. package/src/composables/usePluginClient.ts +50 -4
  406. package/src/composables/usePluginConfig.ts +252 -0
  407. package/src/composables/usePluginWorkspace.ts +208 -0
  408. package/src/composables/useRackEditor.ts +2 -11
  409. package/src/composables/useRequestSyncState.ts +98 -28
  410. package/src/composables/useResourceCrud.ts +6 -16
  411. package/src/composables/useRovingFocus.ts +66 -0
  412. package/src/composables/useScheduleDrag.ts +37 -0
  413. package/src/composables/useWellPainting.ts +10 -30
  414. package/src/composables/useWellPlateAdapter.ts +5 -1
  415. package/src/composables/useWellPlateEditor.ts +1 -11
  416. package/src/composables/useWellPlateValidation.ts +5 -1
  417. package/src/composables/workspaceRecipes.ts +267 -0
  418. package/src/index.ts +19 -0
  419. package/src/permissions.ts +48 -9
  420. package/src/stores/auth.ts +1 -0
  421. package/src/stores/experiment.ts +2 -7
  422. package/src/stores/index.ts +9 -1
  423. package/src/stores/settings.ts +93 -1
  424. package/src/styles/components/audit-trail.css +5 -5
  425. package/src/styles/components/batch-progress-list.css +7 -0
  426. package/src/styles/components/color-slider.css +2 -2
  427. package/src/styles/components/data-state.css +30 -0
  428. package/src/styles/components/dataframe.css +29 -35
  429. package/src/styles/components/experiment-data-viewer.css +4 -4
  430. package/src/styles/components/experiment-timeline.css +3 -30
  431. package/src/styles/components/file-browser-modal.css +489 -0
  432. package/src/styles/components/file-picker.css +502 -0
  433. package/src/styles/components/form-builder.css +5 -5
  434. package/src/styles/components/icon-button.css +9 -0
  435. package/src/styles/components/lcms-sequence-table.css +16 -1
  436. package/src/styles/components/multi-select.css +1 -1
  437. package/src/styles/components/reagent-list.css +38 -31
  438. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  439. package/src/styles/components/sample-selector.css +9 -2
  440. package/src/styles/components/schedule-calendar.css +8 -0
  441. package/src/styles/components/scientific-number.css +1 -1
  442. package/src/styles/components/secret-input.css +60 -0
  443. package/src/styles/components/well-plate.css +8 -0
  444. package/src/styles/index.css +12 -0
  445. package/src/styles/variables.css +29 -0
  446. package/src/templates/builderPresetControls.ts +1 -1
  447. package/src/templates/builderReadUtils.ts +1 -3
  448. package/src/templates/componentPlateHelpers.ts +3 -1
  449. package/src/templates/componentTargetResolvers.ts +1 -3
  450. package/src/templates/controlSchemaTargets.ts +1 -3
  451. package/src/templates/doseResponseAdapters.ts +2 -13
  452. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  453. package/src/templates/plateMapBuilder.ts +2 -2
  454. package/src/templates/qpcrPlateBuilder.ts +1 -1
  455. package/src/templates/templateValidators.ts +1 -1
  456. package/src/templates/timeCourseBuilder.ts +2 -2
  457. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  458. package/src/types/auth.ts +1 -0
  459. package/src/types/fileBrowserTypes.ts +85 -0
  460. package/src/types/filePicker.ts +72 -0
  461. package/src/types/form-builder.ts +1 -0
  462. package/src/types/index.ts +14 -0
  463. package/src/utils/color.ts +51 -0
  464. package/src/utils/errors.ts +29 -0
  465. package/src/utils/formModelSync.ts +1 -3
  466. package/src/utils/pluginIcon.ts +1 -3
  467. package/src/utils/pluginSecrets.ts +102 -0
  468. package/src/utils/records.ts +9 -0
  469. package/dist/EmptyState-p90quq8Z.js.map +0 -1
  470. package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
  471. package/dist/ExperimentSelectorModal-DyPAUCLw.js.map +0 -1
  472. package/dist/SettingsModal-BgKY93zh.js.map +0 -1
  473. package/dist/components-DkHddRMl.js.map +0 -1
  474. package/dist/composables/autoGroupConstants.d.ts +0 -1
  475. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  476. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  477. package/dist/composables-C_i90sGS.js.map +0 -1
  478. package/dist/experiment-Cq145oEW.js.map +0 -1
  479. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  480. package/dist/templates/builderDefaults.d.ts +0 -1
  481. package/dist/templates/plateGeometry.d.ts +0 -4
  482. package/dist/templates-DsP51i-S.js.map +0 -1
  483. package/dist/useApi-J6zFkHmn.js.map +0 -1
  484. package/dist/useControlSchema-BDx2Olha.js.map +0 -1
  485. package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
  486. package/dist/useProtocolTemplates-CS_SQl6U.js.map +0 -1
  487. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  488. package/src/composables/autoGroupConstants.ts +0 -4
  489. package/src/composables/autoGroupGrouping.ts +0 -148
  490. package/src/composables/useAutoGroupInputSources.ts +0 -147
  491. package/src/templates/builderDefaults.ts +0 -11
  492. package/src/templates/plateGeometry.ts +0 -62
  493. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
@@ -1,9 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  /** Input that opens a calendar dropdown for selecting a single date, with clearable support. */
3
- import { ref, computed, watch } from 'vue'
3
+ import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom'
4
+ import { ref, computed, nextTick, onBeforeUnmount, watch } from 'vue'
4
5
  import { useCalendarGrid, type CalendarGridDay } from '../composables/useCalendarGrid'
5
6
  import { useDropdownState } from '../composables/useDropdownState'
6
- import { useEventListener } from '../composables/useEventListener'
7
+ import { useFocusTrap } from '../composables/useFocusTrap'
7
8
  import CalendarGridPanelInternal from './internal/CalendarGridPanelInternal.vue'
8
9
 
9
10
  interface Props {
@@ -28,12 +29,14 @@ const emit = defineEmits<{
28
29
  'update:modelValue': [value: string | undefined]
29
30
  }>()
30
31
 
31
- const inputRef = ref<HTMLDivElement | null>(null)
32
- const dropdownRef = ref<HTMLDivElement | null>(null)
32
+ const dropdownRef = ref<HTMLElement | null>(null)
33
33
  const dropdownStyle = ref<Record<string, string>>({})
34
- const { isOpen, rootRef: containerRef, close, toggle } = useDropdownState({
34
+ const { isOpen, rootRef: containerRef, triggerRef, open, close, toggle } = useDropdownState({
35
35
  insideRefs: [dropdownRef],
36
36
  })
37
+ // The panel is a non-modal dialog: Tab cycles inside it while it is open, and
38
+ // focus returns to the input when it closes.
39
+ useFocusTrap({ containerRef: dropdownRef, active: isOpen })
37
40
  const {
38
41
  weekDays,
39
42
  monthYear,
@@ -88,41 +91,88 @@ function toggleCalendar() {
88
91
  toggle()
89
92
  }
90
93
 
91
- function updateDropdownPosition() {
92
- if (!inputRef.value) return
93
- const rect = inputRef.value.getBoundingClientRect()
94
+ function handleTriggerKeydown(event: KeyboardEvent) {
95
+ if (props.disabled || isOpen.value) return
96
+ if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
97
+ event.preventDefault()
98
+ open()
99
+ }
100
+ }
101
+
102
+ // floating-ui keeps the panel anchored (flip/shift) while the page scrolls,
103
+ // instead of the panel closing on every scroll or resize.
104
+ let stopAutoUpdate: (() => void) | null = null
105
+
106
+ async function updateDropdownPosition() {
107
+ const trigger = triggerRef.value
108
+ const panel = dropdownRef.value
109
+ if (!trigger || !panel) return
110
+
111
+ const { x, y } = await computePosition(trigger, panel, {
112
+ placement: 'bottom-start',
113
+ strategy: 'fixed',
114
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
115
+ })
94
116
  dropdownStyle.value = {
95
117
  position: 'fixed',
96
- top: `${rect.bottom + 4}px`,
97
- left: `${rect.left}px`,
98
- width: `${Math.max(rect.width, 288)}px`,
118
+ top: `${y}px`,
119
+ left: `${x}px`,
120
+ width: `${Math.max(trigger.getBoundingClientRect().width, 288)}px`,
99
121
  zIndex: '9999',
100
122
  }
101
123
  }
102
124
 
103
- function handleScrollOrResize() {
104
- close()
125
+ function stopPositioning() {
126
+ stopAutoUpdate?.()
127
+ stopAutoUpdate = null
105
128
  }
106
129
 
107
- watch(isOpen, (open) => {
108
- if (open) {
109
- updateDropdownPosition()
110
- if (selectedDate.value) {
111
- setCurrentMonthFromDate(selectedDate.value)
112
- }
130
+ function startPositioning() {
131
+ const trigger = triggerRef.value
132
+ const panel = dropdownRef.value
133
+ if (!trigger || !panel) return
134
+ stopPositioning()
135
+ stopAutoUpdate = autoUpdate(trigger, panel, () => void updateDropdownPosition())
136
+ }
137
+
138
+ /** Moves focus into the calendar grid: selected day, else today, else the first day. */
139
+ function focusCalendar() {
140
+ const panel = dropdownRef.value
141
+ if (!panel) return
142
+ const target =
143
+ panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??
144
+ panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??
145
+ panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')
146
+ target?.focus()
147
+ }
148
+
149
+ watch(isOpen, async (opened) => {
150
+ if (!opened) {
151
+ stopPositioning()
152
+ return
153
+ }
154
+ if (selectedDate.value) {
155
+ setCurrentMonthFromDate(selectedDate.value)
113
156
  }
157
+ await nextTick()
158
+ startPositioning()
159
+ // A second tick so this focus lands after the focus trap's initial focus.
160
+ await nextTick()
161
+ focusCalendar()
114
162
  })
115
163
 
116
- useEventListener(() => window, 'scroll', handleScrollOrResize, true)
117
- useEventListener(() => window, 'resize', handleScrollOrResize)
164
+ onBeforeUnmount(stopPositioning)
118
165
  </script>
119
166
 
120
167
  <template>
121
168
  <div ref="containerRef" class="mint-date-picker">
122
- <div ref="inputRef" class="mint-date-picker__input-wrapper">
169
+ <div class="mint-date-picker__input-wrapper">
123
170
  <input
171
+ ref="triggerRef"
124
172
  type="text"
125
173
  readonly
174
+ aria-haspopup="dialog"
175
+ :aria-expanded="isOpen"
126
176
  :value="displayValue"
127
177
  :placeholder="placeholder || 'Select date'"
128
178
  :disabled="disabled"
@@ -133,6 +183,7 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
133
183
  disabled ? 'mint-date-picker__input--disabled' : '',
134
184
  ]"
135
185
  @click="toggleCalendar"
186
+ @keydown="handleTriggerKeydown"
136
187
  />
137
188
  <div class="mint-date-picker__icon">
138
189
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -156,7 +207,6 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
156
207
  class="mint-date-picker__dropdown"
157
208
  :style="dropdownStyle"
158
209
  role="dialog"
159
- aria-modal="true"
160
210
  aria-label="Date picker"
161
211
  >
162
212
  <CalendarGridPanelInternal
@@ -191,7 +241,3 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
191
241
  </Teleport>
192
242
  </div>
193
243
  </template>
194
-
195
- <style>
196
- @import '../styles/components/date-picker.css';
197
- </style>
@@ -1,8 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /** Combined date and time picker with a dropdown calendar, time slots, and 12/24 h format support. */
3
- import { computed, watch, nextTick } from 'vue'
3
+ import { computed, ref, watch, nextTick } from 'vue'
4
4
  import { useCalendarGrid, type CalendarGridDay } from '../composables/useCalendarGrid'
5
5
  import { useDropdownState } from '../composables/useDropdownState'
6
+ import { useFocusTrap } from '../composables/useFocusTrap'
6
7
  import { formatTime, formatTimeSlot, generateTimeSlots, toMinutes } from '../composables/useTimeUtils'
7
8
  import CalendarGridPanelInternal from './internal/CalendarGridPanelInternal.vue'
8
9
 
@@ -35,7 +36,10 @@ const emit = defineEmits<{
35
36
  'update:modelValue': [value: string | undefined]
36
37
  }>()
37
38
 
38
- const { isOpen, rootRef, close, toggle } = useDropdownState()
39
+ const dropdownRef = ref<HTMLElement | null>(null)
40
+ const { isOpen, rootRef, triggerRef, open, close, toggle } = useDropdownState()
41
+ // Non-modal dialog: Tab stays inside the panel, focus returns to the input on close.
42
+ useFocusTrap({ containerRef: dropdownRef, active: isOpen })
39
43
  const {
40
44
  weekDays,
41
45
  monthYear,
@@ -148,18 +152,42 @@ function toggleDropdown() {
148
152
  toggle()
149
153
  }
150
154
 
151
- watch(isOpen, (open) => {
152
- if (open && selectedDate.value) {
155
+ function handleTriggerKeydown(event: KeyboardEvent) {
156
+ if (props.disabled || isOpen.value) return
157
+ if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
158
+ event.preventDefault()
159
+ open()
160
+ }
161
+ }
162
+
163
+ /** Moves focus into the calendar grid: selected day, else today, else the first day. */
164
+ function focusCalendar() {
165
+ const panel = dropdownRef.value
166
+ if (!panel) return
167
+ const target =
168
+ panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??
169
+ panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??
170
+ panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')
171
+ target?.focus()
172
+ }
173
+
174
+ watch(isOpen, async (opened) => {
175
+ if (!opened) return
176
+ if (selectedDate.value) {
153
177
  setCurrentMonthFromDate(selectedDate.value)
154
- nextTick(() => {
155
- // Scroll time grid to selected time
156
- const grid = rootRef.value?.querySelector('.mint-datetime-picker__time-grid')
157
- const active = rootRef.value?.querySelector('.mint-datetime-picker__time-chip--active')
158
- if (grid && active) {
159
- active.scrollIntoView({ block: 'center' })
160
- }
161
- })
162
178
  }
179
+ await nextTick()
180
+ if (selectedDate.value) {
181
+ // Scroll time grid to selected time
182
+ const grid = dropdownRef.value?.querySelector('.mint-datetime-picker__time-grid')
183
+ const active = dropdownRef.value?.querySelector('.mint-datetime-picker__time-chip--active')
184
+ if (grid && active) {
185
+ active.scrollIntoView({ block: 'center' })
186
+ }
187
+ }
188
+ // A second tick so this focus lands after the focus trap's initial focus.
189
+ await nextTick()
190
+ focusCalendar()
163
191
  })
164
192
  </script>
165
193
 
@@ -172,8 +200,11 @@ watch(isOpen, (open) => {
172
200
  </svg>
173
201
  </div>
174
202
  <input
203
+ ref="triggerRef"
175
204
  type="text"
176
205
  readonly
206
+ aria-haspopup="dialog"
207
+ :aria-expanded="isOpen"
177
208
  :value="displayValue"
178
209
  :placeholder="placeholder"
179
210
  :disabled="disabled"
@@ -185,6 +216,7 @@ watch(isOpen, (open) => {
185
216
  ]"
186
217
  aria-label="Select date and time"
187
218
  @click="toggleDropdown"
219
+ @keydown="handleTriggerKeydown"
188
220
  />
189
221
  <div class="mint-datetime-picker__icon-clock">
190
222
  <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -203,9 +235,9 @@ watch(isOpen, (open) => {
203
235
  >
204
236
  <div
205
237
  v-if="isOpen"
238
+ ref="dropdownRef"
206
239
  class="mint-datetime-picker__dropdown"
207
240
  role="dialog"
208
- aria-modal="true"
209
241
  aria-label="Date and time picker"
210
242
  >
211
243
  <!-- Calendar section -->
@@ -265,7 +297,3 @@ watch(isOpen, (open) => {
265
297
  </Transition>
266
298
  </div>
267
299
  </template>
268
-
269
- <style>
270
- @import '../styles/components/datetime-picker.css';
271
- </style>
@@ -43,7 +43,3 @@ withDefaults(defineProps<Props>(), {
43
43
  </template>
44
44
  </div>
45
45
  </template>
46
-
47
- <style>
48
- @import '../styles/components/divider.css';
49
- </style>
@@ -410,7 +410,3 @@ watch(activeMode, (mode) => {
410
410
  </div>
411
411
  </div>
412
412
  </template>
413
-
414
- <style>
415
- @import '../styles/components/dose-calculator.css';
416
- </style>
@@ -19,6 +19,7 @@ import type {
19
19
  UseControlWorkspaceReturn,
20
20
  } from '../composables/useControlSchema'
21
21
  import { defineDoseDesignControlModel } from '../composables/useControlSchema'
22
+ import { doseDesignWorkspaceProps } from '../composables/workspaceRecipes'
22
23
  import ControlWorkspaceView from './ControlWorkspaceView.vue'
23
24
  import DoseCalculator from './DoseCalculator.vue'
24
25
  import WellPlate from './WellPlate.vue'
@@ -158,6 +159,10 @@ const resolvedModel = computed<ControlModel | ControlModelBinding>(() =>
158
159
  props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)
159
160
  )
160
161
 
162
+ // Thin recipe: forwards the ~25 props this view shares verbatim with ControlWorkspaceView
163
+ // (see workspaceRecipes.ts) instead of spelling each one out in the template.
164
+ const controlWorkspaceRecipe = computed(() => doseDesignWorkspaceProps(props, resolvedModel.value))
165
+
161
166
  function resolvedPlateId(): string {
162
167
  return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'
163
168
  }
@@ -200,31 +205,7 @@ function slotProps(slotProps: {
200
205
 
201
206
  <template>
202
207
  <ControlWorkspaceView
203
- :model="resolvedModel"
204
- :workspace="workspace"
205
- :control-options="controlOptions"
206
- :initial-values="initialValues"
207
- :model-value="modelValue"
208
- :values="values"
209
- :title="title"
210
- :subtitle="subtitle"
211
- :top-bar-variant="topBarVariant"
212
- :sidebar-width="sidebarWidth"
213
- :sidebar-variant="sidebarVariant"
214
- :responsive-sidebar="responsiveSidebar"
215
- :sidebar-position="sidebarPosition"
216
- :sidebar-title="sidebarTitle"
217
- :sidebar-subtitle="sidebarSubtitle"
218
- :sidebar-badge="sidebarBadge"
219
- :floating="floating"
220
- :dense="dense"
221
- :show-settings="showSettings"
222
- :show-form-actions="showFormActions"
223
- :form-enhancements="formEnhancements"
224
- :form-loading="formLoading"
225
- :form-disabled="formDisabled"
226
- :form-readonly="formReadonly"
227
- :form-size="formSize"
208
+ v-bind="controlWorkspaceRecipe"
228
209
  @update:model-value="emit('update:modelValue', $event)"
229
210
  @update:values="emit('update:values', $event)"
230
211
  @submit="emit('submit', $event)"
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Button that opens a dropdown menu for selecting one option from a list. */
3
- import { computed } from 'vue'
3
+ import { computed, ref } from 'vue'
4
4
  import { useDropdownState } from '../composables/useDropdownState'
5
+ import { useMenuKeyboard } from '../composables/useMenuKeyboard'
5
6
  import type { SelectOption, SelectOptionInput, ButtonVariant, ButtonSize } from '../types'
6
7
  import { normalizeOptionInput } from '../utils/options'
7
8
 
@@ -53,11 +54,25 @@ const emit = defineEmits<{
53
54
  select: [option: SelectOption<string | number>]
54
55
  }>()
55
56
 
56
- const { isOpen, rootRef, close, toggle: toggleDropdown } = useDropdownState()
57
+ const { isOpen, rootRef, triggerRef, open, close, toggle: toggleDropdown } = useDropdownState()
57
58
  const normalizedOptions = computed<SelectOption<string | number>[]>(() =>
58
59
  props.options.map(normalizeOptionInput)
59
60
  )
60
61
 
62
+ const panelRef = ref<HTMLElement | null>(null)
63
+
64
+ function optionItems(): HTMLElement[] {
65
+ if (!panelRef.value) return []
66
+ return Array.from(panelRef.value.querySelectorAll<HTMLElement>('[role="option"]:not([aria-disabled="true"])'))
67
+ }
68
+
69
+ const { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({
70
+ items: optionItems,
71
+ isOpen: () => isOpen.value,
72
+ open,
73
+ close,
74
+ })
75
+
61
76
  const selectedLabel = computed(() => {
62
77
  if (props.modelValue === undefined) return props.placeholder ?? 'Select...'
63
78
  const found = normalizedOptions.value.find(o => o.value === props.modelValue)
@@ -80,6 +95,7 @@ function selectOption(option: SelectOption<string | number>) {
80
95
  <template>
81
96
  <div ref="rootRef" class="mint-dropdown-button">
82
97
  <button
98
+ ref="triggerRef"
83
99
  type="button"
84
100
  :disabled="disabled || loading"
85
101
  :class="[
@@ -94,6 +110,7 @@ function selectOption(option: SelectOption<string | number>) {
94
110
  :aria-expanded="isOpen"
95
111
  aria-haspopup="listbox"
96
112
  @click="toggle"
113
+ @keydown="handleTriggerKeydown"
97
114
  >
98
115
  <svg v-if="loading" class="mint-dropdown-button__spinner" viewBox="0 0 24 24" fill="none" aria-hidden="true">
99
116
  <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" opacity="0.25" />
@@ -116,15 +133,18 @@ function selectOption(option: SelectOption<string | number>) {
116
133
  <Transition name="mint-dropdown-button-menu">
117
134
  <ul
118
135
  v-if="isOpen"
136
+ ref="panelRef"
119
137
  class="mint-dropdown-button__menu"
120
138
  role="listbox"
121
139
  :aria-activedescendant="modelValue !== undefined ? `mint-dropdown-option-${modelValue}` : undefined"
140
+ @keydown="handlePanelKeydown"
122
141
  >
123
142
  <li
124
143
  v-for="option in normalizedOptions"
125
144
  :id="`mint-dropdown-option-${option.value}`"
126
145
  :key="String(option.value)"
127
146
  role="option"
147
+ :tabindex="-1"
128
148
  :aria-selected="option.value === modelValue"
129
149
  :aria-disabled="option.disabled"
130
150
  :class="[
@@ -135,6 +155,8 @@ function selectOption(option: SelectOption<string | number>) {
135
155
  },
136
156
  ]"
137
157
  @click="selectOption(option)"
158
+ @keydown.enter.prevent="selectOption(option)"
159
+ @keydown.space.prevent="selectOption(option)"
138
160
  >
139
161
  <span class="mint-dropdown-button__option-label">{{ option.label }}</span>
140
162
  <span v-if="option.description" class="mint-dropdown-button__option-description">
@@ -157,7 +179,3 @@ function selectOption(option: SelectOption<string | number>) {
157
179
  </Transition>
158
180
  </div>
159
181
  </template>
160
-
161
- <style>
162
- @import '../styles/components/dropdown-button.css';
163
- </style>
@@ -65,7 +65,3 @@ const defaultIconPaths = [
65
65
  </div>
66
66
  </div>
67
67
  </template>
68
-
69
- <style>
70
- @import '../styles/components/empty-state.css';
71
- </style>
@@ -58,7 +58,3 @@ function handleKeydown(event: KeyboardEvent) {
58
58
  {{ copied ? 'Copied!' : code }}
59
59
  </span>
60
60
  </template>
61
-
62
- <style>
63
- @import '../styles/components/experiment-code-badge.css';
64
- </style>
@@ -96,6 +96,7 @@ const views = ['summary', 'tree', 'table'] as const
96
96
  <template #default="{ state }">
97
97
  <div style="padding: 1rem; max-width: 800px; margin: 0 auto;">
98
98
  <ExperimentDataViewer
99
+ :error="state.error || null"
99
100
  :tree-data="treeData"
100
101
  :table-data="tableData"
101
102
  :summary-data="state.showSummary ? summaryData : null"
@@ -107,6 +108,8 @@ const views = ['summary', 'tree', 'table'] as const
107
108
  </template>
108
109
 
109
110
  <template #controls="{ state }">
111
+
112
+ <HstText v-model="state.error" title="Error Message" />
110
113
  <HstSelect
111
114
  v-model="state.defaultView"
112
115
  title="Default View"
@@ -8,6 +8,7 @@ import DataFrame from './DataFrame.vue'
8
8
  import SegmentedControl from './SegmentedControl.vue'
9
9
  import BaseButton from './BaseButton.vue'
10
10
  import Skeleton from './Skeleton.vue'
11
+ import DataStateInternal from './internal/DataStateInternal.vue'
11
12
 
12
13
  interface Props {
13
14
  treeData?: TreeNode[]
@@ -20,6 +21,11 @@ interface Props {
20
21
  pluginRoutePrefix?: string
21
22
  experimentId?: number
22
23
  loading?: boolean
24
+ /**
25
+ * Error message. When set (or when the auto-fetch fails), the content area
26
+ * renders an error state instead of the data views.
27
+ */
28
+ error?: string | null
23
29
  downloadJsonUrl?: string
24
30
  downloadCsvUrl?: string
25
31
  autoFetch?: boolean
@@ -29,6 +35,7 @@ const props = withDefaults(defineProps<Props>(), {
29
35
  title: 'Data',
30
36
  defaultView: 'summary',
31
37
  loading: false,
38
+ error: null,
32
39
  autoFetch: true,
33
40
  })
34
41
 
@@ -61,6 +68,9 @@ const mergedSummaryData = computed(() =>
61
68
  const isLoading = computed(() =>
62
69
  props.loading || expData.isLoading.value,
63
70
  )
71
+ const activeError = computed<string | null>(() =>
72
+ props.error ?? expData.error.value,
73
+ )
64
74
 
65
75
  const emit = defineEmits<{
66
76
  'open-plugin': []
@@ -179,13 +189,20 @@ function handleDownloadCsv() {
179
189
  </div>
180
190
 
181
191
  <div class="mint-data-viewer__content">
182
- <div v-if="isLoading" class="mint-data-viewer__loading">
183
- <div class="mint-data-viewer__skeleton">
184
- <Skeleton width="40%" height="16px" />
185
- <Skeleton width="100%" height="12px" />
186
- <Skeleton width="100%" height="12px" />
187
- <Skeleton width="75%" height="12px" />
188
- </div>
192
+ <div v-if="activeError" class="mint-data-viewer__state">
193
+ <slot name="error" :error="activeError">
194
+ <DataStateInternal state="error" :title="activeError" />
195
+ </slot>
196
+ </div>
197
+ <div v-else-if="isLoading" class="mint-data-viewer__loading">
198
+ <slot name="loading">
199
+ <div class="mint-data-viewer__skeleton">
200
+ <Skeleton width="40%" height="16px" />
201
+ <Skeleton width="100%" height="12px" />
202
+ <Skeleton width="100%" height="12px" />
203
+ <Skeleton width="75%" height="12px" />
204
+ </div>
205
+ </slot>
189
206
  </div>
190
207
  <template v-else>
191
208
  <!-- Summary View -->
@@ -280,14 +297,17 @@ function handleDownloadCsv() {
280
297
  :searchable="true"
281
298
  :sortable="true"
282
299
  />
283
- <div v-else-if="viewMode === 'table'" class="mint-data-viewer__empty">
284
- No tabular data available. Use tree view.
300
+ <div v-else-if="viewMode === 'table'" class="mint-data-viewer__state">
301
+ <slot name="empty">
302
+ <DataStateInternal
303
+ state="empty"
304
+ title="No tabular data available"
305
+ description="Switch to the tree view to inspect this experiment."
306
+ size="sm"
307
+ />
308
+ </slot>
285
309
  </div>
286
310
  </template>
287
311
  </div>
288
312
  </div>
289
313
  </template>
290
-
291
- <style>
292
- @import '../styles/components/experiment-data-viewer.css';
293
- </style>
@@ -57,12 +57,20 @@ const emit = defineEmits<{
57
57
  detach: []
58
58
  }>()
59
59
 
60
- const { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({
60
+ const { isOpen, rootRef: popoverRef, triggerRef, close, toggle } = useDropdownState({
61
61
  closeOnEscape: false,
62
62
  })
63
63
  const showSuccess = ref(false)
64
64
  const showConfirm = ref(false)
65
65
 
66
+ function handleTriggerClick() {
67
+ if (!hasExperiment.value && !isUnresolved.value) {
68
+ handleSelect()
69
+ return
70
+ }
71
+ toggle()
72
+ }
73
+
66
74
  function handleSelect() {
67
75
  emit('select')
68
76
  close()
@@ -115,9 +123,10 @@ onUnmounted(() => {
115
123
  { 'mint-experiment-popover__split--with-save': showSave && experimentName },
116
124
  ]"
117
125
  >
118
- <!-- Left: experiment trigger (opens popover). Prefer the human-readable
126
+ <!-- Left: experiment trigger (selects when empty, otherwise opens popover). Prefer the human-readable
119
127
  name; the formal code, when present, stays in the detail panel. -->
120
128
  <button
129
+ ref="triggerRef"
121
130
  type="button"
122
131
  :class="[
123
132
  'mint-experiment-popover__trigger',
@@ -130,7 +139,7 @@ onUnmounted(() => {
130
139
  :aria-label="isUnresolved ? triggerLabel : undefined"
131
140
  :aria-busy="isResolving || undefined"
132
141
  :aria-expanded="isOpen"
133
- @click.stop="toggle"
142
+ @click.stop="handleTriggerClick"
134
143
  >
135
144
  <!-- Flask icon -->
136
145
  <svg class="mint-experiment-popover__trigger-icon" aria-hidden="true" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -306,7 +315,3 @@ onUnmounted(() => {
306
315
  />
307
316
  </div>
308
317
  </template>
309
-
310
- <style>
311
- @import '../styles/components/experiment-popover.css';
312
- </style>
@@ -426,7 +426,3 @@ watch(
426
426
  </div>
427
427
  </BaseModal>
428
428
  </template>
429
-
430
- <style>
431
- @import '../styles/components/experiment-selector-modal.css';
432
- </style>
@@ -57,6 +57,8 @@ const mockSteps: ProtocolStep[] = [
57
57
 
58
58
  function initState() {
59
59
  return {
60
+ loading: false,
61
+ error: '',
60
62
  modelValue: mockSteps,
61
63
  orientation: 'vertical' as const,
62
64
  showDuration: true,
@@ -76,6 +78,8 @@ function initState() {
76
78
  <template #default="{ state }">
77
79
  <div style="padding: 2rem; max-width: 600px;">
78
80
  <ExperimentTimeline
81
+ :loading="state.loading"
82
+ :error="state.error || null"
79
83
  v-model="state.modelValue"
80
84
  :orientation="state.orientation"
81
85
  :show-duration="state.showDuration"
@@ -89,6 +93,8 @@ function initState() {
89
93
  </div>
90
94
  </template>
91
95
  <template #controls="{ state }">
96
+ <HstCheckbox v-model="state.loading" title="Loading" />
97
+ <HstText v-model="state.error" title="Error Message" />
92
98
  <HstSelect
93
99
  v-model="state.orientation"
94
100
  title="Orientation"
@@ -157,5 +163,17 @@ function initState() {
157
163
  <ExperimentTimeline :model-value="[]" editable />
158
164
  </div>
159
165
  </Variant>
166
+
167
+ <Variant title="Loading">
168
+ <div style="padding: 2rem; max-width: 600px;">
169
+ <ExperimentTimeline :model-value="[]" loading />
170
+ </div>
171
+ </Variant>
172
+
173
+ <Variant title="Error">
174
+ <div style="padding: 2rem; max-width: 600px;">
175
+ <ExperimentTimeline :model-value="[]" error="Could not load the protocol." />
176
+ </div>
177
+ </Variant>
160
178
  </Story>
161
179
  </template>