@morscherlab/mint-sdk 1.2.0-beta.8 → 1.2.0
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.
- package/README.md +60 -0
- package/dist/{ExperimentPopover-CfOdh5Rm.js → ExperimentPopover-CJdNlCZb.js} +24 -15
- package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DvIagrxY.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
- package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
- package/dist/{SettingsModal-W3TYfnP9.js → SettingsModal-BYR20I4c.js} +5 -5
- package/dist/SettingsModal-BYR20I4c.js.map +1 -0
- package/dist/{EmptyState-NBqf2_8z.js → Skeleton-BAF3CKY7.js} +111 -93
- package/dist/Skeleton-BAF3CKY7.js.map +1 -0
- package/dist/__tests__/components/DataStates.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
- package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
- package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
- package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/apiErrors.d.ts +26 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/BaseInput.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +1 -1
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +2 -2
- package/dist/components/DateTimePicker.vue.d.ts +3 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
- package/dist/components/ExperimentPopover.vue.d.ts +1 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
- package/dist/components/FileBrowserModal.vue.d.ts +99 -0
- package/dist/components/FilePicker.vue.d.ts +32 -0
- package/dist/components/FileUploader.vue.d.ts +5 -2
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/MobileSupportGate.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlotlyChart.vue.d.ts +59 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
- package/dist/components/ReagentList.vue.d.ts +29 -1
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
- package/dist/components/SampleSelector.drag.d.ts +13 -0
- package/dist/components/SampleSelector.tree.d.ts +2 -0
- package/dist/components/SampleSelector.vue.d.ts +34 -1
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +1 -1
- package/dist/components/TimePicker.vue.d.ts +2 -1
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +1 -1
- package/dist/components/WellPlate.geometry.d.ts +15 -0
- package/dist/components/WellPlate.interaction.d.ts +13 -0
- package/dist/components/WellPlate.vue.d.ts +30 -1
- package/dist/components/fileBrowserPresentation.d.ts +10 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
- package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
- package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
- package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
- package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
- package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
- package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
- package/dist/{components-B80gV0P6.js → components-DIum5hkx.js} +5415 -2753
- package/dist/components-DIum5hkx.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/filePicker/localFiles.d.ts +3 -0
- package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
- package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
- package/dist/composables/filePicker/validation.d.ts +5 -0
- package/dist/composables/index.d.ts +3 -0
- package/dist/composables/index.js +10 -10
- package/dist/composables/useApi.d.ts +14 -7
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +9 -0
- package/dist/composables/useFileBrowser.d.ts +55 -0
- package/dist/composables/useFilePicker.d.ts +189 -0
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +2 -2
- package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
- package/dist/composables/usePluginConfig.d.ts +3 -1
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/{composables-C8mmD5ta.js → composables-mFXsTA4g.js} +307 -74
- package/dist/composables-mFXsTA4g.js.map +1 -0
- package/dist/{useApi-CDBq6z23.js → errors-BLuqMee5.js} +178 -16
- package/dist/errors-BLuqMee5.js.map +1 -0
- package/dist/{experiment-Y9Iz7KYi.js → experiment-CVDRAnqp.js} +3 -8
- package/dist/experiment-CVDRAnqp.js.map +1 -0
- package/dist/{experiment-utils-DV3DrvE_.js → experiment-utils-CV2Wza3z.js} +1 -1
- package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -15
- package/dist/install.js +2 -7
- package/dist/install.js.map +1 -1
- package/dist/permissions.d.ts +42 -1
- package/dist/permissions.js +20 -10
- package/dist/permissions.js.map +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +3 -3
- package/dist/stores/settings.d.ts +31 -1
- package/dist/styles.css +15162 -32475
- package/dist/templates/index.js +2 -4
- package/dist/{templates-CK72aKO1.js → templates-CmGfmBRA.js} +291 -85
- package/dist/templates-CmGfmBRA.js.map +1 -0
- package/dist/types/fileBrowserTypes.d.ts +77 -0
- package/dist/types/filePicker.d.ts +77 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/{useControlSchema-_OVhavKq.js → useControlSchema-DldpxNOP.js} +234 -1121
- package/dist/useControlSchema-DldpxNOP.js.map +1 -0
- package/dist/{useDropdownState-D5G4UvWd.js → useDropdownState-C7hRiVQf.js} +22 -6
- package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
- package/dist/{useExperimentSelector-DSEckf_g.js → useExperimentSelector-CEcIaueJ.js} +4 -4
- package/dist/{useExperimentSelector-DSEckf_g.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
- package/dist/{useFormBuilder-Bd714C7y.js → useFormBuilder-z5L_dUIy.js} +2 -2
- package/dist/{useFormBuilder-Bd714C7y.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/{useProtocolTemplates-Dq5Pzwyd.js → validation-uwoJGEb6.js} +231 -158
- package/dist/validation-uwoJGEb6.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/apiErrors.test.ts +81 -0
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/DataFrame.test.ts +183 -11
- package/src/__tests__/components/DataStates.test.ts +176 -0
- package/src/__tests__/components/DatePicker.test.ts +76 -0
- package/src/__tests__/components/DateTimePicker.test.ts +36 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
- package/src/__tests__/components/FilePicker.state.test.ts +287 -0
- package/src/__tests__/components/FilePicker.test.ts +487 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PlotlyChart.test.ts +305 -0
- package/src/__tests__/components/RackEditor.test.ts +35 -0
- package/src/__tests__/components/ReagentList.test.ts +64 -2
- package/src/__tests__/components/RetiredStyles.test.ts +31 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
- package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
- package/src/__tests__/components/SegmentedControl.test.ts +107 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
- package/src/__tests__/composables/useApi.test.ts +92 -9
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
- package/src/__tests__/composables/usePluginClient.test.ts +3 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/stores/settings.test.ts +71 -1
- package/src/__tests__/styles/cssTokens.test.ts +131 -0
- package/src/__tests__/utils/permissions.test.ts +68 -0
- package/src/apiErrors.ts +85 -0
- package/src/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +2 -14
- package/src/components/AppLayout.vue +0 -4
- package/src/components/AppPluginSwitcher.story.vue +87 -0
- package/src/components/AppPluginSwitcher.vue +2 -5
- package/src/components/AppSidebar.vue +0 -4
- package/src/components/AppToastContainer.vue +6 -8
- package/src/components/AppTopBar.vue +0 -4
- package/src/components/ArtifactSaveDialog.vue +0 -4
- package/src/components/ArtifactSelectorModal.vue +0 -4
- package/src/components/AuditTrail.story.vue +18 -0
- package/src/components/AuditTrail.vue +27 -7
- package/src/components/AutoGroupModal.vue +0 -4
- package/src/components/Avatar.vue +0 -4
- package/src/components/BaseButton.vue +0 -4
- package/src/components/BaseCheckbox.vue +0 -4
- package/src/components/BaseInput.vue +0 -4
- package/src/components/BaseModal.vue +0 -4
- package/src/components/BasePill.vue +0 -4
- package/src/components/BaseRadioGroup.vue +0 -4
- package/src/components/BaseSelect.vue +0 -4
- package/src/components/BaseSlider.vue +0 -4
- package/src/components/BaseTabs.vue +0 -4
- package/src/components/BaseTextarea.vue +0 -4
- package/src/components/BaseToggle.vue +0 -4
- package/src/components/BatchProgressList.story.vue +18 -0
- package/src/components/BatchProgressList.vue +32 -4
- package/src/components/Breadcrumb.vue +0 -4
- package/src/components/Calendar.vue +0 -4
- package/src/components/ChartContainer.story.vue +1 -1
- package/src/components/ChartContainer.vue +0 -4
- package/src/components/ChemicalFormula.vue +0 -4
- package/src/components/CollapsibleCard.vue +0 -4
- package/src/components/ColorSlider.vue +0 -4
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/DataFrame.story.vue +14 -0
- package/src/components/DataFrame.vue +97 -23
- package/src/components/DatePicker.vue +73 -27
- package/src/components/DateTimePicker.vue +45 -17
- package/src/components/Divider.vue +0 -4
- package/src/components/DoseCalculator.vue +0 -4
- package/src/components/DropdownButton.vue +2 -14
- package/src/components/EmptyState.vue +0 -4
- package/src/components/ExperimentCodeBadge.vue +0 -4
- package/src/components/ExperimentDataViewer.story.vue +3 -0
- package/src/components/ExperimentDataViewer.vue +33 -13
- package/src/components/ExperimentPopover.vue +12 -7
- package/src/components/ExperimentSelectorModal.vue +0 -4
- package/src/components/ExperimentTimeline.story.vue +18 -0
- package/src/components/ExperimentTimeline.vue +81 -75
- package/src/components/FileBrowserModal.story.vue +203 -0
- package/src/components/FileBrowserModal.vue +384 -0
- package/src/components/FilePicker.story.vue +44 -0
- package/src/components/FilePicker.vue +284 -0
- package/src/components/FileUploader.vue +32 -18
- package/src/components/FitPanel.vue +0 -4
- package/src/components/FormActions.story.vue +96 -0
- package/src/components/FormActions.vue +0 -4
- package/src/components/FormBuilder.vue +0 -4
- package/src/components/FormField.vue +0 -4
- package/src/components/FormulaInput.vue +0 -4
- package/src/components/GeneratedPathInput.story.vue +68 -0
- package/src/components/GeneratedPlotlyResult.story.vue +96 -0
- package/src/components/GeneratedPlotlyResult.vue +34 -69
- package/src/components/GeneratedPluginView.story.vue +145 -0
- package/src/components/GeneratedPluginView.vue +10 -10
- package/src/components/GeneratedResultRenderer.story.vue +117 -0
- package/src/components/GeneratedResultRenderer.vue +4 -9
- package/src/components/GroupAssigner.vue +67 -5
- package/src/components/IconButton.vue +0 -4
- package/src/components/InstrumentAlertLog.vue +0 -4
- package/src/components/InstrumentStateBadge.vue +0 -4
- package/src/components/InstrumentStatusCard.vue +0 -4
- package/src/components/JobsStatusTray.vue +0 -4
- package/src/components/LcmsSequenceTable.vue +81 -50
- package/src/components/LoadingSpinner.vue +0 -4
- package/src/components/MobileSupportGate.vue +0 -4
- package/src/components/MoleculeInput.vue +0 -4
- package/src/components/MultiSelect.vue +0 -4
- package/src/components/NumberInput.vue +0 -4
- package/src/components/PlateMapEditor.vue +3 -14
- package/src/components/PlotlyChart.story.vue +61 -0
- package/src/components/PlotlyChart.vue +233 -0
- package/src/components/PluginIcon.vue +0 -4
- package/src/components/ProgressBar.vue +0 -4
- package/src/components/ProtocolStepEditor.vue +2 -4
- package/src/components/RackEditor.vue +51 -70
- package/src/components/ReagentEditor.vue +0 -4
- package/src/components/ReagentList.story.vue +18 -0
- package/src/components/ReagentList.vue +127 -82
- package/src/components/ResourceCard.vue +0 -4
- package/src/components/RuntimeUpdateNotice.story.vue +49 -0
- package/src/components/RuntimeUpdateNotice.vue +4 -4
- package/src/components/SampleHierarchyTree.story.vue +18 -0
- package/src/components/SampleHierarchyTree.vue +32 -9
- package/src/components/SampleLegend.vue +2 -8
- package/src/components/SampleSelector.drag.ts +51 -0
- package/src/components/SampleSelector.story.vue +6 -0
- package/src/components/SampleSelector.tree.ts +2 -0
- package/src/components/SampleSelector.vue +49 -10
- package/src/components/SampleSelectorGroupNode.vue +19 -0
- package/src/components/ScheduleCalendar.story.vue +6 -0
- package/src/components/ScheduleCalendar.vue +82 -7
- package/src/components/ScientificNumber.vue +0 -4
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +50 -7
- package/src/components/SequenceInput.vue +0 -4
- package/src/components/SequenceProgressBar.story.vue +110 -0
- package/src/components/SequenceProgressBar.vue +0 -4
- package/src/components/SettingsModal.vue +0 -4
- package/src/components/Skeleton.vue +0 -4
- package/src/components/SmartGroupFieldRecipe.vue +8 -6
- package/src/components/SmartGroupManual.vue +0 -4
- package/src/components/StatusIndicator.vue +0 -4
- package/src/components/StepWizard.vue +0 -4
- package/src/components/TagsInput.vue +4 -6
- package/src/components/ThemeToggle.vue +0 -4
- package/src/components/TimePicker.vue +36 -7
- package/src/components/TimeRangeInput.vue +0 -4
- package/src/components/Tooltip.story.vue +3 -3
- package/src/components/Tooltip.vue +0 -4
- package/src/components/UnitInput.vue +0 -4
- package/src/components/WellPlate.geometry.ts +60 -1
- package/src/components/WellPlate.interaction.ts +44 -0
- package/src/components/WellPlate.story.vue +8 -0
- package/src/components/WellPlate.vue +53 -7
- package/src/components/fileBrowserPresentation.ts +39 -0
- package/src/components/index.ts +5 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
- package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
- package/src/components/internal/DataStateInternal.vue +74 -0
- package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
- package/src/components/internal/FileBrowserListingInternal.vue +197 -0
- package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
- package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
- package/src/components/internal/FilePickerTreeInternal.vue +238 -0
- package/src/components/internal/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +0 -4
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/filePicker/localFiles.ts +58 -0
- package/src/composables/filePicker/usePickerNavigation.ts +220 -0
- package/src/composables/filePicker/usePickerSelection.ts +259 -0
- package/src/composables/filePicker/validation.ts +41 -0
- package/src/composables/index.ts +17 -0
- package/src/composables/useApi.ts +57 -13
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +14 -2
- package/src/composables/useFileBrowser.ts +314 -0
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFilePicker.ts +366 -0
- package/src/composables/useGeneratedAnalysis.ts +18 -26
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +2 -2
- package/src/composables/useOptimisticMutation.ts +1 -15
- package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
- package/src/composables/usePluginClient.ts +3 -0
- package/src/composables/usePluginConfig.ts +3 -1
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +1 -15
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +5 -29
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/index.ts +3 -0
- package/src/permissions.ts +48 -9
- package/src/stores/experiment.ts +2 -7
- package/src/stores/index.ts +9 -1
- package/src/stores/settings.ts +93 -1
- package/src/styles/components/audit-trail.css +5 -5
- package/src/styles/components/batch-progress-list.css +7 -0
- package/src/styles/components/color-slider.css +2 -2
- package/src/styles/components/data-state.css +30 -0
- package/src/styles/components/dataframe.css +29 -35
- package/src/styles/components/experiment-data-viewer.css +4 -4
- package/src/styles/components/experiment-timeline.css +3 -30
- package/src/styles/components/file-browser-modal.css +489 -0
- package/src/styles/components/file-picker.css +502 -0
- package/src/styles/components/form-builder.css +5 -5
- package/src/styles/components/icon-button.css +9 -0
- package/src/styles/components/lcms-sequence-table.css +16 -1
- package/src/styles/components/multi-select.css +1 -1
- package/src/styles/components/reagent-list.css +38 -31
- package/src/styles/components/sample-hierarchy-tree.css +5 -19
- package/src/styles/components/sample-selector.css +9 -2
- package/src/styles/components/schedule-calendar.css +8 -0
- package/src/styles/components/scientific-number.css +1 -1
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +11 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/doseResponseAdapters.ts +2 -13
- package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
- package/src/templates/plateMapBuilder.ts +2 -2
- package/src/templates/qpcrPlateBuilder.ts +1 -1
- package/src/templates/templateValidators.ts +1 -1
- package/src/templates/timeCourseBuilder.ts +2 -2
- package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
- package/src/types/fileBrowserTypes.ts +85 -0
- package/src/types/filePicker.ts +72 -0
- package/src/types/index.ts +14 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/dist/EmptyState-NBqf2_8z.js.map +0 -1
- package/dist/ExperimentPopover-CfOdh5Rm.js.map +0 -1
- package/dist/ExperimentSelectorModal-DvIagrxY.js.map +0 -1
- package/dist/SettingsModal-W3TYfnP9.js.map +0 -1
- package/dist/components-B80gV0P6.js.map +0 -1
- package/dist/composables/autoGroupConstants.d.ts +0 -1
- package/dist/composables/autoGroupGrouping.d.ts +0 -3
- package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
- package/dist/composables-C8mmD5ta.js.map +0 -1
- package/dist/experiment-Y9Iz7KYi.js.map +0 -1
- package/dist/experiment-utils-DV3DrvE_.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-CK72aKO1.js.map +0 -1
- package/dist/useApi-CDBq6z23.js.map +0 -1
- package/dist/useControlSchema-_OVhavKq.js.map +0 -1
- package/dist/useDropdownState-D5G4UvWd.js.map +0 -1
- package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +0 -1
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
- package/src/composables/autoGroupConstants.ts +0 -4
- package/src/composables/autoGroupGrouping.ts +0 -148
- package/src/composables/useAutoGroupInputSources.ts +0 -147
- package/src/templates/builderDefaults.ts +0 -11
- package/src/templates/plateGeometry.ts +0 -62
- /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { computed, ref, shallowRef, watch } from 'vue'
|
|
2
|
+
import type {
|
|
3
|
+
PickerCapability,
|
|
4
|
+
PickerExcluded,
|
|
5
|
+
PickerFile,
|
|
6
|
+
PickerNode,
|
|
7
|
+
PickerSelection,
|
|
8
|
+
PickerSelectionMode,
|
|
9
|
+
} from '../../types/filePicker'
|
|
10
|
+
import type { usePickerNavigation } from './usePickerNavigation'
|
|
11
|
+
import { pickerExclusion } from './validation'
|
|
12
|
+
|
|
13
|
+
type Navigation = ReturnType<typeof usePickerNavigation>
|
|
14
|
+
export function usePickerSelection(
|
|
15
|
+
nav: Navigation,
|
|
16
|
+
mode: () => PickerSelectionMode,
|
|
17
|
+
capability: () => PickerCapability | undefined,
|
|
18
|
+
skipBlanks: () => boolean,
|
|
19
|
+
) {
|
|
20
|
+
const keys = ref(new Set<string>())
|
|
21
|
+
const dropped = ref(new Set<string>())
|
|
22
|
+
const focusPath = shallowRef('')
|
|
23
|
+
const resolving = shallowRef(false)
|
|
24
|
+
const resolutionError = shallowRef('')
|
|
25
|
+
let anchor = ''
|
|
26
|
+
let generation = 0
|
|
27
|
+
|
|
28
|
+
function ancestorSelected(node: PickerNode, candidates = keys.value): boolean {
|
|
29
|
+
const seen = new Set<string>()
|
|
30
|
+
let parent = node.parentPath
|
|
31
|
+
while (parent && !seen.has(parent)) {
|
|
32
|
+
if (candidates.has(parent)) return true
|
|
33
|
+
seen.add(parent)
|
|
34
|
+
parent = nav.nodes.value.get(parent)?.parentPath
|
|
35
|
+
}
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
function exclusion(node: PickerNode) {
|
|
39
|
+
if (!nav.isAllowed(node))
|
|
40
|
+
return { path: node.path, name: node.name, reason: 'forbidden' as const }
|
|
41
|
+
const own = pickerExclusion(node, capability(), skipBlanks())
|
|
42
|
+
if (own) return own
|
|
43
|
+
const seen = new Set<string>()
|
|
44
|
+
let parent = node.parentPath
|
|
45
|
+
while (parent && !seen.has(parent)) {
|
|
46
|
+
seen.add(parent)
|
|
47
|
+
const folder = nav.nodes.value.get(parent)
|
|
48
|
+
if (!folder) break
|
|
49
|
+
const inherited = pickerExclusion(folder, capability(), skipBlanks())
|
|
50
|
+
if (inherited) return { ...inherited, path: node.path, name: node.name }
|
|
51
|
+
parent = folder.parentPath
|
|
52
|
+
}
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
function selected(node: PickerNode): boolean {
|
|
56
|
+
return (
|
|
57
|
+
!exclusion(node) &&
|
|
58
|
+
!dropped.value.has(node.path) &&
|
|
59
|
+
!ancestorSelected(node, dropped.value) &&
|
|
60
|
+
(keys.value.has(node.path) || (mode() === 'folder+files' && ancestorSelected(node)))
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
function selectable(node: PickerNode): boolean {
|
|
64
|
+
if (exclusion(node)) return false
|
|
65
|
+
return node.kind === 'folder'
|
|
66
|
+
? mode() === 'folder' || mode() === 'folder+files'
|
|
67
|
+
: mode() !== 'folder'
|
|
68
|
+
}
|
|
69
|
+
function select(
|
|
70
|
+
node: PickerNode,
|
|
71
|
+
modifiers: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; toggle?: boolean } = {},
|
|
72
|
+
) {
|
|
73
|
+
if (exclusion(node)) return
|
|
74
|
+
focusPath.value = node.path
|
|
75
|
+
if (!selectable(node)) return
|
|
76
|
+
const multi = mode() === 'multi-file' || mode() === 'folder+files'
|
|
77
|
+
// Multi modes behave like a checklist: a plain click (or Space) adds or
|
|
78
|
+
// removes one row, Shift extends a range, Ctrl/Cmd is accepted for habit.
|
|
79
|
+
const additive = multi && (modifiers.metaKey || modifiers.ctrlKey || modifiers.toggle)
|
|
80
|
+
if (multi && modifiers.shiftKey && anchor) {
|
|
81
|
+
const rows = nav.rows.value.map((row) => row.node)
|
|
82
|
+
const from = rows.findIndex((item) => item.path === anchor)
|
|
83
|
+
const to = rows.findIndex((item) => item.path === node.path)
|
|
84
|
+
if (from >= 0 && to >= 0) {
|
|
85
|
+
keys.value = new Set([
|
|
86
|
+
...(additive ? keys.value : []),
|
|
87
|
+
...rows
|
|
88
|
+
.slice(Math.min(from, to), Math.max(from, to) + 1)
|
|
89
|
+
.filter(selectable)
|
|
90
|
+
.map((item) => item.path),
|
|
91
|
+
])
|
|
92
|
+
dropped.value.clear()
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (additive || (modifiers.toggle && (selected(node) || dropped.value.has(node.path)))) {
|
|
97
|
+
if (mode() === 'folder+files' && ancestorSelected(node)) {
|
|
98
|
+
if (dropped.value.has(node.path)) dropped.value.delete(node.path)
|
|
99
|
+
else dropped.value.add(node.path)
|
|
100
|
+
} else if (keys.value.has(node.path)) keys.value.delete(node.path)
|
|
101
|
+
else {
|
|
102
|
+
keys.value.add(node.path)
|
|
103
|
+
dropped.value.delete(node.path)
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
keys.value = new Set([node.path])
|
|
107
|
+
dropped.value.clear()
|
|
108
|
+
}
|
|
109
|
+
anchor = node.path
|
|
110
|
+
}
|
|
111
|
+
function reset(selection?: PickerSelection | null) {
|
|
112
|
+
++generation
|
|
113
|
+
keys.value.clear()
|
|
114
|
+
dropped.value.clear()
|
|
115
|
+
resolving.value = false
|
|
116
|
+
resolutionError.value = ''
|
|
117
|
+
focusPath.value = ''
|
|
118
|
+
if (selection?.source === 'server') {
|
|
119
|
+
const files =
|
|
120
|
+
mode() === 'folder'
|
|
121
|
+
? []
|
|
122
|
+
: mode() === 'single-file'
|
|
123
|
+
? selection.files.slice(0, 1)
|
|
124
|
+
: selection.files
|
|
125
|
+
const folders =
|
|
126
|
+
mode() === 'folder'
|
|
127
|
+
? selection.folders.slice(0, 1)
|
|
128
|
+
: mode() === 'folder+files'
|
|
129
|
+
? selection.folders
|
|
130
|
+
: []
|
|
131
|
+
nav.register(files)
|
|
132
|
+
folders.forEach((path) => {
|
|
133
|
+
if (!nav.nodes.value.has(path))
|
|
134
|
+
nav.register([
|
|
135
|
+
{ kind: 'folder', path, name: 'Selected folder', parentName: 'Selected location' },
|
|
136
|
+
])
|
|
137
|
+
keys.value.add(path)
|
|
138
|
+
})
|
|
139
|
+
files.forEach((file) => keys.value.add(file.path))
|
|
140
|
+
selection.excluded
|
|
141
|
+
.filter((item) => item.reason === 'deselected')
|
|
142
|
+
.forEach((item) => dropped.value.add(item.path))
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const roots = computed(() =>
|
|
146
|
+
[...keys.value]
|
|
147
|
+
.map((key) => nav.nodes.value.get(key))
|
|
148
|
+
.filter((node): node is PickerNode => !!node && !ancestorSelected(node) && nav.isAllowed(node)),
|
|
149
|
+
)
|
|
150
|
+
const chosen = computed(() => {
|
|
151
|
+
const files: PickerFile[] = []
|
|
152
|
+
const excluded: PickerExcluded[] = []
|
|
153
|
+
for (const node of nav.nodes.value.values()) {
|
|
154
|
+
if (!nav.isAllowed(node)) continue
|
|
155
|
+
if (!keys.value.has(node.path) && !ancestorSelected(node)) continue
|
|
156
|
+
const reason =
|
|
157
|
+
exclusion(node) ??
|
|
158
|
+
(dropped.value.has(node.path)
|
|
159
|
+
? { path: node.path, name: node.name, reason: 'deselected' as const }
|
|
160
|
+
: null)
|
|
161
|
+
// A dropped folder excludes its descendants as well.
|
|
162
|
+
const droppedByParent = ancestorSelected(node, dropped.value)
|
|
163
|
+
if (reason || droppedByParent) {
|
|
164
|
+
if (node.kind === 'file' || reason?.reason === 'unreadable')
|
|
165
|
+
excluded.push(reason ?? { path: node.path, name: node.name, reason: 'deselected' })
|
|
166
|
+
} else if (node.kind === 'file') files.push(node)
|
|
167
|
+
}
|
|
168
|
+
return { files, excluded, bytes: files.reduce((total, node) => total + (node.bytes ?? 0), 0) }
|
|
169
|
+
})
|
|
170
|
+
async function resolve() {
|
|
171
|
+
const token = ++generation
|
|
172
|
+
resolving.value = true
|
|
173
|
+
resolutionError.value = ''
|
|
174
|
+
let count = 0
|
|
175
|
+
const seen = new Set<string>()
|
|
176
|
+
async function walk(node: PickerNode, ancestors: string[] = []): Promise<PickerFile[]> {
|
|
177
|
+
if (token !== generation) throw new DOMException('Cancelled', 'AbortError')
|
|
178
|
+
if (!nav.isAllowed(node)) return []
|
|
179
|
+
if (node.kind === 'file') return [node]
|
|
180
|
+
if (node.unavailableReason) return []
|
|
181
|
+
if (ancestors.includes(node.path))
|
|
182
|
+
throw new Error('The adapter returned a circular folder tree.')
|
|
183
|
+
if (seen.has(node.path)) return []
|
|
184
|
+
seen.add(node.path)
|
|
185
|
+
if (++count > 500 || ancestors.length > 30)
|
|
186
|
+
throw new Error('This selection is too large to validate. Choose a smaller folder.')
|
|
187
|
+
const children = await nav.loadChildren(node.path, ancestors)
|
|
188
|
+
const files: PickerFile[] = []
|
|
189
|
+
for (const child of children) files.push(...(await walk(child, [...ancestors, node.path])))
|
|
190
|
+
if (token !== generation) throw new DOMException('Cancelled', 'AbortError')
|
|
191
|
+
const formats: Record<string, number> = {}
|
|
192
|
+
files.forEach((file) => {
|
|
193
|
+
formats[file.format] = (formats[file.format] ?? 0) + 1
|
|
194
|
+
})
|
|
195
|
+
nav.register([
|
|
196
|
+
{
|
|
197
|
+
...node,
|
|
198
|
+
fileCount: files.length,
|
|
199
|
+
bytes: files.reduce((sum, file) => sum + (file.bytes ?? 0), 0),
|
|
200
|
+
formats,
|
|
201
|
+
},
|
|
202
|
+
])
|
|
203
|
+
return files
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
for (const node of roots.value) await walk(node)
|
|
207
|
+
if (token === generation) {
|
|
208
|
+
// Resolved descendants inherit their folder, including restored selections.
|
|
209
|
+
for (const key of [...keys.value]) {
|
|
210
|
+
const node = nav.nodes.value.get(key)
|
|
211
|
+
if (node && ancestorSelected(node)) keys.value.delete(key)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (cause) {
|
|
215
|
+
if (token === generation)
|
|
216
|
+
resolutionError.value =
|
|
217
|
+
cause instanceof Error ? cause.message : 'Could not validate the selection.'
|
|
218
|
+
} finally {
|
|
219
|
+
if (token === generation) resolving.value = false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const focused = computed(() => {
|
|
223
|
+
const node = nav.nodes.value.get(focusPath.value)
|
|
224
|
+
return node && nav.isAllowed(node) ? node : roots.value[roots.value.length - 1] ?? null
|
|
225
|
+
})
|
|
226
|
+
watch(
|
|
227
|
+
() => new Map([...nav.nodes.value].map(([path, node]) => [path, nav.isAllowed(node)])),
|
|
228
|
+
(allowed, previous) => {
|
|
229
|
+
if (keys.value.size && [...allowed].some(([path, value]) =>
|
|
230
|
+
previous.has(path) && value !== previous.get(path),
|
|
231
|
+
)) void resolve()
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
function folderProgress(node: PickerNode): string {
|
|
235
|
+
if (node.kind !== 'folder' || node.fileCount == null) return ''
|
|
236
|
+
const allowed = chosen.value.files.filter((file) =>
|
|
237
|
+
ancestorSelected(file, new Set([node.path])),
|
|
238
|
+
).length
|
|
239
|
+
return `${selected(node) ? allowed : 0} / ${node.fileCount}`
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
keys,
|
|
243
|
+
dropped,
|
|
244
|
+
focusPath,
|
|
245
|
+
resolving,
|
|
246
|
+
resolutionError,
|
|
247
|
+
roots,
|
|
248
|
+
chosen,
|
|
249
|
+
focused,
|
|
250
|
+
select,
|
|
251
|
+
selected,
|
|
252
|
+
selectable,
|
|
253
|
+
exclusion,
|
|
254
|
+
reset,
|
|
255
|
+
resolve,
|
|
256
|
+
folderProgress,
|
|
257
|
+
ancestorSelected,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PickerCapability, PickerExcluded, PickerNode } from '../../types/filePicker'
|
|
2
|
+
|
|
3
|
+
export function normalizePickerFormat(format: string): string {
|
|
4
|
+
return format.trim().replace(/^\./, '').toLowerCase()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function pickerExclusion(
|
|
8
|
+
node: PickerNode,
|
|
9
|
+
capability?: PickerCapability,
|
|
10
|
+
skipBlanks = false,
|
|
11
|
+
): PickerExcluded | null {
|
|
12
|
+
const base = { path: node.path, name: node.name }
|
|
13
|
+
if (node.unavailableReason) return { ...base, reason: 'unreadable' }
|
|
14
|
+
if (skipBlanks && capability?.isBlank?.(node)) return { ...base, reason: 'blank' }
|
|
15
|
+
if (node.kind === 'folder') return null
|
|
16
|
+
const formats = capability?.formats.map(normalizePickerFormat) ?? []
|
|
17
|
+
if (formats.length && !formats.some((format) => node.name.toLowerCase().endsWith(`.${format}`))) {
|
|
18
|
+
return { ...base, reason: 'unsupported' }
|
|
19
|
+
}
|
|
20
|
+
if (capability?.maxBytes != null && node.bytes != null && node.bytes > capability.maxBytes) {
|
|
21
|
+
return { ...base, reason: 'tooLarge' }
|
|
22
|
+
}
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function pickerReason(excluded: PickerExcluded | null, node?: PickerNode): string {
|
|
27
|
+
if (!excluded) return ''
|
|
28
|
+
return {
|
|
29
|
+
unsupported: 'This format is not supported by this source.',
|
|
30
|
+
tooLarge: 'This file exceeds the size limit for this source.',
|
|
31
|
+
blank: 'Skipped by the blank-file rule.',
|
|
32
|
+
unreadable: node?.unavailableReason || 'This item cannot be read.',
|
|
33
|
+
deselected: 'Removed from the selection.',
|
|
34
|
+
forbidden: 'This item is not permitted by the system filter.',
|
|
35
|
+
}[excluded.reason]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function pickerFormat(name: string): string {
|
|
39
|
+
const compound = /\.(mzml|mzxml)\.gz$/i.exec(name)
|
|
40
|
+
return compound ? `${compound[1]}.gz` : name.includes('.') ? name.split('.').pop()! : ''
|
|
41
|
+
}
|
package/src/composables/index.ts
CHANGED
|
@@ -174,6 +174,11 @@ export {
|
|
|
174
174
|
type UseAnalysisArtifactsOptions,
|
|
175
175
|
type UseAnalysisArtifactsReturn,
|
|
176
176
|
} from './useAnalysisArtifacts'
|
|
177
|
+
export {
|
|
178
|
+
useFileBrowser,
|
|
179
|
+
type UseFileBrowserOptions,
|
|
180
|
+
type UseFileBrowserReturn,
|
|
181
|
+
} from './useFileBrowser'
|
|
177
182
|
export {
|
|
178
183
|
buildAnalysisArtifactTargetQuery,
|
|
179
184
|
parseAnalysisArtifactTarget,
|
|
@@ -549,8 +554,20 @@ export {
|
|
|
549
554
|
type UseRovingFocusOptions,
|
|
550
555
|
type UseRovingFocusReturn,
|
|
551
556
|
} from './useRovingFocus'
|
|
557
|
+
export {
|
|
558
|
+
reorderList,
|
|
559
|
+
useListReorder,
|
|
560
|
+
useReorderAnnouncer,
|
|
561
|
+
REORDER_ANNOUNCER_STYLE,
|
|
562
|
+
type ListReorderMove,
|
|
563
|
+
type UseListReorderOptions,
|
|
564
|
+
type UseListReorderReturn,
|
|
565
|
+
type UseReorderAnnouncerReturn,
|
|
566
|
+
} from './useListReorder'
|
|
552
567
|
export {
|
|
553
568
|
useMenuKeyboard,
|
|
554
569
|
type UseMenuKeyboardOptions,
|
|
555
570
|
type UseMenuKeyboardReturn,
|
|
556
571
|
} from './useMenuKeyboard'
|
|
572
|
+
|
|
573
|
+
export { usePlatformFilePickerAdapter, encodePlatformPickerPath, decodePlatformPickerPath } from './usePlatformFilePickerAdapter'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'
|
|
2
2
|
import { useSettingsStore } from '../stores/settings'
|
|
3
3
|
import { useAuthStore } from '../stores/auth'
|
|
4
|
+
import { MintApiError, mintApiErrorFromResponse } from '../apiErrors'
|
|
4
5
|
|
|
5
6
|
let apiClientInstance: AxiosInstance | null = null
|
|
6
7
|
let interceptorAttached = false
|
|
@@ -8,11 +9,13 @@ let interceptorAttached = false
|
|
|
8
9
|
interface MintAxiosRequestConfig extends AxiosRequestConfig {
|
|
9
10
|
_mintSkipAuth?: boolean
|
|
10
11
|
_mintAuthToken?: string
|
|
12
|
+
_mintTypedErrors?: boolean
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
interface MintInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
|
|
14
16
|
_mintSkipAuth?: boolean
|
|
15
17
|
_mintAuthToken?: string
|
|
18
|
+
_mintTypedErrors?: boolean
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
type MutableHeaders = Record<string, unknown> & {
|
|
@@ -103,14 +106,27 @@ function getAuthorizationHeader(headers: AxiosRequestConfig['headers']): unknown
|
|
|
103
106
|
return key ? bag[key] : undefined
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Raised when an authenticated SDK request is rejected because the login is no
|
|
111
|
+
* longer valid. It is also a `MintApiError` (status 401), so typed-error callers
|
|
112
|
+
* and `instanceof AuthenticationRequiredError` callers both recognise it.
|
|
113
|
+
*/
|
|
114
|
+
export class AuthenticationRequiredError extends MintApiError {
|
|
115
|
+
override readonly name: string = 'AuthenticationRequiredError'
|
|
116
|
+
declare readonly code: 'AUTHENTICATION_REQUIRED'
|
|
117
|
+
declare readonly status: 401
|
|
118
|
+
|
|
119
|
+
constructor(cause?: unknown, envelope?: MintApiError) {
|
|
120
|
+
super(
|
|
121
|
+
'Authentication required. Please sign in again.',
|
|
122
|
+
'AUTHENTICATION_REQUIRED',
|
|
123
|
+
401,
|
|
124
|
+
envelope?.requestId ?? null,
|
|
125
|
+
envelope?.details ?? null,
|
|
126
|
+
envelope?.detail,
|
|
127
|
+
envelope?.body ?? null,
|
|
128
|
+
cause,
|
|
129
|
+
)
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
132
|
|
|
@@ -118,12 +134,13 @@ export class AuthenticationRequiredError extends Error {
|
|
|
118
134
|
export function authenticationRequiredError(
|
|
119
135
|
cause: unknown,
|
|
120
136
|
rejectedToken?: string,
|
|
137
|
+
envelope?: MintApiError,
|
|
121
138
|
): AuthenticationRequiredError {
|
|
122
139
|
const currentAuthStore = useAuthStore()
|
|
123
140
|
if (rejectedToken && currentAuthStore.token === rejectedToken) {
|
|
124
141
|
currentAuthStore.clearToken()
|
|
125
142
|
}
|
|
126
|
-
return new AuthenticationRequiredError(cause)
|
|
143
|
+
return new AuthenticationRequiredError(cause, envelope)
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
function getApiClient(): AxiosInstance {
|
|
@@ -141,6 +158,8 @@ export interface ApiClientOptions {
|
|
|
141
158
|
baseUrl?: string
|
|
142
159
|
timeout?: number
|
|
143
160
|
withAuth?: boolean
|
|
161
|
+
/** Normalize failed responses into MintApiError. Generated clients enable this by default. */
|
|
162
|
+
typedErrors?: boolean
|
|
144
163
|
}
|
|
145
164
|
|
|
146
165
|
export interface UseApiReturn {
|
|
@@ -187,11 +206,35 @@ export function useApi(options: ApiClientOptions = {}): UseApiReturn {
|
|
|
187
206
|
})
|
|
188
207
|
apiClient.interceptors.response.use(
|
|
189
208
|
(response) => response,
|
|
190
|
-
(error: unknown) => {
|
|
191
|
-
if (axios.isAxiosError(error) && error.response
|
|
209
|
+
async (error: unknown) => {
|
|
210
|
+
if (axios.isAxiosError(error) && error.response) {
|
|
192
211
|
const request = error.config as MintInternalAxiosRequestConfig | undefined
|
|
193
|
-
if (request?.
|
|
194
|
-
|
|
212
|
+
if (request?._mintTypedErrors) {
|
|
213
|
+
let payload: unknown = error.response.data
|
|
214
|
+
if (payload instanceof Blob) {
|
|
215
|
+
try {
|
|
216
|
+
payload = JSON.parse(await payload.text())
|
|
217
|
+
} catch {
|
|
218
|
+
// Keep non-JSON download errors available as the original body.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const typed = mintApiErrorFromResponse(
|
|
222
|
+
error.response.status,
|
|
223
|
+
payload,
|
|
224
|
+
{
|
|
225
|
+
requestId: String(error.response.headers?.['x-request-id'] ?? '') || null,
|
|
226
|
+
cause: error,
|
|
227
|
+
},
|
|
228
|
+
)
|
|
229
|
+
if (error.response.status === 401 && request._mintSkipAuth !== true) {
|
|
230
|
+
return Promise.reject(authenticationRequiredError(error, request._mintAuthToken, typed))
|
|
231
|
+
}
|
|
232
|
+
return Promise.reject(typed)
|
|
233
|
+
}
|
|
234
|
+
if (error.response.status === 401) {
|
|
235
|
+
if (request?._mintSkipAuth !== true) {
|
|
236
|
+
return Promise.reject(authenticationRequiredError(error, request?._mintAuthToken))
|
|
237
|
+
}
|
|
195
238
|
}
|
|
196
239
|
}
|
|
197
240
|
return Promise.reject(error)
|
|
@@ -213,6 +256,7 @@ export function useApi(options: ApiClientOptions = {}): UseApiReturn {
|
|
|
213
256
|
const base: MintAxiosRequestConfig = {
|
|
214
257
|
baseURL: getBaseUrl(),
|
|
215
258
|
timeout: options.timeout ?? settingsStore.requestTimeout,
|
|
259
|
+
_mintTypedErrors: options.typedErrors,
|
|
216
260
|
...config,
|
|
217
261
|
}
|
|
218
262
|
// Strip auth header if explicitly disabled
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
1
|
+
import { nextTick, ref, type Ref } from 'vue'
|
|
2
2
|
import { useEventListener } from './useEventListener'
|
|
3
3
|
|
|
4
4
|
export interface UseDropdownStateOptions {
|
|
5
5
|
closeOnOutsideClick?: boolean
|
|
6
6
|
closeOnEscape?: boolean
|
|
7
|
+
/** Close the panel when the page scrolls or the viewport resizes. Default `false`. */
|
|
8
|
+
closeOnScroll?: boolean
|
|
7
9
|
insideRefs?: Array<Ref<HTMLElement | null>>
|
|
10
|
+
/**
|
|
11
|
+
* Element focus returns to when the panel closes with `close(true)` (Escape).
|
|
12
|
+
* Pass one when the trigger lives outside `rootRef`; otherwise bind the
|
|
13
|
+
* returned `triggerRef` to the trigger element.
|
|
14
|
+
*/
|
|
15
|
+
triggerRef?: Ref<HTMLElement | null>
|
|
8
16
|
onOpen?: () => void
|
|
9
17
|
onClose?: () => void
|
|
10
18
|
}
|
|
@@ -12,19 +20,29 @@ export interface UseDropdownStateOptions {
|
|
|
12
20
|
export interface UseDropdownStateReturn {
|
|
13
21
|
isOpen: Ref<boolean>
|
|
14
22
|
rootRef: Ref<HTMLElement | null>
|
|
23
|
+
/** Bind to the trigger element so Escape can return focus to it. */
|
|
24
|
+
triggerRef: Ref<HTMLElement | null>
|
|
15
25
|
open: () => void
|
|
16
|
-
|
|
26
|
+
/** Closes the panel; `returnFocus` moves focus back to `triggerRef`. */
|
|
27
|
+
close: (returnFocus?: boolean) => void
|
|
17
28
|
toggle: () => void
|
|
18
29
|
handleClickOutside: (event: MouseEvent) => void
|
|
19
30
|
handleKeydown: (event: KeyboardEvent) => void
|
|
20
31
|
}
|
|
21
32
|
|
|
22
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Shared disclosure state for dropdown-style SDK components: open/close state,
|
|
35
|
+
* outside-click and Escape closing, optional scroll/resize closing, and focus
|
|
36
|
+
* return to the trigger. `close(returnFocus)` mirrors `useMenuKeyboard`'s
|
|
37
|
+
* contract, so the two compose directly.
|
|
38
|
+
*/
|
|
23
39
|
export function useDropdownState(options: UseDropdownStateOptions = {}): UseDropdownStateReturn {
|
|
24
40
|
const isOpen = ref(false)
|
|
25
41
|
const rootRef = ref<HTMLElement | null>(null)
|
|
42
|
+
const triggerRef = options.triggerRef ?? ref<HTMLElement | null>(null)
|
|
26
43
|
const closeOnOutsideClick = options.closeOnOutsideClick ?? true
|
|
27
44
|
const closeOnEscape = options.closeOnEscape ?? true
|
|
45
|
+
const closeOnScroll = options.closeOnScroll ?? false
|
|
28
46
|
|
|
29
47
|
function open(): void {
|
|
30
48
|
if (isOpen.value) return
|
|
@@ -32,10 +50,13 @@ export function useDropdownState(options: UseDropdownStateOptions = {}): UseDrop
|
|
|
32
50
|
options.onOpen?.()
|
|
33
51
|
}
|
|
34
52
|
|
|
35
|
-
function close(): void {
|
|
53
|
+
function close(returnFocus = false): void {
|
|
36
54
|
if (!isOpen.value) return
|
|
37
55
|
isOpen.value = false
|
|
38
56
|
options.onClose?.()
|
|
57
|
+
if (returnFocus) {
|
|
58
|
+
void nextTick(() => triggerRef.value?.focus())
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
61
|
|
|
41
62
|
function toggle(): void {
|
|
@@ -60,20 +81,29 @@ export function useDropdownState(options: UseDropdownStateOptions = {}): UseDrop
|
|
|
60
81
|
|
|
61
82
|
function handleKeydown(event: KeyboardEvent): void {
|
|
62
83
|
if (closeOnEscape && event.key === 'Escape') {
|
|
63
|
-
close()
|
|
84
|
+
close(true)
|
|
64
85
|
}
|
|
65
86
|
}
|
|
66
87
|
|
|
88
|
+
function handleViewportChange(): void {
|
|
89
|
+
if (isOpen.value) close()
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
if (closeOnOutsideClick) {
|
|
68
93
|
useEventListener(() => document, 'click', handleClickOutside)
|
|
69
94
|
}
|
|
70
95
|
if (closeOnEscape) {
|
|
71
96
|
useEventListener(() => document, 'keydown', handleKeydown)
|
|
72
97
|
}
|
|
98
|
+
if (closeOnScroll) {
|
|
99
|
+
useEventListener(() => window, 'scroll', handleViewportChange, true)
|
|
100
|
+
useEventListener(() => window, 'resize', handleViewportChange)
|
|
101
|
+
}
|
|
73
102
|
|
|
74
103
|
return {
|
|
75
104
|
isOpen,
|
|
76
105
|
rootRef,
|
|
106
|
+
triggerRef,
|
|
77
107
|
open,
|
|
78
108
|
close,
|
|
79
109
|
toggle,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
shallowRef,
|
|
8
8
|
type Ref,
|
|
9
9
|
} from 'vue'
|
|
10
|
+
import { tryUseSettingsStore } from '../stores/settings'
|
|
10
11
|
|
|
11
12
|
export type EventStreamTransport = (
|
|
12
13
|
input: RequestInfo | URL,
|
|
@@ -26,7 +27,16 @@ export interface EventStreamOptions<TData = string> {
|
|
|
26
27
|
headers?: HeadersInit
|
|
27
28
|
credentials?: RequestCredentials
|
|
28
29
|
immediate?: boolean
|
|
30
|
+
/**
|
|
31
|
+
* Reconnect after a dropped stream. Left unset, it follows the platform's
|
|
32
|
+
* `wsAutoReconnect` setting, defaulting to `true` without a settings store.
|
|
33
|
+
*/
|
|
29
34
|
reconnect?: boolean
|
|
35
|
+
/**
|
|
36
|
+
* Delay before the first reconnect attempt. Left unset, it follows the
|
|
37
|
+
* platform's `wsReconnectInterval` setting, defaulting to 2000 ms without a
|
|
38
|
+
* settings store. A server-sent `retry:` field still overrides it.
|
|
39
|
+
*/
|
|
30
40
|
reconnectDelayMs?: number
|
|
31
41
|
parseJson?: boolean
|
|
32
42
|
shouldReconnect?: (cause?: unknown) => boolean
|
|
@@ -114,7 +124,9 @@ export function useEventStream<TData = string>(
|
|
|
114
124
|
let controller: AbortController | null = null
|
|
115
125
|
let reader: ReadableStreamDefaultReader<Uint8Array> | null = null
|
|
116
126
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
|
117
|
-
|
|
127
|
+
const settings = tryUseSettingsStore()
|
|
128
|
+
const reconnectEnabled = options.reconnect ?? settings?.wsAutoReconnect ?? true
|
|
129
|
+
let reconnectDelayMs = options.reconnectDelayMs ?? settings?.wsReconnectInterval ?? 2_000
|
|
118
130
|
let stopped = true
|
|
119
131
|
let generation = 0
|
|
120
132
|
|
|
@@ -135,7 +147,7 @@ export function useEventStream<TData = string>(
|
|
|
135
147
|
|
|
136
148
|
function scheduleReconnect(connectionGeneration: number, cause?: unknown): void {
|
|
137
149
|
if (stopped || generation !== connectionGeneration) return
|
|
138
|
-
if (
|
|
150
|
+
if (!reconnectEnabled || options.shouldReconnect?.(cause) === false) {
|
|
139
151
|
stopped = true
|
|
140
152
|
return
|
|
141
153
|
}
|