@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.
- package/README.md +44 -12
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
- package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
- package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
- package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
- package/dist/{SettingsModal-BW3Ar-uy.js → SettingsModal-Dphh3RrI.js} +5 -8
- package/dist/SettingsModal-Dphh3RrI.js.map +1 -0
- package/dist/{EmptyState-p90quq8Z.js → Skeleton-DxtwJ5gt.js} +111 -93
- package/dist/Skeleton-DxtwJ5gt.js.map +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
- package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
- package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -0
- package/dist/components/DropdownButton.vue.d.ts +2 -0
- 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/FitPanel.vue.d.ts +1 -1
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
- 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/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 +28 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
- package/dist/components/TimePicker.vue.d.ts +1 -0
- 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 +29 -0
- package/dist/components/index.d.ts +9 -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/WellEditPopupInternal.vue.d.ts +3 -1
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
- package/dist/{components-DQt0tcpY.js → components-CgAUzF8F.js} +2491 -2235
- package/dist/components-CgAUzF8F.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
- package/dist/composables/index.d.ts +8 -2
- package/dist/composables/index.js +10 -10
- package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
- package/dist/composables/useAsync.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
- package/dist/composables/useCommandHistory.d.ts +5 -1
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +52 -0
- package/dist/composables/useFocusTrap.d.ts +21 -0
- package/dist/composables/useJobsStatusTray.d.ts +1 -1
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +29 -0
- package/dist/composables/useOptimisticMutation.d.ts +5 -1
- package/dist/composables/usePluginClient.d.ts +4 -17
- package/dist/composables/usePluginConfig.d.ts +3 -1
- package/dist/composables/usePluginWorkspace.d.ts +51 -0
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useResourceCrud.d.ts +5 -1
- package/dist/composables/useRovingFocus.d.ts +24 -0
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/composables/useWellPainting.d.ts +5 -1
- package/dist/composables/useWellPlateAdapter.d.ts +5 -1
- package/dist/composables/useWellPlateValidation.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +188 -0
- package/dist/{composables-CkXDe7nm.js → composables-B_pia3hO.js} +105 -254
- package/dist/composables-B_pia3hO.js.map +1 -0
- package/dist/{useApi-CDBq6z23.js → errors-_W0-LbA6.js} +102 -3
- package/dist/errors-_W0-LbA6.js.map +1 -0
- package/dist/{experiment-Y9Iz7KYi.js → experiment-Cwl9AYxw.js} +3 -8
- package/dist/experiment-Cwl9AYxw.js.map +1 -0
- package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
- package/dist/experiment-utils-DV3DrvE_.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 +11247 -29501
- package/dist/templates/builderReadUtils.d.ts +1 -1
- package/dist/templates/index.js +2 -4
- package/dist/{templates-CovvRZ-9.js → templates-Qq113pyZ.js} +303 -106
- package/dist/templates-Qq113pyZ.js.map +1 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/{useControlSchema-BhoJzWzx.js → useControlSchema-aMZlb4Sc.js} +684 -1216
- package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
- package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
- package/dist/useDropdownState-DitCtrTT.js.map +1 -0
- package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-I8HC8hUu.js} +4 -4
- package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
- package/dist/{useFormBuilder-DiEcDis2.js → useFormBuilder-TCxNnqeb.js} +2 -2
- package/dist/{useFormBuilder-DiEcDis2.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
- package/dist/{useProtocolTemplates-DHwSveE6.js → useProtocolTemplates-BmX_iVBR.js} +660 -197
- package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/utils/pluginSecrets.d.ts +53 -0
- package/dist/utils/records.d.ts +4 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
- package/src/__tests__/components/BaseTabs.test.ts +67 -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/DropdownButton.test.ts +134 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +0 -5
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/JobsStatusTray.test.ts +20 -18
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -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/SecretInput.test.ts +87 -0
- package/src/__tests__/components/SegmentedControl.test.ts +88 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/composables/useAuth.test.ts +25 -0
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useEventStream.test.ts +140 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +103 -0
- package/src/__tests__/composables/useJobsStatusTray.test.ts +25 -161
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePluginClient.test.ts +148 -13
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +55 -2
- 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/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +26 -7
- 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 +8 -60
- 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 +26 -5
- 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/BioTemplateExperimentWorkspaceView.vue +10 -21
- package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
- 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/ComponentBindingRenderer.vue +1 -3
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/ControlWorkspaceView.vue +11 -22
- 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/DoseDesignWorkspaceView.vue +6 -25
- package/src/components/DropdownButton.vue +24 -6
- 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 +2 -5
- 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/FileUploader.vue +0 -4
- 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 +3 -3
- 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 -4
- 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.story.vue +6 -10
- 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/PluginIcon.vue +0 -4
- package/src/components/PluginWorkspaceView.props.ts +38 -0
- package/src/components/PluginWorkspaceView.vue +53 -224
- 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/SecretInput.story.vue +79 -0
- package/src/components/SecretInput.vue +150 -0
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +48 -5
- 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/index.ts +9 -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/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +20 -5
- package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
- package/src/components/internal/WorkspaceShellInternal.vue +125 -0
- package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/experimentDesignData.ts +1 -3
- package/src/composables/flattenWorkspaceBindings.ts +104 -0
- package/src/composables/formBuilderRegistry.ts +5 -0
- package/src/composables/index.ts +41 -5
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
- package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
- package/src/composables/useBioTemplateWorkspace.ts +18 -43
- package/src/composables/useCommandHistory.ts +5 -1
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +326 -0
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFocusTrap.ts +91 -0
- package/src/composables/useGeneratedAnalysis.ts +46 -103
- package/src/composables/useJobsStatusTray.ts +1 -1
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +90 -0
- package/src/composables/useOptimisticMutation.ts +6 -16
- package/src/composables/usePluginClient.ts +91 -252
- package/src/composables/usePluginConfig.ts +3 -1
- package/src/composables/usePluginWorkspace.ts +208 -0
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +6 -16
- package/src/composables/useRovingFocus.ts +66 -0
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +10 -30
- package/src/composables/useWellPlateAdapter.ts +5 -1
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/composables/useWellPlateValidation.ts +5 -1
- package/src/composables/workspaceRecipes.ts +267 -0
- package/src/index.ts +16 -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/form-builder.css +5 -5
- 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/secret-input.css +60 -0
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +9 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/builderReadUtils.ts +1 -3
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/componentTargetResolvers.ts +1 -3
- package/src/templates/controlSchemaTargets.ts +1 -3
- 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/form-builder.ts +1 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/src/utils/formModelSync.ts +1 -3
- package/src/utils/pluginIcon.ts +1 -3
- package/src/utils/pluginSecrets.ts +102 -0
- package/src/utils/records.ts +9 -0
- package/dist/EmptyState-p90quq8Z.js.map +0 -1
- package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
- package/dist/ExperimentSelectorModal-B6P2msT4.js.map +0 -1
- package/dist/SettingsModal-BW3Ar-uy.js.map +0 -1
- package/dist/components-DQt0tcpY.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-CkXDe7nm.js.map +0 -1
- package/dist/experiment-Y9Iz7KYi.js.map +0 -1
- package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-CovvRZ-9.js.map +0 -1
- package/dist/useApi-CDBq6z23.js.map +0 -1
- package/dist/useControlSchema-BhoJzWzx.js.map +0 -1
- package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
- package/dist/useProtocolTemplates-DHwSveE6.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 → components/DataStates.test.d.ts} +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
import type { ComputedRef } from 'vue'
|
|
3
|
+
import type { PageSelectorItem, PillNavItem } from '../types'
|
|
4
|
+
import type {
|
|
5
|
+
PluginWorkspaceViewEmit,
|
|
6
|
+
PluginWorkspaceViewProps,
|
|
7
|
+
} from '../components/PluginWorkspaceView.props'
|
|
8
|
+
import { usePluginWorkspaceControls } from '../components/PluginWorkspaceView.controls'
|
|
9
|
+
import { usePluginWorkspaceNavigation } from '../components/PluginWorkspaceView.navigation'
|
|
10
|
+
import {
|
|
11
|
+
getPluginWorkspaceSidebarSlotNames,
|
|
12
|
+
getPluginWorkspaceTopBarSlotNames,
|
|
13
|
+
hasPluginWorkspaceSidebarSurface,
|
|
14
|
+
} from '../components/PluginWorkspaceView.shell'
|
|
15
|
+
import type {
|
|
16
|
+
PluginWorkspaceControlsModel,
|
|
17
|
+
PluginWorkspaceShellModel,
|
|
18
|
+
PluginWorkspaceSidebarModel,
|
|
19
|
+
} from './pluginWorkspaceModel'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Loosely-typed view of a component's `useSlots()` result: only slot presence and names are
|
|
23
|
+
* read here, so this accepts any `defineSlots()` shape without fighting Vue's `Slot` render
|
|
24
|
+
* function type. Mirrors the `SlotKeyMap` helper type in PluginWorkspaceView.shell.ts.
|
|
25
|
+
*/
|
|
26
|
+
export type PluginWorkspaceSlotsLike = Readonly<Record<string, unknown>>
|
|
27
|
+
|
|
28
|
+
export interface UsePluginWorkspaceOptions {
|
|
29
|
+
/** PluginWorkspaceView's own reactive props object (from `defineProps`). */
|
|
30
|
+
props: PluginWorkspaceViewProps
|
|
31
|
+
/** PluginWorkspaceView's own slots (from `useSlots()`), used to detect provided passthrough slots. */
|
|
32
|
+
slots: PluginWorkspaceSlotsLike
|
|
33
|
+
/** PluginWorkspaceView's own typed `emit` function (from `defineEmits<PluginWorkspaceViewEmits>()`). */
|
|
34
|
+
emit: PluginWorkspaceViewEmit
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface UsePluginWorkspaceReturn {
|
|
38
|
+
/** Model consumed by WorkspaceShellInternal's `shell` prop. */
|
|
39
|
+
shell: ComputedRef<PluginWorkspaceShellModel>
|
|
40
|
+
/** Model consumed by WorkspaceSidebarInternal's `sidebar` prop. */
|
|
41
|
+
sidebar: ComputedRef<PluginWorkspaceSidebarModel>
|
|
42
|
+
/** Model consumed by WorkspaceControlsInternal's `controls` prop, and by the raw `default` slot. */
|
|
43
|
+
controls: ComputedRef<PluginWorkspaceControlsModel>
|
|
44
|
+
/** Whether AppLayout should render a sidebar surface at all. */
|
|
45
|
+
hasSidebarSurface: ComputedRef<boolean>
|
|
46
|
+
/** Applies an AppSidebar `update:model-value` payload to the shared control values and emits `update:modelValue`/`update:values`. */
|
|
47
|
+
handleControlValuesUpdate: (values: Record<string, unknown>) => void
|
|
48
|
+
/** Resolves an AppTopBar `page-selector-select` payload into the active view and emits `page-selector-select`. */
|
|
49
|
+
handlePageSelectorSelect: (page: PageSelectorItem) => void
|
|
50
|
+
/** Resolves an AppTopBar `pill-select` payload into the active view and emits `pill-select`. */
|
|
51
|
+
handlePillSelect: (item: PillNavItem) => void
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Maps PluginWorkspaceView's flat 72-prop surface into the three typed model objects
|
|
56
|
+
* (`shell`, `sidebar`, `controls`) rendered by its internal pieces
|
|
57
|
+
* (WorkspaceShellInternal, WorkspaceSidebarInternal, WorkspaceControlsInternal). Reuses the
|
|
58
|
+
* existing `usePluginWorkspaceControls` / `usePluginWorkspaceNavigation` composables and the
|
|
59
|
+
* PluginWorkspaceView.shell slot-detection helpers, so it changes only how that state is
|
|
60
|
+
* assembled and exposed, not the underlying behavior.
|
|
61
|
+
*
|
|
62
|
+
* @example Recommended
|
|
63
|
+
* ```ts
|
|
64
|
+
* const props = withDefaults(defineProps<PluginWorkspaceViewProps>(), { ... })
|
|
65
|
+
* const emit = defineEmits<PluginWorkspaceViewEmits>()
|
|
66
|
+
* const slots = useSlots()
|
|
67
|
+
* const { shell, sidebar, controls } = usePluginWorkspace({ props, slots, emit })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function usePluginWorkspace(options: UsePluginWorkspaceOptions): UsePluginWorkspaceReturn {
|
|
71
|
+
const { props, slots, emit } = options
|
|
72
|
+
|
|
73
|
+
const controlsState = usePluginWorkspaceControls({
|
|
74
|
+
model: () => props.model,
|
|
75
|
+
controls: () => props.controls,
|
|
76
|
+
controlOptions: () => props.controlOptions ?? {},
|
|
77
|
+
modelValue: () => props.modelValue,
|
|
78
|
+
values: () => props.values,
|
|
79
|
+
componentBindings: () => props.componentBindings,
|
|
80
|
+
componentProps: () => props.componentProps,
|
|
81
|
+
componentPropsById: () => props.componentPropsById,
|
|
82
|
+
emitValues: (values) => {
|
|
83
|
+
emit('update:modelValue', values)
|
|
84
|
+
emit('update:values', values)
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const navigationState = usePluginWorkspaceNavigation({
|
|
89
|
+
activeView: () => props.activeView,
|
|
90
|
+
defaultActiveView: () => props.defaultActiveView,
|
|
91
|
+
currentPillId: () => props.currentPillId,
|
|
92
|
+
currentPageSelectorId: () => props.currentPageSelectorId,
|
|
93
|
+
pillNav: () => props.pillNav,
|
|
94
|
+
pageSelector: () => props.pageSelector,
|
|
95
|
+
panelIds: () => Object.keys(props.panels ?? {}),
|
|
96
|
+
generatedPillNav: controlsState.generatedPillNav,
|
|
97
|
+
generatedDefaultView: controlsState.generatedDefaultView,
|
|
98
|
+
emitActiveView: viewId => emit('update:activeView', viewId),
|
|
99
|
+
emitPageSelectorSelect: page => emit('page-selector-select', page),
|
|
100
|
+
emitPillSelect: item => emit('pill-select', item),
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const topBarSlotNames = computed<string[]>(() => getPluginWorkspaceTopBarSlotNames(slots))
|
|
104
|
+
const sidebarSlotNames = computed<string[]>(() => getPluginWorkspaceSidebarSlotNames(slots))
|
|
105
|
+
|
|
106
|
+
const hasSidebarSurface = computed(() => hasPluginWorkspaceSidebarSurface({
|
|
107
|
+
showSidebar: props.showSidebar ?? true,
|
|
108
|
+
panels: props.panels ?? {},
|
|
109
|
+
forms: props.forms ?? {},
|
|
110
|
+
model: props.model,
|
|
111
|
+
controls: props.controls,
|
|
112
|
+
showSidebarWhenEmpty: props.showSidebarWhenEmpty ?? false,
|
|
113
|
+
sidebarContentId: props.sidebarContentId,
|
|
114
|
+
sidebarTitle: props.sidebarTitle,
|
|
115
|
+
sidebarSubtitle: props.sidebarSubtitle,
|
|
116
|
+
sidebarBadge: props.sidebarBadge,
|
|
117
|
+
hasSidebarSlot: Boolean(slots.sidebar),
|
|
118
|
+
sidebarSlotCount: sidebarSlotNames.value.length,
|
|
119
|
+
}))
|
|
120
|
+
|
|
121
|
+
const controls = computed<PluginWorkspaceControlsModel>(() => ({
|
|
122
|
+
activeView: navigationState.resolvedActiveView.value,
|
|
123
|
+
setActiveView: navigationState.setActiveView,
|
|
124
|
+
values: controlsState.internalControlValues,
|
|
125
|
+
componentBindings: controlsState.resolvedComponentBindings.value,
|
|
126
|
+
componentBindingsById: controlsState.resolvedComponentBindingsById.value,
|
|
127
|
+
componentProps: controlsState.resolvedComponentProps.value,
|
|
128
|
+
componentPropsById: controlsState.resolvedComponentPropsById.value,
|
|
129
|
+
}))
|
|
130
|
+
|
|
131
|
+
const shell = computed<PluginWorkspaceShellModel>(() => ({
|
|
132
|
+
sidebarPosition: props.sidebarPosition ?? 'left',
|
|
133
|
+
sidebarWidth: props.sidebarWidth,
|
|
134
|
+
floating: props.floating ?? true,
|
|
135
|
+
responsiveSidebar: props.responsiveSidebar ?? true,
|
|
136
|
+
sidebarToggleLabel: props.sidebarToggleLabel ?? 'Open workspace sidebar',
|
|
137
|
+
sidebarCloseLabel: props.sidebarCloseLabel ?? 'Close workspace sidebar',
|
|
138
|
+
hasSidebarSurface: hasSidebarSurface.value,
|
|
139
|
+
title: props.title ?? 'Workspace',
|
|
140
|
+
subtitle: props.subtitle,
|
|
141
|
+
topBarVariant: props.topBarVariant ?? 'card',
|
|
142
|
+
homePath: props.homePath ?? '/',
|
|
143
|
+
showLogo: props.showLogo ?? true,
|
|
144
|
+
pageSelector: props.pageSelector,
|
|
145
|
+
currentPageSelectorId: navigationState.resolvedCurrentPageSelectorId.value,
|
|
146
|
+
pluginSwitcher: props.pluginSwitcher,
|
|
147
|
+
pillNav: navigationState.resolvedPillNav.value,
|
|
148
|
+
currentPillId: navigationState.resolvedCurrentPillId.value,
|
|
149
|
+
showThemeToggle: props.showThemeToggle ?? true,
|
|
150
|
+
showSettings: props.showSettings ?? true,
|
|
151
|
+
settingsConfig: props.settingsConfig,
|
|
152
|
+
showStandaloneLabel: props.showStandaloneLabel ?? false,
|
|
153
|
+
standaloneLabel: props.standaloneLabel ?? 'Standalone',
|
|
154
|
+
accountMenu: props.accountMenu,
|
|
155
|
+
showNotifications: props.showNotifications ?? false,
|
|
156
|
+
hasNotificationDot: props.hasNotificationDot ?? false,
|
|
157
|
+
showAdmin: props.showAdmin ?? false,
|
|
158
|
+
adminPath: props.adminPath ?? '/admin',
|
|
159
|
+
userName: props.userName,
|
|
160
|
+
userInitial: props.userInitial,
|
|
161
|
+
userEmail: props.userEmail,
|
|
162
|
+
topBarSlotNames: topBarSlotNames.value,
|
|
163
|
+
controls: controls.value,
|
|
164
|
+
}))
|
|
165
|
+
|
|
166
|
+
const sidebar = computed<PluginWorkspaceSidebarModel>(() => ({
|
|
167
|
+
title: props.sidebarTitle,
|
|
168
|
+
subtitle: props.sidebarSubtitle,
|
|
169
|
+
badge: props.sidebarBadge,
|
|
170
|
+
variant: props.sidebarVariant ?? 'analysis',
|
|
171
|
+
panels: props.panels ?? {},
|
|
172
|
+
activeView: navigationState.resolvedActiveView.value,
|
|
173
|
+
floating: false,
|
|
174
|
+
dense: props.dense ?? true,
|
|
175
|
+
width: props.sidebarWidth,
|
|
176
|
+
side: props.sidebarPosition ?? 'left',
|
|
177
|
+
forms: props.forms ?? {},
|
|
178
|
+
contentId: props.sidebarContentId,
|
|
179
|
+
showWhenEmpty: (props.showSidebarWhenEmpty ?? false) || props.sidebarContentId !== undefined,
|
|
180
|
+
model: props.model,
|
|
181
|
+
controlSchema: props.controls,
|
|
182
|
+
controlOptions: props.controlOptions ?? {},
|
|
183
|
+
values: controlsState.internalControlValues,
|
|
184
|
+
formEnhancements: props.formEnhancements,
|
|
185
|
+
showFormActions: props.showFormActions ?? false,
|
|
186
|
+
formLoading: props.formLoading ?? false,
|
|
187
|
+
formDisabled: props.formDisabled ?? false,
|
|
188
|
+
formReadonly: props.formReadonly ?? false,
|
|
189
|
+
formSize: props.formSize ?? 'sm',
|
|
190
|
+
collapsible: props.sidebarCollapsible,
|
|
191
|
+
collapsed: props.sidebarCollapsed,
|
|
192
|
+
defaultCollapsed: props.defaultSidebarCollapsed ?? false,
|
|
193
|
+
collapsedWidth: props.sidebarCollapsedWidth ?? '3rem',
|
|
194
|
+
collapseButtonLabel: props.sidebarCollapseButtonLabel ?? 'Collapse sidebar',
|
|
195
|
+
expandButtonLabel: props.sidebarExpandButtonLabel ?? 'Expand sidebar',
|
|
196
|
+
slotNames: sidebarSlotNames.value,
|
|
197
|
+
}))
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
shell,
|
|
201
|
+
sidebar,
|
|
202
|
+
controls,
|
|
203
|
+
hasSidebarSurface,
|
|
204
|
+
handleControlValuesUpdate: controlsState.handleControlValuesUpdate,
|
|
205
|
+
handlePageSelectorSelect: navigationState.handlePageSelectorSelect,
|
|
206
|
+
handlePillSelect: navigationState.handlePillSelect,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ref, computed, type Ref, type ComputedRef } from 'vue'
|
|
2
2
|
import type { Rack, SlotPosition, WellPlateFormat, Well } from '../types'
|
|
3
|
+
import { PLATE_CONFIGS, rowLabel as plateRowLabel } from '../components/WellPlate.geometry'
|
|
3
4
|
|
|
4
5
|
export interface UseRackEditorOptions {
|
|
5
6
|
defaultFormat?: WellPlateFormat
|
|
@@ -31,15 +32,6 @@ export interface UseRackEditorReturn {
|
|
|
31
32
|
|
|
32
33
|
const SLOT_CYCLE: SlotPosition[] = ['R', 'G', 'B', 'Y']
|
|
33
34
|
|
|
34
|
-
const PLATE_CONFIGS: Record<WellPlateFormat, { rows: number; cols: number }> = {
|
|
35
|
-
6: { rows: 2, cols: 3 },
|
|
36
|
-
12: { rows: 3, cols: 4 },
|
|
37
|
-
24: { rows: 4, cols: 6 },
|
|
38
|
-
48: { rows: 6, cols: 8 },
|
|
39
|
-
54: { rows: 6, cols: 9 },
|
|
40
|
-
96: { rows: 8, cols: 12 },
|
|
41
|
-
384: { rows: 16, cols: 24 },
|
|
42
|
-
}
|
|
43
35
|
|
|
44
36
|
let globalIdCounter = 0
|
|
45
37
|
|
|
@@ -156,8 +148,7 @@ export function useRackEditor(
|
|
|
156
148
|
let counter = 1
|
|
157
149
|
for (let row = 0; row < config.rows; row++) {
|
|
158
150
|
for (let col = 0; col < config.cols; col++) {
|
|
159
|
-
const
|
|
160
|
-
const wellId = `${rowLabel}${col + 1}`
|
|
151
|
+
const wellId = `${plateRowLabel(row)}${col + 1}`
|
|
161
152
|
// Only fill empty wells
|
|
162
153
|
if (!rack.wells[wellId]?.sampleType) {
|
|
163
154
|
const paddedNum = String(counter).padStart(3, '0')
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { ref, type Ref } from 'vue'
|
|
2
|
+
import { readErrorMessage as readSharedErrorMessage } from '../utils/errors'
|
|
2
3
|
|
|
3
4
|
export type RequestSyncSuccessKind = 'load' | 'save' | 'run'
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* The unit of work `run()` drives.
|
|
8
|
+
*
|
|
9
|
+
* It receives an `AbortSignal` that fires when the run is superseded by a
|
|
10
|
+
* newer `run()` or by `cancel()`. Zero-argument operations stay valid and
|
|
11
|
+
* simply ignore it.
|
|
12
|
+
*/
|
|
13
|
+
export type RequestSyncOperation<T> = (signal: AbortSignal) => Promise<T>
|
|
14
|
+
|
|
5
15
|
export interface RequestSyncRunOptions {
|
|
6
16
|
success?: RequestSyncSuccessKind
|
|
7
17
|
errorMessage?: string
|
|
18
|
+
/**
|
|
19
|
+
* Abort the previously started run when this one starts. Default `true`;
|
|
20
|
+
* pass `false` to let overlapping runs finish their own work (the stale
|
|
21
|
+
* guard on shared state still applies either way).
|
|
22
|
+
*/
|
|
23
|
+
abortPrevious?: boolean
|
|
24
|
+
/** Caller-owned signal; aborting it aborts this run's signal too. */
|
|
25
|
+
signal?: AbortSignal
|
|
8
26
|
}
|
|
9
27
|
|
|
10
28
|
export interface UseRequestSyncStateReturn {
|
|
@@ -30,8 +48,32 @@ export interface UseRequestSyncStateReturn {
|
|
|
30
48
|
markSaved: (date?: Date) => void
|
|
31
49
|
/** Mark the operation as successfully run. */
|
|
32
50
|
markRun: (date?: Date) => void
|
|
33
|
-
/**
|
|
34
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Run a request with shared loading/error handling and optional sync marking.
|
|
53
|
+
*
|
|
54
|
+
* ## Stale-response semantics
|
|
55
|
+
*
|
|
56
|
+
* Every call takes a monotonically increasing token. Starting a run
|
|
57
|
+
* supersedes any run started before it, and a superseded run writes **no**
|
|
58
|
+
* shared state when it finally settles: it does not clear `loading`, does not
|
|
59
|
+
* set `error`, and does not move `lastLoadedAt` / `lastSavedAt` / `lastRunAt`.
|
|
60
|
+
* Only the newest run owns those.
|
|
61
|
+
*
|
|
62
|
+
* The caller's own promise is untouched by the guard — a superseded run still
|
|
63
|
+
* resolves with its value or rejects with its own error, exactly as the
|
|
64
|
+
* operation did. The guard never invents a rejection, so a caller awaiting
|
|
65
|
+
* `run()` never sees a "superseded" error it did not cause.
|
|
66
|
+
*
|
|
67
|
+
* The operation additionally receives an `AbortSignal` that fires when its
|
|
68
|
+
* run is superseded or `cancel()` is called, so an operation that forwards
|
|
69
|
+
* the signal (`fetch(url, { signal })`) stops work instead of racing.
|
|
70
|
+
*/
|
|
71
|
+
run: <T>(operation: RequestSyncOperation<T>, options?: RequestSyncRunOptions) => Promise<T>
|
|
72
|
+
/**
|
|
73
|
+
* Abort the in-flight run and drop `loading`. The aborted run's completion
|
|
74
|
+
* is treated as stale and writes no state.
|
|
75
|
+
*/
|
|
76
|
+
cancel: () => void
|
|
35
77
|
}
|
|
36
78
|
|
|
37
79
|
/** Shared loading/error/timestamp state for generated plugin request helpers. */
|
|
@@ -49,22 +91,7 @@ export function useRequestSyncState(
|
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
function readErrorMessage(value: unknown, fallback = defaultErrorMessage): string {
|
|
52
|
-
|
|
53
|
-
return value.message
|
|
54
|
-
}
|
|
55
|
-
if (typeof value === 'string' && value.trim()) {
|
|
56
|
-
return value
|
|
57
|
-
}
|
|
58
|
-
if (
|
|
59
|
-
typeof value === 'object'
|
|
60
|
-
&& value !== null
|
|
61
|
-
&& 'message' in value
|
|
62
|
-
&& typeof (value as { message?: unknown }).message === 'string'
|
|
63
|
-
&& (value as { message: string }).message
|
|
64
|
-
) {
|
|
65
|
-
return (value as { message: string }).message
|
|
66
|
-
}
|
|
67
|
-
return fallback
|
|
94
|
+
return readSharedErrorMessage(value, fallback)
|
|
68
95
|
}
|
|
69
96
|
|
|
70
97
|
function setError(value: unknown, fallback?: string): string {
|
|
@@ -85,27 +112,69 @@ export function useRequestSyncState(
|
|
|
85
112
|
lastRunAt.value = date
|
|
86
113
|
}
|
|
87
114
|
|
|
115
|
+
// Monotonic token guarding every write to the shared loading/error/timestamp
|
|
116
|
+
// state. Overlapping runs settle in any order, and a superseded one must not
|
|
117
|
+
// clear a newer run's loading flag or overwrite its error.
|
|
118
|
+
let runToken = 0
|
|
119
|
+
let activeController: AbortController | null = null
|
|
120
|
+
|
|
121
|
+
function cancel(): void {
|
|
122
|
+
runToken += 1
|
|
123
|
+
activeController?.abort()
|
|
124
|
+
activeController = null
|
|
125
|
+
loading.value = false
|
|
126
|
+
}
|
|
127
|
+
|
|
88
128
|
async function run<T>(
|
|
89
|
-
operation:
|
|
129
|
+
operation: RequestSyncOperation<T>,
|
|
90
130
|
options: RequestSyncRunOptions = {},
|
|
91
131
|
): Promise<T> {
|
|
132
|
+
if (options.abortPrevious !== false) {
|
|
133
|
+
activeController?.abort()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const token = (runToken += 1)
|
|
137
|
+
const controller = new AbortController()
|
|
138
|
+
activeController = controller
|
|
139
|
+
|
|
140
|
+
const callerSignal = options.signal
|
|
141
|
+
if (callerSignal) {
|
|
142
|
+
if (callerSignal.aborted) controller.abort(callerSignal.reason)
|
|
143
|
+
else callerSignal.addEventListener(
|
|
144
|
+
'abort',
|
|
145
|
+
() => controller.abort(callerSignal.reason),
|
|
146
|
+
{ once: true },
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const isCurrent = (): boolean => token === runToken
|
|
151
|
+
|
|
92
152
|
loading.value = true
|
|
93
153
|
clearError()
|
|
94
154
|
try {
|
|
95
|
-
const response = await operation()
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
155
|
+
const response = await operation(controller.signal)
|
|
156
|
+
if (isCurrent()) {
|
|
157
|
+
if (options.success === 'load') {
|
|
158
|
+
markLoaded()
|
|
159
|
+
} else if (options.success === 'save') {
|
|
160
|
+
markSaved()
|
|
161
|
+
} else if (options.success === 'run') {
|
|
162
|
+
markRun()
|
|
163
|
+
}
|
|
102
164
|
}
|
|
103
165
|
return response
|
|
104
166
|
} catch (err) {
|
|
105
|
-
|
|
167
|
+
if (isCurrent()) {
|
|
168
|
+
setError(err, options.errorMessage)
|
|
169
|
+
}
|
|
106
170
|
throw err
|
|
107
171
|
} finally {
|
|
108
|
-
|
|
172
|
+
if (isCurrent()) {
|
|
173
|
+
loading.value = false
|
|
174
|
+
}
|
|
175
|
+
if (activeController === controller) {
|
|
176
|
+
activeController = null
|
|
177
|
+
}
|
|
109
178
|
}
|
|
110
179
|
}
|
|
111
180
|
|
|
@@ -122,5 +191,6 @@ export function useRequestSyncState(
|
|
|
122
191
|
markSaved,
|
|
123
192
|
markRun,
|
|
124
193
|
run,
|
|
194
|
+
cancel,
|
|
125
195
|
}
|
|
126
196
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { computed, ref, shallowRef, type ComputedRef, type Ref, type ShallowRef } from 'vue'
|
|
2
|
+
import { readErrorMessage } from '../utils/errors'
|
|
2
3
|
|
|
3
4
|
export type ResourceKey = string | number
|
|
4
5
|
|
|
@@ -38,7 +39,11 @@ export interface UseResourceCrudReturn<TItem, TCreate, TUpdate, TKey extends Res
|
|
|
38
39
|
clearError: () => void
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Common list/create/update/delete state wrapper for generated plugin clients.
|
|
44
|
+
*
|
|
45
|
+
* @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useGeneratedPluginClient()` instead.
|
|
46
|
+
*/
|
|
42
47
|
export function useResourceCrud<
|
|
43
48
|
TItem,
|
|
44
49
|
TCreate = Partial<TItem>,
|
|
@@ -228,18 +233,3 @@ function resolveKey<TItem, TKey extends ResourceKey>(
|
|
|
228
233
|
}
|
|
229
234
|
return getKey(keyOrItem as TItem)
|
|
230
235
|
}
|
|
231
|
-
|
|
232
|
-
function readErrorMessage(value: unknown): string {
|
|
233
|
-
if (value instanceof Error && value.message) return value.message
|
|
234
|
-
if (typeof value === 'string' && value.trim()) return value
|
|
235
|
-
if (
|
|
236
|
-
typeof value === 'object'
|
|
237
|
-
&& value !== null
|
|
238
|
-
&& 'message' in value
|
|
239
|
-
&& typeof (value as { message?: unknown }).message === 'string'
|
|
240
|
-
&& (value as { message: string }).message.trim()
|
|
241
|
-
) {
|
|
242
|
-
return (value as { message: string }).message
|
|
243
|
-
}
|
|
244
|
-
return 'Request failed.'
|
|
245
|
-
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { nextTick } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface UseRovingFocusOptions {
|
|
4
|
+
/** Selectable item ids, in DOM order. Already excludes items skipped entirely (e.g. disabled tabs). */
|
|
5
|
+
ids: () => string[]
|
|
6
|
+
/** The id that is currently active/selected. */
|
|
7
|
+
activeId: () => string | undefined
|
|
8
|
+
/** Activates an id (e.g. emit `update:modelValue`). The composable focuses the matching element afterward. */
|
|
9
|
+
activate: (id: string) => void
|
|
10
|
+
/** Returns the focusable element for an id, queried lazily so it reflects the post-activation DOM. */
|
|
11
|
+
getElement: (id: string) => HTMLElement | null | undefined
|
|
12
|
+
/** Arrow-key axis: `'horizontal'` (Left/Right, default) or `'vertical'` (Up/Down). */
|
|
13
|
+
orientation?: 'horizontal' | 'vertical'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UseRovingFocusReturn {
|
|
17
|
+
/** Bind to each item's `@keydown`. */
|
|
18
|
+
handleKeydown: (event: KeyboardEvent) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* WAI-ARIA roving-tabindex navigation with automatic activation (the tabs pattern):
|
|
23
|
+
* Left/Right (or Up/Down when `orientation` is `'vertical'`) move to the adjacent item
|
|
24
|
+
* and activate it immediately, wrapping past the ends; Home/End jump to the first/last
|
|
25
|
+
* item. Only the active item should carry `tabindex="0"`; give every other item
|
|
26
|
+
* `tabindex="-1"`. Used by BaseTabs.
|
|
27
|
+
*/
|
|
28
|
+
export function useRovingFocus(options: UseRovingFocusOptions): UseRovingFocusReturn {
|
|
29
|
+
const orientation = options.orientation ?? 'horizontal'
|
|
30
|
+
const prevKey = orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'
|
|
31
|
+
const nextKey = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'
|
|
32
|
+
|
|
33
|
+
function moveTo(id: string | undefined) {
|
|
34
|
+
if (id === undefined) return
|
|
35
|
+
options.activate(id)
|
|
36
|
+
void nextTick(() => options.getElement(id)?.focus())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
40
|
+
const ids = options.ids()
|
|
41
|
+
if (ids.length === 0) return
|
|
42
|
+
const currentIndex = ids.indexOf(options.activeId() ?? '')
|
|
43
|
+
|
|
44
|
+
if (event.key === 'Home') {
|
|
45
|
+
event.preventDefault()
|
|
46
|
+
moveTo(ids[0])
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
if (event.key === 'End') {
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
moveTo(ids[ids.length - 1])
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
if (event.key === prevKey) {
|
|
55
|
+
event.preventDefault()
|
|
56
|
+
moveTo(ids[currentIndex <= 0 ? ids.length - 1 : currentIndex - 1])
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
if (event.key === nextKey) {
|
|
60
|
+
event.preventDefault()
|
|
61
|
+
moveTo(ids[currentIndex === -1 || currentIndex >= ids.length - 1 ? 0 : currentIndex + 1])
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { handleKeydown }
|
|
66
|
+
}
|
|
@@ -200,6 +200,42 @@ export function useScheduleDrag(options: UseScheduleDragOptions) {
|
|
|
200
200
|
cleanup()
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Keyboard alternative to the pointer drag: shifts an event by one slot
|
|
205
|
+
* (`mode: 'move'`) or grows/shrinks its end by one slot (`mode: 'resize'`),
|
|
206
|
+
* staying inside the visible day. Returns whether the nudge was applied.
|
|
207
|
+
*/
|
|
208
|
+
function nudgeEvent(
|
|
209
|
+
event: ScheduleEvent,
|
|
210
|
+
direction: -1 | 1,
|
|
211
|
+
mode: 'move' | 'resize' = 'move',
|
|
212
|
+
): boolean {
|
|
213
|
+
if (options.readonly.value) return false
|
|
214
|
+
if (mode === 'move' && event.draggable === false) return false
|
|
215
|
+
if (mode === 'resize' && event.resizable === false) return false
|
|
216
|
+
|
|
217
|
+
const slotMin = options.slotDuration.value
|
|
218
|
+
const dayStart = options.dayStartHour.value * 60
|
|
219
|
+
const dayEnd = options.dayEndHour.value * 60
|
|
220
|
+
const start = dateToMinutes(new Date(event.start))
|
|
221
|
+
const end = dateToMinutes(new Date(event.end))
|
|
222
|
+
|
|
223
|
+
const nextStart = mode === 'move' ? start + direction * slotMin : start
|
|
224
|
+
const nextEnd = mode === 'move'
|
|
225
|
+
? end + direction * slotMin
|
|
226
|
+
: Math.max(start + slotMin, end + direction * slotMin)
|
|
227
|
+
|
|
228
|
+
if (nextStart < dayStart || nextEnd > dayEnd || nextEnd <= nextStart) return false
|
|
229
|
+
|
|
230
|
+
const base = new Date(event.start)
|
|
231
|
+
const newStart = minutesToDate(base, nextStart)
|
|
232
|
+
const newEnd = minutesToDate(base, nextEnd)
|
|
233
|
+
|
|
234
|
+
if (mode === 'move') options.onMoveComplete?.(event, newStart, newEnd)
|
|
235
|
+
else options.onResizeComplete?.(event, newStart, newEnd)
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
|
|
203
239
|
function addListeners() {
|
|
204
240
|
document.addEventListener('pointermove', onPointerMove)
|
|
205
241
|
document.addEventListener('pointerup', onPointerUp)
|
|
@@ -221,6 +257,7 @@ export function useScheduleDrag(options: UseScheduleDragOptions) {
|
|
|
221
257
|
startCreate,
|
|
222
258
|
startMove,
|
|
223
259
|
startResize,
|
|
260
|
+
nudgeEvent,
|
|
224
261
|
}
|
|
225
262
|
}
|
|
226
263
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { computed, ref, toValue, type ComputedRef, type Ref } from 'vue'
|
|
2
2
|
import type { WellPlateFormat } from '../types'
|
|
3
|
+
import {
|
|
4
|
+
PLATE_CONFIGS as PLATE_DIMENSIONS,
|
|
5
|
+
rowIndex as rowLabelToIndex,
|
|
6
|
+
rowLabel as indexToRowLabel,
|
|
7
|
+
} from '../components/WellPlate.geometry'
|
|
3
8
|
|
|
4
9
|
export type WellPlateSource<T> = T | Ref<T> | ComputedRef<T> | (() => T)
|
|
5
10
|
|
|
@@ -34,19 +39,12 @@ export interface UseWellPaintingReturn<TMode extends string = string, TPayload =
|
|
|
34
39
|
cancel: () => void
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
const ROW_LABELS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
54: { rows: 6, cols: 9 },
|
|
45
|
-
96: { rows: 8, cols: 12 },
|
|
46
|
-
384: { rows: 16, cols: 24 },
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Pointer painting/rectangle selection state for WellPlate-based editors. */
|
|
43
|
+
/**
|
|
44
|
+
* Pointer painting/rectangle selection state for WellPlate-based editors.
|
|
45
|
+
*
|
|
46
|
+
* @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
|
|
47
|
+
*/
|
|
50
48
|
export function useWellPainting<TMode extends string = string, TPayload = unknown>(
|
|
51
49
|
options: UseWellPaintingOptions<TMode, TPayload>,
|
|
52
50
|
): UseWellPaintingReturn<TMode, TPayload> {
|
|
@@ -166,22 +164,4 @@ export function wellIdsInRectangle(
|
|
|
166
164
|
return ids
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
function rowLabelToIndex(label: string): number {
|
|
170
|
-
let result = 0
|
|
171
|
-
for (const char of label) {
|
|
172
|
-
result = result * 26 + (char.charCodeAt(0) - 64)
|
|
173
|
-
}
|
|
174
|
-
return result - 1
|
|
175
|
-
}
|
|
176
167
|
|
|
177
|
-
function indexToRowLabel(index: number): string {
|
|
178
|
-
if (index < ROW_LABELS.length) return ROW_LABELS[index]
|
|
179
|
-
let value = index + 1
|
|
180
|
-
let label = ''
|
|
181
|
-
while (value > 0) {
|
|
182
|
-
value--
|
|
183
|
-
label = ROW_LABELS[value % 26] + label
|
|
184
|
-
value = Math.floor(value / 26)
|
|
185
|
-
}
|
|
186
|
-
return label
|
|
187
|
-
}
|
|
@@ -28,7 +28,11 @@ export interface UseWellPlateAdapterReturn {
|
|
|
28
28
|
wells: ComputedRef<Record<string, Partial<Well>>>
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Convert domain rows into WellPlate's wells prop without hand-written adapters.
|
|
33
|
+
*
|
|
34
|
+
* @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
|
|
35
|
+
*/
|
|
32
36
|
export function useWellPlateAdapter<TEntry>(
|
|
33
37
|
options: WellPlateAdapterOptions<TEntry>,
|
|
34
38
|
): UseWellPlateAdapterReturn {
|
|
@@ -5,18 +5,8 @@ import type {
|
|
|
5
5
|
SampleType,
|
|
6
6
|
WellPlateFormat,
|
|
7
7
|
} from '../types'
|
|
8
|
+
import { SAMPLE_COLOR_SCALE as DEFAULT_PALETTE } from '../utils/color'
|
|
8
9
|
|
|
9
|
-
const DEFAULT_PALETTE = [
|
|
10
|
-
'#3B82F6', // blue
|
|
11
|
-
'#10B981', // green
|
|
12
|
-
'#EF4444', // red
|
|
13
|
-
'#F59E0B', // amber
|
|
14
|
-
'#8B5CF6', // purple
|
|
15
|
-
'#F97316', // orange
|
|
16
|
-
'#06B6D4', // cyan
|
|
17
|
-
'#14B8A6', // teal
|
|
18
|
-
'#6B7280', // gray
|
|
19
|
-
]
|
|
20
10
|
|
|
21
11
|
const MAX_HISTORY = 50
|
|
22
12
|
|
|
@@ -33,7 +33,11 @@ export interface UseWellPlateValidationReturn<TType extends string = string> {
|
|
|
33
33
|
errorCount: ComputedRef<number>
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Rule-driven validation state for WellPlate layout/design screens.
|
|
38
|
+
*
|
|
39
|
+
* @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
|
|
40
|
+
*/
|
|
37
41
|
export function useWellPlateValidation<TContext, TType extends string = string>(
|
|
38
42
|
options: UseWellPlateValidationOptions<TContext, TType>,
|
|
39
43
|
): UseWellPlateValidationReturn<TType> {
|