@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,7 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /** Multi-rack editor for managing collections of well-plate racks with add/remove/reorder controls and per-well editing. */
3
- import { ref, computed, watch } from 'vue'
3
+ import { computed, watch, type ComponentPublicInstance } from 'vue'
4
+ import { SLOT_COLORS, SLOT_ORDER } from '../utils/color'
4
5
  import type { Rack, SlotPosition, WellPlateFormat, WellPlateSize, WellEditData, Well, WellEditField, WellSampleDropData, RackSampleDropMapper } from '../types'
6
+ import { useListReorder } from '../composables/useListReorder'
5
7
  import { useRackEditor } from '../composables/useRackEditor'
6
8
  import WellPlate from './WellPlate.vue'
7
9
 
@@ -104,23 +106,48 @@ watch(
104
106
  },
105
107
  )
106
108
 
107
- const SLOT_COLORS: Record<SlotPosition, string> = {
108
- R: '#ef4444',
109
- G: '#22c55e',
110
- B: '#3b82f6',
111
- Y: '#eab308',
112
- }
113
-
114
- const SLOT_OPTIONS: SlotPosition[] = ['R', 'G', 'B', 'Y']
109
+ const SLOT_OPTIONS: readonly SlotPosition[] = SLOT_ORDER
115
110
 
116
111
  const FORMAT_OPTIONS: { value: WellPlateFormat; label: string }[] = [
117
112
  { value: 54, label: '54' },
118
113
  { value: 96, label: '96' },
119
114
  ]
120
115
 
121
- // Drag state for rack reordering
122
- const draggingRackId = ref<string | null>(null)
123
- const dragOverRackId = ref<string | null>(null)
116
+ // Rack tab reordering: HTML5 drag for pointers, Alt+Arrow for the keyboard,
117
+ // both owned by the shared reorder composable.
118
+ const tabRefs = new Map<string, HTMLElement>()
119
+
120
+ const {
121
+ draggingId: draggingRackId,
122
+ dragOverId: dragOverRackId,
123
+ announcement: reorderAnnouncement,
124
+ announcerStyle,
125
+ onDragStart: onRackDragStart,
126
+ onDragOver: onRackDragOver,
127
+ onDragLeave: onRackDragLeave,
128
+ onDrop: onRackDrop,
129
+ onDragEnd: onRackDragEnd,
130
+ onKeydown: onRackReorderKeydown,
131
+ } = useListReorder<Rack>({
132
+ items: () => editor.racks.value,
133
+ getId: (rack) => rack.id,
134
+ getLabel: (rack) => rack.name,
135
+ disabled: () => !props.allowReorder || props.readonly,
136
+ getElement: (id) => tabRefs.get(id),
137
+ itemNoun: 'Rack',
138
+ onReorder: ({ fromIndex, toIndex }) => {
139
+ editor.reorderRacks(fromIndex, toIndex)
140
+ emit('rack-reorder', editor.racks.value.map(r => r.id))
141
+ },
142
+ })
143
+
144
+ function setTabRef(id: string, el: Element | ComponentPublicInstance | null) {
145
+ if (el instanceof HTMLElement) {
146
+ tabRefs.set(id, el)
147
+ } else {
148
+ tabRefs.delete(id)
149
+ }
150
+ }
124
151
 
125
152
  function handleAddRack() {
126
153
  const rack = editor.addRack()
@@ -136,54 +163,6 @@ function handleTabClick(rackId: string) {
136
163
  editor.setActiveRack(rackId)
137
164
  }
138
165
 
139
- // Rack drag handlers
140
- function handleRackDragStart(event: DragEvent, rackId: string) {
141
- if (!props.allowReorder) return
142
- draggingRackId.value = rackId
143
- if (event.dataTransfer) {
144
- event.dataTransfer.effectAllowed = 'move'
145
- event.dataTransfer.setData('text/plain', rackId)
146
- }
147
- }
148
-
149
- function handleRackDragOver(event: DragEvent, rackId: string) {
150
- event.preventDefault()
151
- if (draggingRackId.value && draggingRackId.value !== rackId) {
152
- dragOverRackId.value = rackId
153
- }
154
- }
155
-
156
- function handleRackDragLeave() {
157
- dragOverRackId.value = null
158
- }
159
-
160
- function handleRackDrop(event: DragEvent, targetRackId: string) {
161
- event.preventDefault()
162
- if (!draggingRackId.value || draggingRackId.value === targetRackId) {
163
- clearDragState()
164
- return
165
- }
166
-
167
- const fromIndex = editor.racks.value.findIndex(r => r.id === draggingRackId.value)
168
- const toIndex = editor.racks.value.findIndex(r => r.id === targetRackId)
169
-
170
- if (fromIndex !== -1 && toIndex !== -1) {
171
- editor.reorderRacks(fromIndex, toIndex)
172
- emit('rack-reorder', editor.racks.value.map(r => r.id))
173
- }
174
-
175
- clearDragState()
176
- }
177
-
178
- function handleRackDragEnd() {
179
- clearDragState()
180
- }
181
-
182
- function clearDragState() {
183
- draggingRackId.value = null
184
- dragOverRackId.value = null
185
- }
186
-
187
166
  // Toolbar actions
188
167
  function handleFormatChange(format: WellPlateFormat) {
189
168
  if (!editor.activeRack.value) return
@@ -281,9 +260,11 @@ const activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})
281
260
  <div class="mint-rack-editor__tabs">
282
261
  <div class="mint-rack-editor__tabs-inner">
283
262
  <button
284
- v-for="rack in editor.racks.value"
263
+ v-for="(rack, rackIndex) in editor.racks.value"
285
264
  :key="rack.id"
265
+ :ref="(el) => setTabRef(rack.id, el)"
286
266
  :draggable="allowReorder && !readonly"
267
+ :title="allowReorder && !readonly ? `${rack.name} — press Alt with the arrow keys to reorder` : undefined"
287
268
  :class="[
288
269
  'mint-rack-editor__tab',
289
270
  {
@@ -293,11 +274,12 @@ const activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})
293
274
  },
294
275
  ]"
295
276
  @click="handleTabClick(rack.id)"
296
- @dragstart="handleRackDragStart($event, rack.id)"
297
- @dragover="handleRackDragOver($event, rack.id)"
298
- @dragleave="handleRackDragLeave"
299
- @drop="handleRackDrop($event, rack.id)"
300
- @dragend="handleRackDragEnd"
277
+ @keydown="onRackReorderKeydown($event, rack, rackIndex)"
278
+ @dragstart="onRackDragStart($event, rack, rackIndex)"
279
+ @dragover="onRackDragOver($event, rack, rackIndex)"
280
+ @dragleave="onRackDragLeave"
281
+ @drop="onRackDrop($event, rack, rackIndex)"
282
+ @dragend="onRackDragEnd"
301
283
  >
302
284
  <span
303
285
  class="mint-rack-editor__slot-dot"
@@ -434,9 +416,8 @@ const activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})
434
416
  </template>
435
417
  </WellPlate>
436
418
  </div>
419
+
420
+ <!-- Reorder announcements for screen readers -->
421
+ <div role="status" aria-live="polite" :style="announcerStyle">{{ reorderAnnouncement }}</div>
437
422
  </div>
438
423
  </template>
439
-
440
- <style>
441
- @import '../styles/components/rack-editor.css';
442
- </style>
@@ -412,7 +412,3 @@ function formatLevel(value: number): string {
412
412
  </div>
413
413
  </div>
414
414
  </template>
415
-
416
- <style>
417
- @import '../styles/components/reagent-editor.css';
418
- </style>
@@ -76,6 +76,8 @@ const mockReagents: Reagent[] = [
76
76
 
77
77
  function initState() {
78
78
  return {
79
+ loading: false,
80
+ error: '',
79
81
  modelValue: mockReagents,
80
82
  readonly: false,
81
83
  showStockLevel: true,
@@ -92,6 +94,8 @@ function initState() {
92
94
  <template #default="{ state }">
93
95
  <div style="padding: 2rem;">
94
96
  <ReagentList
97
+ :loading="state.loading"
98
+ :error="state.error || null"
95
99
  v-model="state.modelValue"
96
100
  :readonly="state.readonly"
97
101
  :show-stock-level="state.showStockLevel"
@@ -102,6 +106,8 @@ function initState() {
102
106
  </div>
103
107
  </template>
104
108
  <template #controls="{ state }">
109
+ <HstCheckbox v-model="state.loading" title="Loading" />
110
+ <HstText v-model="state.error" title="Error Message" />
105
111
  <HstCheckbox v-model="state.readonly" title="Readonly" />
106
112
  <HstCheckbox v-model="state.showStockLevel" title="Show Stock Level" />
107
113
  <HstSlider v-model="state.lowStockThreshold" title="Low Stock Threshold (%)" :min="0" :max="50" />
@@ -133,5 +139,17 @@ function initState() {
133
139
  <ReagentList :model-value="[]" />
134
140
  </div>
135
141
  </Variant>
142
+
143
+ <Variant title="Loading">
144
+ <div style="padding: 2rem;">
145
+ <ReagentList :model-value="[]" loading />
146
+ </div>
147
+ </Variant>
148
+
149
+ <Variant title="Error">
150
+ <div style="padding: 2rem;">
151
+ <ReagentList :model-value="[]" error="Could not load reagents." />
152
+ </div>
153
+ </Variant>
136
154
  </Story>
137
155
  </template>
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  /** Sortable, searchable table of experiment reagents showing lot number, expiry, storage conditions, and stock level with low-stock warnings. */
3
- import { ref, computed } from 'vue'
3
+ import { ref, computed, type ComponentPublicInstance } from 'vue'
4
4
  import type { ReagentColumn, Reagent } from '../types'
5
+ import { useListReorder } from '../composables/useListReorder'
5
6
  import { useSortedItems } from '../composables/useSortedItems'
6
7
  import { useTextSearch } from '../composables/useTextSearch'
7
8
  import {
@@ -14,6 +15,13 @@ import {
14
15
  isReagentExpired as isExpired,
15
16
  isReagentExpiringSoon as isExpiringSoon,
16
17
  } from './ReagentList.presentation'
18
+ import DataStateInternal from './internal/DataStateInternal.vue'
19
+
20
+ /** Beaker glyph kept from the pre-1.2 hand-rolled empty state. */
21
+ const EMPTY_ICON_PATH
22
+ = 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0'
23
+ + ' 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26'
24
+ + ' 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z'
17
25
 
18
26
  interface Props {
19
27
  modelValue?: Reagent[]
@@ -23,6 +31,13 @@ interface Props {
23
31
  columns?: ReagentColumn[]
24
32
  sortable?: boolean
25
33
  searchable?: boolean
34
+ /** Replace the table with a loading placeholder. */
35
+ loading?: boolean
36
+ /**
37
+ * Error message. When set, the table is replaced by an error state.
38
+ * `null`/`undefined` renders normally.
39
+ */
40
+ error?: string | null
26
41
  }
27
42
 
28
43
  const props = withDefaults(defineProps<Props>(), {
@@ -33,6 +48,8 @@ const props = withDefaults(defineProps<Props>(), {
33
48
  columns: () => ['name', 'lot', 'expiry', 'storage', 'stock'],
34
49
  sortable: true,
35
50
  searchable: true,
51
+ loading: false,
52
+ error: null,
36
53
  })
37
54
 
38
55
  const emit = defineEmits<{
@@ -47,8 +64,7 @@ const emit = defineEmits<{
47
64
  const searchQuery = ref('')
48
65
  const sortColumn = ref<ReagentColumn | null>(null)
49
66
  const sortDirection = ref<'asc' | 'desc'>('asc')
50
- const draggedId = ref<string | null>(null)
51
- const dragOverId = ref<string | null>(null)
67
+ const dragHandleRefs = new Map<string, HTMLElement>()
52
68
 
53
69
  const reagentSearch = useTextSearch({
54
70
  items: () => props.modelValue || [],
@@ -78,14 +94,63 @@ const sortedReagents = useSortedItems<Reagent, ReagentColumn>({
78
94
  })
79
95
  const filteredReagents = sortedReagents.sortedItems
80
96
 
97
+ // Drag-and-drop plus the Alt+Arrow keyboard alternative live in the shared
98
+ // reorder composable, which also announces every move politely.
99
+ const {
100
+ draggingId,
101
+ dragOverId,
102
+ announcement: reorderAnnouncement,
103
+ announcerStyle,
104
+ onDragStart,
105
+ onDragOver,
106
+ onDragLeave,
107
+ onDrop,
108
+ onDragEnd,
109
+ onKeydown: onReorderKeydown,
110
+ } = useListReorder<Reagent>({
111
+ items: () => props.modelValue || [],
112
+ getId: (reagent) => reagent.id,
113
+ getLabel: (reagent) => reagent.name,
114
+ disabled: () => props.readonly || !props.sortable,
115
+ getElement: (id) => dragHandleRefs.get(id),
116
+ itemNoun: 'Reagent',
117
+ onReorder: ({ fromIndex, toIndex, items }) => {
118
+ emit('reorder', items[toIndex].id, fromIndex, toIndex)
119
+ emit('update:modelValue', items)
120
+ },
121
+ })
122
+
123
+ function setDragHandleRef(id: string, el: Element | ComponentPublicInstance | null) {
124
+ if (el instanceof HTMLElement) {
125
+ dragHandleRefs.set(id, el)
126
+ } else {
127
+ dragHandleRefs.delete(id)
128
+ }
129
+ }
130
+
81
131
  function getRowClass(reagent: Reagent): string[] {
82
132
  return getReagentRowClasses(reagent, {
83
133
  lowStockThreshold: props.lowStockThreshold,
84
- draggedId: draggedId.value,
134
+ draggedId: draggingId.value,
85
135
  dragOverId: dragOverId.value,
86
136
  })
87
137
  }
88
138
 
139
+ /** `aria-sort` for a column header, omitted while sorting is off. */
140
+ function columnAriaSort(column: ReagentColumn): 'ascending' | 'descending' | 'none' | undefined {
141
+ if (!props.sortable) return undefined
142
+ if (sortColumn.value !== column) return 'none'
143
+ return sortDirection.value === 'asc' ? 'ascending' : 'descending'
144
+ }
145
+
146
+ // Native buttons activate on Enter/Space; the default is prevented so a header
147
+ // sorts exactly once per key press in every engine.
148
+ function handleSortKeydown(event: KeyboardEvent, column: ReagentColumn) {
149
+ if (event.key !== 'Enter' && event.key !== ' ') return
150
+ event.preventDefault()
151
+ handleSort(column)
152
+ }
153
+
89
154
  // Actions
90
155
  function handleSort(column: ReagentColumn) {
91
156
  if (!props.sortable) return
@@ -111,54 +176,7 @@ function handleAdd() {
111
176
  emit('add')
112
177
  }
113
178
 
114
- // Drag and drop
115
- function handleDragStart(event: DragEvent, reagent: Reagent) {
116
- if (props.readonly || !props.sortable) return
117
- draggedId.value = reagent.id
118
- if (event.dataTransfer) {
119
- event.dataTransfer.effectAllowed = 'move'
120
- event.dataTransfer.setData('text/plain', reagent.id)
121
- }
122
- }
123
-
124
- function handleDragOver(event: DragEvent, reagent: Reagent) {
125
- if (props.readonly || !props.sortable || !draggedId.value) return
126
- event.preventDefault()
127
- dragOverId.value = reagent.id
128
- }
129
-
130
- function handleDragLeave() {
131
- dragOverId.value = null
132
- }
133
-
134
- function handleDrop(event: DragEvent, targetReagent: Reagent) {
135
- if (props.readonly || !props.sortable || !draggedId.value) return
136
- event.preventDefault()
137
-
138
- const reagents = [...(props.modelValue || [])]
139
- const fromIndex = reagents.findIndex((r) => r.id === draggedId.value)
140
- const toIndex = reagents.findIndex((r) => r.id === targetReagent.id)
141
-
142
- if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {
143
- draggedId.value = null
144
- dragOverId.value = null
145
- return
146
- }
147
179
 
148
- const [removed] = reagents.splice(fromIndex, 1)
149
- reagents.splice(toIndex, 0, removed)
150
-
151
- emit('reorder', draggedId.value, fromIndex, toIndex)
152
- emit('update:modelValue', reagents)
153
-
154
- draggedId.value = null
155
- dragOverId.value = null
156
- }
157
-
158
- function handleDragEnd() {
159
- draggedId.value = null
160
- dragOverId.value = null
161
- }
162
180
  </script>
163
181
 
164
182
  <template>
@@ -193,7 +211,10 @@ function handleDragEnd() {
193
211
  </div>
194
212
 
195
213
  <!-- Table -->
196
- <table v-if="filteredReagents.length > 0" class="mint-reagent-list__table">
214
+ <table
215
+ v-if="!loading && !error && filteredReagents.length > 0"
216
+ class="mint-reagent-list__table"
217
+ >
197
218
  <thead>
198
219
  <tr>
199
220
  <th v-if="sortable && !readonly" style="width: 24px" />
@@ -201,8 +222,15 @@ function handleDragEnd() {
201
222
  v-for="col in columns"
202
223
  :key="col"
203
224
  :class="[sortable ? 'mint-reagent-list__col--sortable' : '']"
204
- @click="handleSort(col)"
225
+ :aria-sort="columnAriaSort(col)"
205
226
  >
227
+ <component
228
+ :is="sortable ? 'button' : 'span'"
229
+ :type="sortable ? 'button' : undefined"
230
+ class="mint-reagent-list__sort-btn"
231
+ @click="handleSort(col)"
232
+ @keydown="sortable ? handleSortKeydown($event, col) : undefined"
233
+ >
206
234
  {{ columnLabels[col] }}
207
235
  <svg
208
236
  v-if="sortable"
@@ -229,27 +257,36 @@ function handleDragEnd() {
229
257
  d="M5 15l7-7 7 7"
230
258
  />
231
259
  </svg>
260
+ </component>
232
261
  </th>
233
262
  <th v-if="!readonly" style="width: 80px" />
234
263
  </tr>
235
264
  </thead>
236
265
  <tbody>
237
266
  <tr
238
- v-for="reagent in filteredReagents"
267
+ v-for="(reagent, index) in filteredReagents"
239
268
  :key="reagent.id"
240
269
  :class="getRowClass(reagent)"
241
270
  :draggable="sortable && !readonly"
242
- @dragstart="handleDragStart($event, reagent)"
243
- @dragover="handleDragOver($event, reagent)"
244
- @dragleave="handleDragLeave"
245
- @drop="handleDrop($event, reagent)"
246
- @dragend="handleDragEnd"
271
+ @dragstart="onDragStart($event, reagent, index)"
272
+ @dragover="onDragOver($event, reagent, index)"
273
+ @dragleave="onDragLeave"
274
+ @drop="onDrop($event, reagent, index)"
275
+ @dragend="onDragEnd"
247
276
  >
248
- <!-- Drag handle -->
277
+ <!-- Drag handle: also the keyboard reorder target (Alt+Arrow keys). -->
249
278
  <td v-if="sortable && !readonly">
250
- <svg class="mint-reagent-list__drag-handle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
251
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16" />
252
- </svg>
279
+ <button
280
+ :ref="(el) => setDragHandleRef(reagent.id, el)"
281
+ type="button"
282
+ class="mint-reagent-list__drag-handle-btn"
283
+ :aria-label="`Reorder ${reagent.name}. Press Alt with the arrow keys to move it.`"
284
+ @keydown="onReorderKeydown($event, reagent, index)"
285
+ >
286
+ <svg class="mint-reagent-list__drag-handle" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
287
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16" />
288
+ </svg>
289
+ </button>
253
290
  </td>
254
291
 
255
292
  <!-- Name -->
@@ -367,26 +404,34 @@ function handleDragEnd() {
367
404
  </tbody>
368
405
  </table>
369
406
 
407
+ <!-- Error state -->
408
+ <div v-else-if="error" class="mint-reagent-list__state">
409
+ <slot name="error" :error="error">
410
+ <DataStateInternal state="error" :title="error" />
411
+ </slot>
412
+ </div>
413
+
414
+ <!-- Loading state -->
415
+ <div v-else-if="loading" class="mint-reagent-list__state">
416
+ <slot name="loading">
417
+ <DataStateInternal state="loading" loading-label="Loading reagents" />
418
+ </slot>
419
+ </div>
420
+
370
421
  <!-- Empty state -->
371
- <div v-else class="mint-reagent-list__empty">
372
- <svg class="mint-reagent-list__empty-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
373
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
374
- </svg>
375
- <p class="mint-reagent-list__empty-text">
376
- {{ searchQuery ? 'No reagents found' : 'No reagents added yet' }}
377
- </p>
378
- <button
379
- v-if="!readonly && !searchQuery"
380
- type="button"
381
- class="mint-reagent-list__empty-btn"
382
- @click="handleAdd"
383
- >
384
- Add Reagent
385
- </button>
422
+ <div v-else class="mint-reagent-list__state">
423
+ <slot name="empty">
424
+ <DataStateInternal
425
+ state="empty"
426
+ :title="searchQuery ? 'No reagents found' : 'No reagents added yet'"
427
+ :icon-path="EMPTY_ICON_PATH"
428
+ :action-label="!readonly && !searchQuery ? 'Add Reagent' : undefined"
429
+ @action="handleAdd"
430
+ />
431
+ </slot>
386
432
  </div>
433
+
434
+ <!-- Reorder announcements for screen readers -->
435
+ <div role="status" aria-live="polite" :style="announcerStyle">{{ reorderAnnouncement }}</div>
387
436
  </div>
388
437
  </template>
389
-
390
- <style>
391
- @import '../styles/components/reagent-list.css';
392
- </style>
@@ -155,7 +155,3 @@ function handleClick() {
155
155
  </div>
156
156
  </div>
157
157
  </template>
158
-
159
- <style>
160
- @import '../styles/components/resource-card.css';
161
- </style>
@@ -0,0 +1,49 @@
1
+ <script setup lang="ts">
2
+ import RuntimeUpdateNotice from './RuntimeUpdateNotice.vue'
3
+
4
+ function handleReload() {
5
+ console.log('reload requested')
6
+ }
7
+
8
+ function initState() {
9
+ return {
10
+ visible: true,
11
+ }
12
+ }
13
+ </script>
14
+
15
+ <template>
16
+ <Story title="Feedback/RuntimeUpdateNotice">
17
+ <Variant title="Playground" :init-state="initState">
18
+ <template #default="{ state }">
19
+ <!-- The notice is `position: fixed`, so it anchors to the preview
20
+ viewport rather than this wrapper. -->
21
+ <div style="padding: 2rem; min-height: 14rem; background: var(--bg-primary);">
22
+ <p style="margin: 0; font-size: 0.8125rem; color: var(--text-secondary);">
23
+ Shown after the platform detects a new frontend build.
24
+ </p>
25
+ <RuntimeUpdateNotice :visible="state.visible" @reload="handleReload" />
26
+ </div>
27
+ </template>
28
+
29
+ <template #controls="{ state }">
30
+ <HstCheckbox v-model="state.visible" title="Visible" />
31
+ </template>
32
+ </Variant>
33
+
34
+ <Variant title="Visible">
35
+ <div style="padding: 2rem; min-height: 14rem; background: var(--bg-primary);">
36
+ <RuntimeUpdateNotice :visible="true" @reload="handleReload" />
37
+ </div>
38
+ </Variant>
39
+
40
+ <Variant title="Hidden">
41
+ <div style="padding: 2rem; min-height: 8rem; background: var(--bg-primary);">
42
+ <p style="margin: 0; font-size: 0.8125rem; color: var(--text-secondary);">
43
+ Nothing renders while <code>visible</code> is false.
44
+ </p>
45
+ <RuntimeUpdateNotice :visible="false" @reload="handleReload" />
46
+ </div>
47
+ </Variant>
48
+ </Story>
49
+ </template>
@@ -30,9 +30,9 @@ const emit = defineEmits<{
30
30
  align-items: center;
31
31
  gap: 10px;
32
32
  padding: 10px 12px;
33
- color: var(--mint-text-primary, #172033);
34
- background: var(--mint-bg-primary, #fff);
35
- border: 1px solid var(--mint-border-default, #d9e1ec);
33
+ color: var(--text-primary);
34
+ background: var(--bg-card);
35
+ border: 1px solid var(--border-color);
36
36
  border-radius: 10px;
37
37
  box-shadow: 0 10px 28px rgb(15 23 42 / 16%);
38
38
  font-size: 13px;
@@ -42,7 +42,7 @@ const emit = defineEmits<{
42
42
  .mint-runtime-update button {
43
43
  padding: 5px 9px;
44
44
  color: #fff;
45
- background: var(--mint-color-primary, #0f9f8f);
45
+ background: var(--color-primary);
46
46
  border: 0;
47
47
  border-radius: 6px;
48
48
  font: inherit;
@@ -84,6 +84,8 @@ const mockNodes: TreeNode[] = [
84
84
 
85
85
  function initState() {
86
86
  return {
87
+ loading: false,
88
+ error: '',
87
89
  nodes: mockNodes,
88
90
  expandAll: false,
89
91
  showIcons: true,
@@ -99,6 +101,8 @@ function initState() {
99
101
  <template #default="{ state }">
100
102
  <div style="padding: 2rem; max-width: 500px;">
101
103
  <SampleHierarchyTree
104
+ :loading="state.loading"
105
+ :error="state.error || null"
102
106
  :nodes="state.nodes"
103
107
  :expand-all="state.expandAll"
104
108
  :show-icons="state.showIcons"
@@ -108,6 +112,8 @@ function initState() {
108
112
  </div>
109
113
  </template>
110
114
  <template #controls="{ state }">
115
+ <HstCheckbox v-model="state.loading" title="Loading" />
116
+ <HstText v-model="state.error" title="Error Message" />
111
117
  <HstCheckbox v-model="state.expandAll" title="Expand All" />
112
118
  <HstCheckbox v-model="state.showIcons" title="Show Icons" />
113
119
  <HstCheckbox v-model="state.showCounts" title="Show Counts" />
@@ -157,5 +163,17 @@ function initState() {
157
163
  <SampleHierarchyTree :nodes="[]" />
158
164
  </div>
159
165
  </Variant>
166
+
167
+ <Variant title="Loading">
168
+ <div style="padding: 2rem; max-width: 600px;">
169
+ <SampleHierarchyTree :nodes="[]" loading />
170
+ </div>
171
+ </Variant>
172
+
173
+ <Variant title="Error">
174
+ <div style="padding: 2rem; max-width: 600px;">
175
+ <SampleHierarchyTree :nodes="[]" error="Could not load the sample hierarchy." />
176
+ </div>
177
+ </Variant>
160
178
  </Story>
161
179
  </template>