@morscherlab/mint-sdk 1.1.25 → 1.2.0-beta.10

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 (403) hide show
  1. package/README.md +44 -12
  2. package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
  3. package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
  5. package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
  6. package/dist/{SettingsModal-BW3Ar-uy.js → SettingsModal-Dphh3RrI.js} +5 -8
  7. package/dist/SettingsModal-Dphh3RrI.js.map +1 -0
  8. package/dist/{EmptyState-p90quq8Z.js → Skeleton-DxtwJ5gt.js} +111 -93
  9. package/dist/Skeleton-DxtwJ5gt.js.map +1 -0
  10. package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
  11. package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
  12. package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
  13. package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
  14. package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
  16. package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
  17. package/dist/components/ActionMenu.vue.d.ts +1 -1
  18. package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
  19. package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
  20. package/dist/components/AppSidebar.vue.d.ts +1 -1
  21. package/dist/components/AuditTrail.vue.d.ts +13 -0
  22. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  23. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  24. package/dist/components/DataFrame.vue.d.ts +14 -2
  25. package/dist/components/DatePicker.vue.d.ts +1 -1
  26. package/dist/components/DateTimePicker.vue.d.ts +2 -0
  27. package/dist/components/DropdownButton.vue.d.ts +2 -0
  28. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  29. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  30. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  31. package/dist/components/FitPanel.vue.d.ts +1 -1
  32. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  33. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  34. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  35. package/dist/components/ProgressBar.vue.d.ts +1 -1
  36. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  37. package/dist/components/ReagentList.vue.d.ts +29 -1
  38. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  39. package/dist/components/SampleSelector.drag.d.ts +13 -0
  40. package/dist/components/SampleSelector.tree.d.ts +2 -0
  41. package/dist/components/SampleSelector.vue.d.ts +34 -1
  42. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  43. package/dist/components/SecretInput.vue.d.ts +28 -0
  44. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
  45. package/dist/components/TimePicker.vue.d.ts +1 -0
  46. package/dist/components/WellPlate.geometry.d.ts +15 -0
  47. package/dist/components/WellPlate.interaction.d.ts +13 -0
  48. package/dist/components/WellPlate.vue.d.ts +29 -0
  49. package/dist/components/index.d.ts +9 -0
  50. package/dist/components/index.js +8 -8
  51. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  52. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  53. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  54. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  55. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  56. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  57. package/dist/{components-DQt0tcpY.js → components-CgAUzF8F.js} +2491 -2235
  58. package/dist/components-CgAUzF8F.js.map +1 -0
  59. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  60. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  61. package/dist/composables/index.d.ts +8 -2
  62. package/dist/composables/index.js +10 -10
  63. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  64. package/dist/composables/useAsync.d.ts +5 -1
  65. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  66. package/dist/composables/useCommandHistory.d.ts +5 -1
  67. package/dist/composables/useDropdownState.d.ts +18 -2
  68. package/dist/composables/useEventStream.d.ts +52 -0
  69. package/dist/composables/useFocusTrap.d.ts +21 -0
  70. package/dist/composables/useJobsStatusTray.d.ts +1 -1
  71. package/dist/composables/useListReorder.d.ts +90 -0
  72. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  73. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  74. package/dist/composables/usePluginClient.d.ts +4 -17
  75. package/dist/composables/usePluginConfig.d.ts +3 -1
  76. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  77. package/dist/composables/useRequestSyncState.d.ts +42 -2
  78. package/dist/composables/useResourceCrud.d.ts +5 -1
  79. package/dist/composables/useRovingFocus.d.ts +24 -0
  80. package/dist/composables/useScheduleDrag.d.ts +1 -0
  81. package/dist/composables/useWellPainting.d.ts +5 -1
  82. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  83. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  84. package/dist/composables/workspaceRecipes.d.ts +188 -0
  85. package/dist/{composables-CkXDe7nm.js → composables-B_pia3hO.js} +105 -254
  86. package/dist/composables-B_pia3hO.js.map +1 -0
  87. package/dist/{useApi-CDBq6z23.js → errors-_W0-LbA6.js} +102 -3
  88. package/dist/errors-_W0-LbA6.js.map +1 -0
  89. package/dist/{experiment-Y9Iz7KYi.js → experiment-Cwl9AYxw.js} +3 -8
  90. package/dist/experiment-Cwl9AYxw.js.map +1 -0
  91. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
  92. package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +15 -15
  95. package/dist/install.js +2 -7
  96. package/dist/install.js.map +1 -1
  97. package/dist/permissions.d.ts +42 -1
  98. package/dist/permissions.js +20 -10
  99. package/dist/permissions.js.map +1 -1
  100. package/dist/stores/index.d.ts +1 -1
  101. package/dist/stores/index.js +3 -3
  102. package/dist/stores/settings.d.ts +31 -1
  103. package/dist/styles.css +11247 -29501
  104. package/dist/templates/builderReadUtils.d.ts +1 -1
  105. package/dist/templates/index.js +2 -4
  106. package/dist/{templates-CovvRZ-9.js → templates-Qq113pyZ.js} +303 -106
  107. package/dist/templates-Qq113pyZ.js.map +1 -0
  108. package/dist/types/form-builder.d.ts +1 -1
  109. package/dist/{useControlSchema-BhoJzWzx.js → useControlSchema-aMZlb4Sc.js} +684 -1216
  110. package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
  111. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
  112. package/dist/useDropdownState-DitCtrTT.js.map +1 -0
  113. package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-I8HC8hUu.js} +4 -4
  114. package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
  115. package/dist/{useFormBuilder-DiEcDis2.js → useFormBuilder-TCxNnqeb.js} +2 -2
  116. package/dist/{useFormBuilder-DiEcDis2.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
  117. package/dist/{useProtocolTemplates-DHwSveE6.js → useProtocolTemplates-BmX_iVBR.js} +660 -197
  118. package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
  119. package/dist/utils/color.d.ts +13 -0
  120. package/dist/utils/errors.d.ts +9 -0
  121. package/dist/utils/pluginSecrets.d.ts +53 -0
  122. package/dist/utils/records.d.ts +4 -0
  123. package/package.json +1 -1
  124. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  125. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  126. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  127. package/src/__tests__/components/DataFrame.test.ts +183 -11
  128. package/src/__tests__/components/DataStates.test.ts +176 -0
  129. package/src/__tests__/components/DatePicker.test.ts +76 -0
  130. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  131. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  132. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  133. package/src/__tests__/components/GeneratedPluginView.test.ts +0 -5
  134. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  135. package/src/__tests__/components/JobsStatusTray.test.ts +20 -18
  136. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  137. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  138. package/src/__tests__/components/RackEditor.test.ts +35 -0
  139. package/src/__tests__/components/ReagentList.test.ts +64 -2
  140. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  141. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  142. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  143. package/src/__tests__/components/SecretInput.test.ts +87 -0
  144. package/src/__tests__/components/SegmentedControl.test.ts +88 -0
  145. package/src/__tests__/components/TimePicker.test.ts +61 -0
  146. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  147. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  148. package/src/__tests__/composables/useAuth.test.ts +25 -0
  149. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  150. package/src/__tests__/composables/useEventStream.test.ts +140 -0
  151. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +103 -0
  152. package/src/__tests__/composables/useJobsStatusTray.test.ts +25 -161
  153. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  154. package/src/__tests__/composables/usePluginClient.test.ts +148 -13
  155. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  156. package/src/__tests__/docs/frontendDocsCatalog.test.ts +55 -2
  157. package/src/__tests__/stores/settings.test.ts +71 -1
  158. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  159. package/src/__tests__/utils/permissions.test.ts +68 -0
  160. package/src/components/ActionMenu.vue +45 -88
  161. package/src/components/AlertBox.vue +0 -4
  162. package/src/components/AppAvatarMenu.vue +26 -7
  163. package/src/components/AppLayout.vue +0 -4
  164. package/src/components/AppPluginSwitcher.story.vue +87 -0
  165. package/src/components/AppPluginSwitcher.vue +2 -5
  166. package/src/components/AppSidebar.vue +0 -4
  167. package/src/components/AppToastContainer.vue +6 -8
  168. package/src/components/AppTopBar.vue +0 -4
  169. package/src/components/ArtifactSaveDialog.vue +0 -4
  170. package/src/components/ArtifactSelectorModal.vue +0 -4
  171. package/src/components/AuditTrail.story.vue +18 -0
  172. package/src/components/AuditTrail.vue +27 -7
  173. package/src/components/AutoGroupModal.vue +0 -4
  174. package/src/components/Avatar.vue +0 -4
  175. package/src/components/BaseButton.vue +0 -4
  176. package/src/components/BaseCheckbox.vue +0 -4
  177. package/src/components/BaseInput.vue +0 -4
  178. package/src/components/BaseModal.vue +8 -60
  179. package/src/components/BasePill.vue +0 -4
  180. package/src/components/BaseRadioGroup.vue +0 -4
  181. package/src/components/BaseSelect.vue +0 -4
  182. package/src/components/BaseSlider.vue +0 -4
  183. package/src/components/BaseTabs.vue +26 -5
  184. package/src/components/BaseTextarea.vue +0 -4
  185. package/src/components/BaseToggle.vue +0 -4
  186. package/src/components/BatchProgressList.story.vue +18 -0
  187. package/src/components/BatchProgressList.vue +32 -4
  188. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  189. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  190. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  191. package/src/components/Breadcrumb.vue +0 -4
  192. package/src/components/Calendar.vue +0 -4
  193. package/src/components/ChartContainer.story.vue +1 -1
  194. package/src/components/ChartContainer.vue +0 -4
  195. package/src/components/ChemicalFormula.vue +0 -4
  196. package/src/components/CollapsibleCard.vue +0 -4
  197. package/src/components/ColorSlider.vue +0 -4
  198. package/src/components/ComponentBindingRenderer.vue +1 -3
  199. package/src/components/ConcentrationInput.vue +0 -4
  200. package/src/components/ConfirmDialog.vue +0 -4
  201. package/src/components/ControlWorkspaceView.vue +11 -22
  202. package/src/components/DataFrame.story.vue +14 -0
  203. package/src/components/DataFrame.vue +97 -23
  204. package/src/components/DatePicker.vue +73 -27
  205. package/src/components/DateTimePicker.vue +45 -17
  206. package/src/components/Divider.vue +0 -4
  207. package/src/components/DoseCalculator.vue +0 -4
  208. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  209. package/src/components/DropdownButton.vue +24 -6
  210. package/src/components/EmptyState.vue +0 -4
  211. package/src/components/ExperimentCodeBadge.vue +0 -4
  212. package/src/components/ExperimentDataViewer.story.vue +3 -0
  213. package/src/components/ExperimentDataViewer.vue +33 -13
  214. package/src/components/ExperimentPopover.vue +2 -5
  215. package/src/components/ExperimentSelectorModal.vue +0 -4
  216. package/src/components/ExperimentTimeline.story.vue +18 -0
  217. package/src/components/ExperimentTimeline.vue +81 -75
  218. package/src/components/FileUploader.vue +0 -4
  219. package/src/components/FitPanel.vue +0 -4
  220. package/src/components/FormActions.story.vue +96 -0
  221. package/src/components/FormActions.vue +0 -4
  222. package/src/components/FormBuilder.vue +0 -4
  223. package/src/components/FormField.vue +0 -4
  224. package/src/components/FormulaInput.vue +0 -4
  225. package/src/components/GeneratedPathInput.story.vue +68 -0
  226. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  227. package/src/components/GeneratedPlotlyResult.vue +3 -3
  228. package/src/components/GeneratedPluginView.story.vue +145 -0
  229. package/src/components/GeneratedPluginView.vue +10 -10
  230. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  231. package/src/components/GeneratedResultRenderer.vue +4 -4
  232. package/src/components/GroupAssigner.vue +67 -5
  233. package/src/components/IconButton.vue +0 -4
  234. package/src/components/InstrumentAlertLog.vue +0 -4
  235. package/src/components/InstrumentStateBadge.vue +0 -4
  236. package/src/components/InstrumentStatusCard.vue +0 -4
  237. package/src/components/JobsStatusTray.story.vue +6 -10
  238. package/src/components/JobsStatusTray.vue +0 -4
  239. package/src/components/LcmsSequenceTable.vue +81 -50
  240. package/src/components/LoadingSpinner.vue +0 -4
  241. package/src/components/MobileSupportGate.vue +0 -4
  242. package/src/components/MoleculeInput.vue +0 -4
  243. package/src/components/MultiSelect.vue +0 -4
  244. package/src/components/NumberInput.vue +0 -4
  245. package/src/components/PlateMapEditor.vue +3 -14
  246. package/src/components/PluginIcon.vue +0 -4
  247. package/src/components/PluginWorkspaceView.props.ts +38 -0
  248. package/src/components/PluginWorkspaceView.vue +53 -224
  249. package/src/components/ProgressBar.vue +0 -4
  250. package/src/components/ProtocolStepEditor.vue +2 -4
  251. package/src/components/RackEditor.vue +51 -70
  252. package/src/components/ReagentEditor.vue +0 -4
  253. package/src/components/ReagentList.story.vue +18 -0
  254. package/src/components/ReagentList.vue +127 -82
  255. package/src/components/ResourceCard.vue +0 -4
  256. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  257. package/src/components/RuntimeUpdateNotice.vue +4 -4
  258. package/src/components/SampleHierarchyTree.story.vue +18 -0
  259. package/src/components/SampleHierarchyTree.vue +32 -9
  260. package/src/components/SampleLegend.vue +2 -8
  261. package/src/components/SampleSelector.drag.ts +51 -0
  262. package/src/components/SampleSelector.story.vue +6 -0
  263. package/src/components/SampleSelector.tree.ts +2 -0
  264. package/src/components/SampleSelector.vue +49 -10
  265. package/src/components/SampleSelectorGroupNode.vue +19 -0
  266. package/src/components/ScheduleCalendar.story.vue +6 -0
  267. package/src/components/ScheduleCalendar.vue +82 -7
  268. package/src/components/ScientificNumber.vue +0 -4
  269. package/src/components/SecretInput.story.vue +79 -0
  270. package/src/components/SecretInput.vue +150 -0
  271. package/src/components/SectionCard.vue +0 -4
  272. package/src/components/SegmentedControl.vue +48 -5
  273. package/src/components/SequenceInput.vue +0 -4
  274. package/src/components/SequenceProgressBar.story.vue +110 -0
  275. package/src/components/SequenceProgressBar.vue +0 -4
  276. package/src/components/SettingsModal.vue +0 -4
  277. package/src/components/Skeleton.vue +0 -4
  278. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  279. package/src/components/SmartGroupManual.vue +0 -4
  280. package/src/components/StatusIndicator.vue +0 -4
  281. package/src/components/StepWizard.vue +0 -4
  282. package/src/components/TagsInput.vue +4 -6
  283. package/src/components/ThemeToggle.vue +0 -4
  284. package/src/components/TimePicker.vue +36 -7
  285. package/src/components/TimeRangeInput.vue +0 -4
  286. package/src/components/Tooltip.story.vue +3 -3
  287. package/src/components/Tooltip.vue +0 -4
  288. package/src/components/UnitInput.vue +0 -4
  289. package/src/components/WellPlate.geometry.ts +60 -1
  290. package/src/components/WellPlate.interaction.ts +44 -0
  291. package/src/components/WellPlate.story.vue +8 -0
  292. package/src/components/WellPlate.vue +53 -7
  293. package/src/components/index.ts +9 -0
  294. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  295. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  296. package/src/components/internal/DataStateInternal.vue +74 -0
  297. package/src/components/internal/FormSectionRenderer.vue +0 -4
  298. package/src/components/internal/WellEditPopupInternal.vue +20 -5
  299. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  300. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  301. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  302. package/src/composables/controlSchemaTypes.ts +34 -0
  303. package/src/composables/experimentDesignData.ts +1 -3
  304. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  305. package/src/composables/formBuilderRegistry.ts +5 -0
  306. package/src/composables/index.ts +41 -5
  307. package/src/composables/pluginWorkspaceModel.ts +182 -0
  308. package/src/composables/useAsync.ts +5 -1
  309. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  310. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  311. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  312. package/src/composables/useCommandHistory.ts +5 -1
  313. package/src/composables/useDropdownState.ts +35 -5
  314. package/src/composables/useEventStream.ts +326 -0
  315. package/src/composables/useFileImport.ts +2 -7
  316. package/src/composables/useFocusTrap.ts +91 -0
  317. package/src/composables/useGeneratedAnalysis.ts +46 -103
  318. package/src/composables/useJobsStatusTray.ts +1 -1
  319. package/src/composables/useListReorder.story.vue +143 -0
  320. package/src/composables/useListReorder.ts +242 -0
  321. package/src/composables/useMenuKeyboard.ts +90 -0
  322. package/src/composables/useOptimisticMutation.ts +6 -16
  323. package/src/composables/usePluginClient.ts +91 -252
  324. package/src/composables/usePluginConfig.ts +3 -1
  325. package/src/composables/usePluginWorkspace.ts +208 -0
  326. package/src/composables/useRackEditor.ts +2 -11
  327. package/src/composables/useRequestSyncState.ts +98 -28
  328. package/src/composables/useResourceCrud.ts +6 -16
  329. package/src/composables/useRovingFocus.ts +66 -0
  330. package/src/composables/useScheduleDrag.ts +37 -0
  331. package/src/composables/useWellPainting.ts +10 -30
  332. package/src/composables/useWellPlateAdapter.ts +5 -1
  333. package/src/composables/useWellPlateEditor.ts +1 -11
  334. package/src/composables/useWellPlateValidation.ts +5 -1
  335. package/src/composables/workspaceRecipes.ts +267 -0
  336. package/src/index.ts +16 -0
  337. package/src/permissions.ts +48 -9
  338. package/src/stores/experiment.ts +2 -7
  339. package/src/stores/index.ts +9 -1
  340. package/src/stores/settings.ts +93 -1
  341. package/src/styles/components/audit-trail.css +5 -5
  342. package/src/styles/components/batch-progress-list.css +7 -0
  343. package/src/styles/components/color-slider.css +2 -2
  344. package/src/styles/components/data-state.css +30 -0
  345. package/src/styles/components/dataframe.css +29 -35
  346. package/src/styles/components/experiment-data-viewer.css +4 -4
  347. package/src/styles/components/experiment-timeline.css +3 -30
  348. package/src/styles/components/form-builder.css +5 -5
  349. package/src/styles/components/lcms-sequence-table.css +16 -1
  350. package/src/styles/components/multi-select.css +1 -1
  351. package/src/styles/components/reagent-list.css +38 -31
  352. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  353. package/src/styles/components/sample-selector.css +9 -2
  354. package/src/styles/components/schedule-calendar.css +8 -0
  355. package/src/styles/components/scientific-number.css +1 -1
  356. package/src/styles/components/secret-input.css +60 -0
  357. package/src/styles/components/well-plate.css +8 -0
  358. package/src/styles/index.css +9 -0
  359. package/src/styles/variables.css +29 -0
  360. package/src/templates/builderPresetControls.ts +1 -1
  361. package/src/templates/builderReadUtils.ts +1 -3
  362. package/src/templates/componentPlateHelpers.ts +3 -1
  363. package/src/templates/componentTargetResolvers.ts +1 -3
  364. package/src/templates/controlSchemaTargets.ts +1 -3
  365. package/src/templates/doseResponseAdapters.ts +2 -13
  366. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  367. package/src/templates/plateMapBuilder.ts +2 -2
  368. package/src/templates/qpcrPlateBuilder.ts +1 -1
  369. package/src/templates/templateValidators.ts +1 -1
  370. package/src/templates/timeCourseBuilder.ts +2 -2
  371. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  372. package/src/types/form-builder.ts +1 -0
  373. package/src/utils/color.ts +51 -0
  374. package/src/utils/errors.ts +29 -0
  375. package/src/utils/formModelSync.ts +1 -3
  376. package/src/utils/pluginIcon.ts +1 -3
  377. package/src/utils/pluginSecrets.ts +102 -0
  378. package/src/utils/records.ts +9 -0
  379. package/dist/EmptyState-p90quq8Z.js.map +0 -1
  380. package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
  381. package/dist/ExperimentSelectorModal-B6P2msT4.js.map +0 -1
  382. package/dist/SettingsModal-BW3Ar-uy.js.map +0 -1
  383. package/dist/components-DQt0tcpY.js.map +0 -1
  384. package/dist/composables/autoGroupConstants.d.ts +0 -1
  385. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  386. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  387. package/dist/composables-CkXDe7nm.js.map +0 -1
  388. package/dist/experiment-Y9Iz7KYi.js.map +0 -1
  389. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  390. package/dist/templates/builderDefaults.d.ts +0 -1
  391. package/dist/templates/plateGeometry.d.ts +0 -4
  392. package/dist/templates-CovvRZ-9.js.map +0 -1
  393. package/dist/useApi-CDBq6z23.js.map +0 -1
  394. package/dist/useControlSchema-BhoJzWzx.js.map +0 -1
  395. package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
  396. package/dist/useProtocolTemplates-DHwSveE6.js.map +0 -1
  397. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  398. package/src/composables/autoGroupConstants.ts +0 -4
  399. package/src/composables/autoGroupGrouping.ts +0 -148
  400. package/src/composables/useAutoGroupInputSources.ts +0 -147
  401. package/src/templates/builderDefaults.ts +0 -11
  402. package/src/templates/plateGeometry.ts +0 -62
  403. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → components/DataStates.test.d.ts} +0 -0
@@ -25,6 +25,7 @@ import {
25
25
  removeSampleFromGroup as removeSampleFromSampleGroup,
26
26
  removeSampleGroups,
27
27
  } from './SampleSelector.groups'
28
+ import DataStateInternal from './internal/DataStateInternal.vue'
28
29
 
29
30
  interface Props {
30
31
  samples?: string[]
@@ -35,6 +36,15 @@ interface Props {
35
36
  experimentId?: number
36
37
  designData?: Record<string, unknown>
37
38
  autoloadExperimentData?: boolean
39
+ /** Replace the selector body with a loading placeholder. */
40
+ loading?: boolean
41
+ /**
42
+ * Error message. When set, the selector body is replaced by an error state.
43
+ * `null`/`undefined` renders normally.
44
+ */
45
+ error?: string | null
46
+ /** Empty-state headline shown when there are no samples to select from. */
47
+ emptyMessage?: string
38
48
  }
39
49
 
40
50
  const props = withDefaults(defineProps<Props>(), {
@@ -45,6 +55,9 @@ const props = withDefaults(defineProps<Props>(), {
45
55
  experimentId: undefined,
46
56
  designData: undefined,
47
57
  autoloadExperimentData: true,
58
+ loading: false,
59
+ error: null,
60
+ emptyMessage: 'No samples available',
48
61
  })
49
62
 
50
63
  const emit = defineEmits<{
@@ -69,6 +82,8 @@ const internalGroups = computed({
69
82
  })
70
83
 
71
84
  const {
85
+ announcement: reorderAnnouncement,
86
+ announcerStyle,
72
87
  draggingSample,
73
88
  dragOverGroup,
74
89
  draggingGroup,
@@ -81,6 +96,7 @@ const {
81
96
  handleDragLeave,
82
97
  handleDrop,
83
98
  handleGroupDragStart,
99
+ handleGroupReorderKeydown,
84
100
  handleGroupDragEnd,
85
101
  handleGroupDragOver,
86
102
  handleGroupDragLeave,
@@ -227,6 +243,7 @@ const treeContext = computed<SampleGroupTreeContext>(() => ({
227
243
  onGroupDragOver: handleGroupDragOver,
228
244
  onGroupDragLeave: handleGroupDragLeave,
229
245
  onGroupDrop: handleGroupDrop,
246
+ onGroupReorderKeydown: handleGroupReorderKeydown,
230
247
  }))
231
248
 
232
249
  // New group
@@ -237,13 +254,36 @@ function addNewGroup() {
237
254
  newGroupName.value = ''
238
255
  }
239
256
 
257
+ /** True while a loading, error or empty placeholder stands in for the body. */
258
+ const isPlaceholderState = computed(() =>
259
+ props.loading || Boolean(props.error) || resolvedSamples.value.length === 0,
260
+ )
261
+
240
262
  defineExpose({ handleSmartGroupApply })
241
263
  </script>
242
264
 
243
265
  <template>
244
266
  <div class="mint-sample-selector">
267
+ <div v-if="error" class="mint-sample-selector__state">
268
+ <slot name="error" :error="error">
269
+ <DataStateInternal state="error" size="sm" :title="error" />
270
+ </slot>
271
+ </div>
272
+
273
+ <div v-else-if="loading" class="mint-sample-selector__state">
274
+ <slot name="loading">
275
+ <DataStateInternal state="loading" size="sm" loading-label="Loading samples" />
276
+ </slot>
277
+ </div>
278
+
279
+ <div v-else-if="resolvedSamples.length === 0" class="mint-sample-selector__state">
280
+ <slot name="empty">
281
+ <DataStateInternal state="empty" size="sm" :title="emptyMessage" />
282
+ </slot>
283
+ </div>
284
+
245
285
  <!-- Select All Row -->
246
- <label class="mint-sample-selector__select-all">
286
+ <label v-if="!isPlaceholderState" class="mint-sample-selector__select-all">
247
287
  <input
248
288
  type="checkbox"
249
289
  :checked="isAllSelected"
@@ -255,7 +295,7 @@ defineExpose({ handleSmartGroupApply })
255
295
  </label>
256
296
 
257
297
  <!-- Action Buttons Row -->
258
- <div v-if="enableGrouping" class="mint-sample-selector__actions">
298
+ <div v-if="!isPlaceholderState && enableGrouping" class="mint-sample-selector__actions">
259
299
  <div class="mint-sample-selector__actions-row">
260
300
  <!-- Grouping Button -->
261
301
  <BaseButton
@@ -291,7 +331,7 @@ defineExpose({ handleSmartGroupApply })
291
331
  </div>
292
332
 
293
333
  <!-- Grouped View -->
294
- <div v-if="groupingEnabled" class="mint-sample-selector__grouped">
334
+ <div v-if="!isPlaceholderState && groupingEnabled" class="mint-sample-selector__grouped">
295
335
  <!-- Groups Header -->
296
336
  <div class="mint-sample-selector__groups-header">
297
337
  <span class="mint-sample-selector__groups-title">Groups ({{ internalGroups.length }})</span>
@@ -320,7 +360,7 @@ defineExpose({ handleSmartGroupApply })
320
360
  />
321
361
 
322
362
  <!-- Empty state -->
323
- <div v-if="internalGroups.length === 0" class="mint-sample-selector__empty">
363
+ <div v-if="internalGroups.length === 0" class="mint-sample-selector__hint">
324
364
  Click Smart to auto-group samples
325
365
  </div>
326
366
  </div>
@@ -394,7 +434,7 @@ defineExpose({ handleSmartGroupApply })
394
434
  </div>
395
435
 
396
436
  <!-- Flat View (when no groups) -->
397
- <div v-if="!groupingEnabled" class="mint-sample-selector__flat">
437
+ <div v-if="!isPlaceholderState && !groupingEnabled" class="mint-sample-selector__flat">
398
438
  <!-- Search -->
399
439
  <div class="mint-sample-selector__search">
400
440
  <svg class="mint-sample-selector__search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -427,7 +467,7 @@ defineExpose({ handleSmartGroupApply })
427
467
  <span class="mint-sample-selector__flat-name">{{ sample }}</span>
428
468
  </div>
429
469
 
430
- <div v-if="filteredSamples.length === 0 && searchQuery.trim()" class="mint-sample-selector__empty">
470
+ <div v-if="filteredSamples.length === 0 && searchQuery.trim()" class="mint-sample-selector__hint">
431
471
  No samples match "{{ searchQuery }}"
432
472
  </div>
433
473
  </div>
@@ -443,9 +483,8 @@ defineExpose({ handleSmartGroupApply })
443
483
  :design-data="resolvedDesignData"
444
484
  @apply="handleSmartGroupApply"
445
485
  />
486
+
487
+ <!-- Group reorder announcements for screen readers -->
488
+ <div role="status" aria-live="polite" :style="announcerStyle">{{ reorderAnnouncement }}</div>
446
489
  </div>
447
490
  </template>
448
-
449
- <style>
450
- @import '../styles/components/sample-selector.css';
451
- </style>
@@ -54,6 +54,18 @@ function onHeaderDrop(event: DragEvent) {
54
54
  if (dragKind.value === null) return
55
55
  props.ctx.onGroupDrop(dragName.value, dragKind.value, event)
56
56
  }
57
+
58
+ /** Enter/Space expand the node; Alt+ArrowUp/Down reorder it among its siblings. */
59
+ function onHeaderKeydown(event: KeyboardEvent) {
60
+ if (event.altKey) {
61
+ if (dragKind.value === null) return
62
+ props.ctx.onGroupReorderKeydown(dragName.value, dragKind.value, event)
63
+ return
64
+ }
65
+ if (event.key !== 'Enter' && event.key !== ' ') return
66
+ event.preventDefault()
67
+ props.ctx.toggleExpanded(props.node.key)
68
+ }
57
69
  </script>
58
70
 
59
71
  <template>
@@ -81,7 +93,14 @@ function onHeaderDrop(event: DragEvent) {
81
93
  reorderEdge === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',
82
94
  ]"
83
95
  :draggable="dragKind !== null"
96
+ role="button"
97
+ tabindex="0"
98
+ :aria-expanded="expanded"
99
+ :aria-label="dragKind !== null
100
+ ? `${node.label}. Press Alt with the up or down arrow keys to reorder.`
101
+ : node.label"
84
102
  @click="ctx.toggleExpanded(node.key)"
103
+ @keydown="onHeaderKeydown"
85
104
  @dragstart="onHeaderDragStart"
86
105
  @dragend="ctx.onGroupDragEnd"
87
106
  @dragover="onHeaderDragOver"
@@ -86,6 +86,8 @@ const mockBlockedSlots: ScheduleBlockedSlot[] = [
86
86
 
87
87
  function initState() {
88
88
  return {
89
+ loading: false,
90
+ error: '',
89
91
  modelValue: today,
90
92
  view: 'week' as const,
91
93
  events: mockEvents,
@@ -106,6 +108,8 @@ function initState() {
106
108
  <template #default="{ state }">
107
109
  <div style="padding: 1rem; height: 700px;">
108
110
  <ScheduleCalendar
111
+ :loading="state.loading"
112
+ :error="state.error || null"
109
113
  v-model="state.modelValue"
110
114
  :view="state.view"
111
115
  :events="state.events"
@@ -123,6 +127,8 @@ function initState() {
123
127
  </div>
124
128
  </template>
125
129
  <template #controls="{ state }">
130
+ <HstCheckbox v-model="state.loading" title="Loading" />
131
+ <HstText v-model="state.error" title="Error Message" />
126
132
  <HstSelect
127
133
  v-model="state.view"
128
134
  title="View"
@@ -7,11 +7,13 @@ import type {
7
7
  ScheduleBlockedSlot,
8
8
  } from '../types/components'
9
9
  import { useScheduleDrag } from '../composables/useScheduleDrag'
10
+ import { useReorderAnnouncer } from '../composables/useListReorder'
10
11
  import {
11
12
  SCHEDULE_SLOT_HEIGHT,
12
13
  parseScheduleModelDate,
13
14
  useScheduleCalendarLayout,
14
15
  } from '../composables/useScheduleCalendarLayout'
16
+ import DataStateInternal from './internal/DataStateInternal.vue'
15
17
 
16
18
  interface Props {
17
19
  modelValue?: Date | string
@@ -28,6 +30,19 @@ interface Props {
28
30
  showNavigation?: boolean
29
31
  statusColors?: Record<string, string>
30
32
  locale?: string
33
+ /** Replace the calendar grid with a loading placeholder. */
34
+ loading?: boolean
35
+ /**
36
+ * Error message. When set, the calendar grid is replaced by an error state.
37
+ * `null`/`undefined` renders normally.
38
+ */
39
+ error?: string | null
40
+ /**
41
+ * Opt-in empty-state headline. An empty grid is meaningful on a calendar, so
42
+ * the empty state only replaces it when this message is supplied and there
43
+ * are no events.
44
+ */
45
+ emptyMessage?: string
31
46
  }
32
47
 
33
48
  const props = withDefaults(defineProps<Props>(), {
@@ -43,6 +58,8 @@ const props = withDefaults(defineProps<Props>(), {
43
58
  showViewToggle: true,
44
59
  showNavigation: true,
45
60
  locale: 'en-US',
61
+ loading: false,
62
+ error: null,
46
63
  })
47
64
 
48
65
  const emit = defineEmits<{
@@ -57,6 +74,11 @@ const emit = defineEmits<{
57
74
 
58
75
  const SLOT_HEIGHT = SCHEDULE_SLOT_HEIGHT
59
76
  const currentView = ref<ScheduleView>(props.view)
77
+
78
+ /** Empty state is opt-in: it needs an `emptyMessage` and an event-free range. */
79
+ const showEmptyState = computed(() =>
80
+ Boolean(props.emptyMessage) && props.events.length === 0,
81
+ )
60
82
  const currentDate = ref<Date>(parseScheduleModelDate(props.modelValue))
61
83
  const nowTimer = ref<ReturnType<typeof setInterval>>()
62
84
  const now = ref(new Date())
@@ -76,7 +98,9 @@ const weekStartsOnRef = computed(() => props.weekStartsOn)
76
98
  const localeRef = computed(() => props.locale)
77
99
  const showNowIndicatorRef = computed(() => props.showNowIndicator)
78
100
 
79
- const { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({
101
+ const { announcement, announce, announcerStyle } = useReorderAnnouncer()
102
+
103
+ const { isDragging, ghost, startCreate, startMove, startResize, nudgeEvent } = useScheduleDrag({
80
104
  slotDuration: slotDurationRef,
81
105
  dayStartHour: dayStartRef,
82
106
  dayEndHour: dayEndRef,
@@ -180,6 +204,35 @@ function onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIn
180
204
  startResize(event, edge, e.clientY, dayIndex)
181
205
  }
182
206
 
207
+ /**
208
+ * Keyboard alternative to dragging an event: Alt+ArrowUp/Down move it by one
209
+ * slot, Shift+Alt+ArrowUp/Down shorten or lengthen it, Enter/Space open it.
210
+ */
211
+ function onEventKeydown(event: ScheduleEvent, e: KeyboardEvent) {
212
+ if (e.key === 'Enter' || e.key === ' ') {
213
+ e.preventDefault()
214
+ emit('event-click', event)
215
+ return
216
+ }
217
+ if (!e.altKey) return
218
+ const direction = e.key === 'ArrowUp' ? -1 : e.key === 'ArrowDown' ? 1 : 0
219
+ if (direction === 0) return
220
+
221
+ e.preventDefault()
222
+ const mode = e.shiftKey ? 'resize' : 'move'
223
+ const applied = nudgeEvent(event, direction, mode)
224
+ announce(applied
225
+ ? `${event.title} ${mode === 'move' ? 'moved' : 'resized'} to ${formatEventTime(event)}.`
226
+ : `${event.title} cannot move further.`)
227
+ }
228
+
229
+ /** Instructions announced with each event. */
230
+ function eventLabel(event: ScheduleEvent): string {
231
+ const base = `${event.title}, ${formatEventTime(event)}`
232
+ if (props.readonly || event.draggable === false) return base
233
+ return `${base}. Press Alt with the up or down arrow keys to reschedule it.`
234
+ }
235
+
183
236
  function onMonthDayClick(date: Date) {
184
237
  currentDate.value = date
185
238
  emit('update:modelValue', date)
@@ -254,8 +307,27 @@ onUnmounted(() => {
254
307
  </div>
255
308
  </div>
256
309
 
310
+ <!-- Loading / error / empty placeholders replace the grid -->
311
+ <div v-if="error" class="mint-schedule__state">
312
+ <slot name="error" :error="error">
313
+ <DataStateInternal state="error" :title="error" />
314
+ </slot>
315
+ </div>
316
+
317
+ <div v-else-if="loading" class="mint-schedule__state">
318
+ <slot name="loading">
319
+ <DataStateInternal state="loading" loading-label="Loading schedule" />
320
+ </slot>
321
+ </div>
322
+
323
+ <div v-else-if="showEmptyState" class="mint-schedule__state">
324
+ <slot name="empty">
325
+ <DataStateInternal state="empty" :title="emptyMessage" />
326
+ </slot>
327
+ </div>
328
+
257
329
  <!-- Day / Week View -->
258
- <template v-if="currentView === 'day' || currentView === 'week'">
330
+ <template v-else-if="currentView === 'day' || currentView === 'week'">
259
331
  <!-- Day headers -->
260
332
  <div v-if="currentView === 'week'" class="mint-schedule__day-headers">
261
333
  <div class="mint-schedule__day-header-gutter" />
@@ -312,7 +384,11 @@ onUnmounted(() => {
312
384
  :key="event.id"
313
385
  :class="['mint-schedule__event', getStatusClass(event.status)]"
314
386
  :style="getEventStyle(event, day) || undefined"
387
+ role="button"
388
+ tabindex="0"
389
+ :aria-label="eventLabel(event)"
315
390
  @click="onEventClick(event, $event)"
391
+ @keydown="onEventKeydown(event, $event)"
316
392
  @pointerdown="onEventPointerDown(event, dayIdx, $event)"
317
393
  >
318
394
  <slot name="event" :event="event">
@@ -364,7 +440,7 @@ onUnmounted(() => {
364
440
  </template>
365
441
 
366
442
  <!-- Month View -->
367
- <template v-if="currentView === 'month'">
443
+ <template v-else-if="currentView === 'month'">
368
444
  <div class="mint-schedule__month-grid">
369
445
  <!-- Weekday headers -->
370
446
  <div
@@ -406,9 +482,8 @@ onUnmounted(() => {
406
482
  </div>
407
483
  </div>
408
484
  </template>
485
+
486
+ <!-- Reschedule announcements for screen readers -->
487
+ <div role="status" aria-live="polite" :style="announcerStyle">{{ announcement }}</div>
409
488
  </div>
410
489
  </template>
411
-
412
- <style>
413
- @import '../styles/components/schedule-calendar.css';
414
- </style>
@@ -191,7 +191,3 @@ async function copyToClipboard() {
191
191
  </button>
192
192
  </span>
193
193
  </template>
194
-
195
- <style>
196
- @import '../styles/components/scientific-number.css';
197
- </style>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import SecretInput from './SecretInput.vue'
4
+ import type { PluginSecretRef } from '../utils/pluginSecrets'
5
+
6
+ const sizes = ['sm', 'md', 'lg'] as const
7
+
8
+ const storedRef: PluginSecretRef = { $secret: 'sec_0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e', set: true }
9
+ const lockedRef: PluginSecretRef = { $secret: null, locked: true }
10
+
11
+ const stored = ref<PluginSecretRef | string | null>({ ...storedRef })
12
+ const unset = ref<PluginSecretRef | string | null>(null)
13
+ const locked = ref<PluginSecretRef | string | null>({ ...lockedRef })
14
+ </script>
15
+
16
+ <template>
17
+ <Story title="Base Inputs/SecretInput">
18
+ <Variant title="Playground">
19
+ <template #default="{ state }">
20
+ <div style="padding: 2rem; max-width: 600px;">
21
+ <SecretInput
22
+ v-model="state.value"
23
+ :placeholder="state.placeholder"
24
+ :disabled="state.disabled"
25
+ :error="state.error"
26
+ :size="state.size"
27
+ />
28
+ <p style="margin-top: 1rem; font-size: 0.875rem; color: var(--text-muted, #94a3b8);">
29
+ Submits: <code>{{ JSON.stringify(state.value) }}</code>
30
+ </p>
31
+ </div>
32
+ </template>
33
+
34
+ <template #controls="{ state }">
35
+ <HstText v-model="state.placeholder" title="Placeholder" />
36
+ <HstSelect
37
+ v-model="state.size"
38
+ title="Size"
39
+ :options="sizes.map(size => ({ label: size, value: size }))"
40
+ />
41
+ <HstCheckbox v-model="state.disabled" title="Disabled" />
42
+ <HstCheckbox v-model="state.error" title="Error" />
43
+ </template>
44
+
45
+ <template #initState>
46
+ {{ {
47
+ value: { $secret: 'sec_0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e', set: true },
48
+ placeholder: 'Enter a new value',
49
+ disabled: false,
50
+ error: false,
51
+ size: 'md',
52
+ } }}
53
+ </template>
54
+ </Variant>
55
+
56
+ <Variant title="States">
57
+ <div style="padding: 2rem; max-width: 600px; display: flex; flex-direction: column; gap: 1.5rem;">
58
+ <div>
59
+ <p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
60
+ Stored
61
+ </p>
62
+ <SecretInput v-model="stored" />
63
+ </div>
64
+ <div>
65
+ <p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
66
+ Not set
67
+ </p>
68
+ <SecretInput v-model="unset" />
69
+ </div>
70
+ <div>
71
+ <p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
72
+ Provided by environment
73
+ </p>
74
+ <SecretInput v-model="locked" />
75
+ </div>
76
+ </div>
77
+ </Variant>
78
+ </Story>
79
+ </template>
@@ -0,0 +1,150 @@
1
+ <script setup lang="ts">
2
+ /** Edits one plugin secret setting: shows set / not set / provided by environment, and submits a `$secret` reference, a new value, or `null`. */
3
+ import { computed, ref, watch } from 'vue'
4
+ import BaseInput from './BaseInput.vue'
5
+ import BaseButton from './BaseButton.vue'
6
+ import {
7
+ clearPluginSecret,
8
+ isPluginSecretLocked,
9
+ isPluginSecretRef,
10
+ keepPluginSecret,
11
+ pluginSecretLabel,
12
+ pluginSecretState,
13
+ setPluginSecret,
14
+ type PluginSecretRef,
15
+ type PluginSecretState,
16
+ } from '../utils/pluginSecrets'
17
+
18
+ type SecretValue = PluginSecretRef | string | null
19
+
20
+ interface Props {
21
+ /** The `$secret` marker from the settings response, a typed string, or `null`. */
22
+ modelValue?: SecretValue
23
+ placeholder?: string
24
+ disabled?: boolean
25
+ error?: boolean
26
+ size?: 'sm' | 'md' | 'lg'
27
+ /** Label on the control that starts entering a replacement value. */
28
+ changeLabel?: string
29
+ /** Label on the control that clears a stored secret. */
30
+ clearLabel?: string
31
+ }
32
+
33
+ const props = withDefaults(defineProps<Props>(), {
34
+ modelValue: null,
35
+ placeholder: 'Enter a new value',
36
+ disabled: false,
37
+ error: false,
38
+ size: 'md',
39
+ changeLabel: 'Change',
40
+ clearLabel: 'Clear',
41
+ })
42
+
43
+ const emit = defineEmits<{
44
+ 'update:modelValue': [value: SecretValue]
45
+ }>()
46
+
47
+ /** The reference this field started from, so an abandoned edit can restore it. */
48
+ const storedReference = ref<PluginSecretRef | null>(keepPluginSecret(props.modelValue))
49
+ const isEditing = ref(typeof props.modelValue === 'string')
50
+
51
+ watch(
52
+ () => props.modelValue,
53
+ (value) => {
54
+ if (isPluginSecretRef(value)) {
55
+ storedReference.value = keepPluginSecret(value)
56
+ isEditing.value = false
57
+ }
58
+ },
59
+ )
60
+
61
+ const state = computed<PluginSecretState>(() => pluginSecretState(props.modelValue))
62
+ const locked = computed(() => isPluginSecretLocked(props.modelValue)
63
+ || isPluginSecretLocked(storedReference.value))
64
+ const statusLabel = computed(() => isEditing.value
65
+ ? 'New value'
66
+ : pluginSecretLabel(props.modelValue))
67
+ const typedValue = computed(() => typeof props.modelValue === 'string' ? props.modelValue : '')
68
+ const canClear = computed(() => !locked.value && (state.value === 'set' || isEditing.value))
69
+
70
+ function startEditing(): void {
71
+ if (props.disabled || locked.value) return
72
+ isEditing.value = true
73
+ emit('update:modelValue', setPluginSecret(''))
74
+ }
75
+
76
+ function updateTyped(value: string | number | null): void {
77
+ emit('update:modelValue', setPluginSecret(value == null ? '' : String(value)))
78
+ }
79
+
80
+ function cancelEditing(): void {
81
+ isEditing.value = false
82
+ emit('update:modelValue', storedReference.value ? { ...storedReference.value } : null)
83
+ }
84
+
85
+ function clear(): void {
86
+ if (props.disabled || locked.value) return
87
+ isEditing.value = false
88
+ storedReference.value = null
89
+ emit('update:modelValue', clearPluginSecret())
90
+ }
91
+ </script>
92
+
93
+ <template>
94
+ <div class="mint-secret-input" :class="{ 'mint-secret-input--error': error }">
95
+ <div class="mint-secret-input__status">
96
+ <span
97
+ class="mint-secret-input__badge"
98
+ :class="`mint-secret-input__badge--${isEditing ? 'pending' : state}`"
99
+ >{{ statusLabel }}</span>
100
+ <span v-if="locked" class="mint-secret-input__hint">
101
+ This value comes from the environment and cannot be edited here.
102
+ </span>
103
+ </div>
104
+
105
+ <BaseInput
106
+ v-if="isEditing"
107
+ type="password"
108
+ autocomplete="new-password"
109
+ :model-value="typedValue"
110
+ :placeholder="placeholder"
111
+ :disabled="disabled"
112
+ :error="error"
113
+ :size="size"
114
+ @update:model-value="updateTyped"
115
+ />
116
+
117
+ <div class="mint-secret-input__actions">
118
+ <BaseButton
119
+ v-if="!isEditing"
120
+ type="button"
121
+ variant="secondary"
122
+ :size="size === 'lg' ? 'md' : 'sm'"
123
+ :disabled="disabled || locked"
124
+ @click="startEditing"
125
+ >
126
+ {{ changeLabel }}
127
+ </BaseButton>
128
+ <BaseButton
129
+ v-else
130
+ type="button"
131
+ variant="ghost"
132
+ :size="size === 'lg' ? 'md' : 'sm'"
133
+ :disabled="disabled"
134
+ @click="cancelEditing"
135
+ >
136
+ Cancel
137
+ </BaseButton>
138
+ <BaseButton
139
+ v-if="canClear"
140
+ type="button"
141
+ variant="ghost"
142
+ :size="size === 'lg' ? 'md' : 'sm'"
143
+ :disabled="disabled"
144
+ @click="clear"
145
+ >
146
+ {{ clearLabel }}
147
+ </BaseButton>
148
+ </div>
149
+ </div>
150
+ </template>
@@ -92,7 +92,3 @@ const cardClasses = computed(() => [
92
92
  </div>
93
93
  </section>
94
94
  </template>
95
-
96
- <style>
97
- @import '../styles/components/section-card.css';
98
- </style>