@morscherlab/mint-sdk 1.2.0-beta.1 → 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 +35 -5
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
- package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
- package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
- package/dist/{SettingsModal-BgKY93zh.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/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -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/AutoGroupModal.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/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/JobsStatusTray.vue.d.ts +9 -2
- 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/SampleLegend.vue.d.ts +1 -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 +35 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +28 -0
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- 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-DkHddRMl.js → components-CgAUzF8F.js} +2497 -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 +9 -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 +9 -0
- package/dist/composables/useFocusTrap.d.ts +21 -0
- package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
- package/dist/composables/useJobsStatusTray.d.ts +25 -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 +2 -0
- package/dist/composables/usePluginConfig.d.ts +27 -0
- 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-C_i90sGS.js → composables-B_pia3hO.js} +209 -82
- package/dist/composables-B_pia3hO.js.map +1 -0
- package/dist/{useApi-J6zFkHmn.js → errors-_W0-LbA6.js} +103 -3
- package/dist/errors-_W0-LbA6.js.map +1 -0
- package/dist/{experiment-Cq145oEW.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/auth.d.ts +6 -0
- 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-DsP51i-S.js → templates-Qq113pyZ.js} +303 -106
- package/dist/templates-Qq113pyZ.js.map +1 -0
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-aMZlb4Sc.js} +462 -1130
- 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-CV4iNfMS.js → useExperimentSelector-I8HC8hUu.js} +4 -4
- package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
- package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-TCxNnqeb.js} +2 -2
- package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
- package/dist/{useProtocolTemplates-CS_SQl6U.js → useProtocolTemplates-BmX_iVBR.js} +766 -212
- 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/GroupAssigner.test.ts +53 -0
- 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 +2 -0
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePluginClient.test.ts +140 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
- 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.vue +16 -5
- 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 +43 -0
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useAuth.ts +3 -0
- 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 +14 -2
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFocusTrap.ts +91 -0
- package/src/composables/useGeneratedAnalysis.ts +22 -10
- package/src/composables/useJobsStatusTray.ts +184 -17
- 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 +47 -4
- package/src/composables/usePluginConfig.ts +252 -0
- 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/auth.ts +1 -0
- 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/auth.ts +1 -0
- 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-DyPAUCLw.js.map +0 -1
- package/dist/SettingsModal-BgKY93zh.js.map +0 -1
- package/dist/components-DkHddRMl.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-C_i90sGS.js.map +0 -1
- package/dist/experiment-Cq145oEW.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-DsP51i-S.js.map +0 -1
- package/dist/useApi-J6zFkHmn.js.map +0 -1
- package/dist/useControlSchema-BDx2Olha.js.map +0 -1
- package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
- package/dist/useProtocolTemplates-CS_SQl6U.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
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
import { h, Transition, type VNode } from 'vue'
|
|
4
4
|
import type { TreeNodeType, BadgeVariant, TreeNode } from '../types'
|
|
5
5
|
import { useExpansionSet } from '../composables/useExpansionSet'
|
|
6
|
+
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
7
|
+
|
|
8
|
+
/** Folder glyph kept from the pre-1.2 hand-rolled empty state. */
|
|
9
|
+
const EMPTY_ICON_PATH
|
|
10
|
+
= 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z'
|
|
6
11
|
|
|
7
12
|
interface Props {
|
|
8
13
|
nodes: TreeNode[]
|
|
@@ -12,6 +17,13 @@ interface Props {
|
|
|
12
17
|
showCounts?: boolean
|
|
13
18
|
maxDepth?: number
|
|
14
19
|
size?: 'sm' | 'md' | 'lg'
|
|
20
|
+
/** Replace the tree with a loading placeholder. */
|
|
21
|
+
loading?: boolean
|
|
22
|
+
/**
|
|
23
|
+
* Error message. When set, the tree is replaced by an error state.
|
|
24
|
+
* `null`/`undefined` renders normally.
|
|
25
|
+
*/
|
|
26
|
+
error?: string | null
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -20,6 +32,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
20
32
|
showIcons: true,
|
|
21
33
|
showCounts: true,
|
|
22
34
|
size: 'md',
|
|
35
|
+
loading: false,
|
|
36
|
+
error: null,
|
|
23
37
|
})
|
|
24
38
|
|
|
25
39
|
const emit = defineEmits<{
|
|
@@ -226,12 +240,25 @@ function renderTree(): VNode[] {
|
|
|
226
240
|
|
|
227
241
|
<template>
|
|
228
242
|
<div :class="['mint-sample-tree', `mint-sample-tree--${size}`]" role="tree">
|
|
243
|
+
<!-- Error state -->
|
|
244
|
+
<div v-if="error" class="mint-sample-tree__state">
|
|
245
|
+
<slot name="error" :error="error">
|
|
246
|
+
<DataStateInternal state="error" size="sm" :title="error" />
|
|
247
|
+
</slot>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- Loading state -->
|
|
251
|
+
<div v-else-if="loading" class="mint-sample-tree__state">
|
|
252
|
+
<slot name="loading">
|
|
253
|
+
<DataStateInternal state="loading" size="sm" loading-label="Loading samples" />
|
|
254
|
+
</slot>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
229
257
|
<!-- Empty state -->
|
|
230
|
-
<div v-if="nodes.length === 0" class="mint-sample-
|
|
231
|
-
<
|
|
232
|
-
<
|
|
233
|
-
</
|
|
234
|
-
<p class="mint-sample-tree__empty-text">No items</p>
|
|
258
|
+
<div v-else-if="nodes.length === 0" class="mint-sample-tree__state">
|
|
259
|
+
<slot name="empty">
|
|
260
|
+
<DataStateInternal state="empty" size="sm" title="No items" :icon-path="EMPTY_ICON_PATH" />
|
|
261
|
+
</slot>
|
|
235
262
|
</div>
|
|
236
263
|
|
|
237
264
|
<!-- Tree nodes rendered via render function -->
|
|
@@ -240,7 +267,3 @@ function renderTree(): VNode[] {
|
|
|
240
267
|
</template>
|
|
241
268
|
</div>
|
|
242
269
|
</template>
|
|
243
|
-
|
|
244
|
-
<style>
|
|
245
|
-
@import '../styles/components/sample-hierarchy-tree.css';
|
|
246
|
-
</style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Clickable color-swatch legend for plate sample types, supporting selection, count display, add/remove, and custom color assignment. */
|
|
3
3
|
import type { SampleType } from '../types'
|
|
4
|
+
import { SAMPLE_COLOR_SCALE } from '../utils/color'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
modelValue?: string
|
|
@@ -17,10 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
17
18
|
samples: () => [],
|
|
18
19
|
showCounts: true,
|
|
19
20
|
editable: false,
|
|
20
|
-
colorPalette: () => [
|
|
21
|
-
'#3B82F6', '#10B981', '#EF4444', '#F59E0B', '#8B5CF6',
|
|
22
|
-
'#F97316', '#06B6D4', '#14B8A6', '#6B7280',
|
|
23
|
-
],
|
|
21
|
+
colorPalette: () => [...SAMPLE_COLOR_SCALE],
|
|
24
22
|
size: 'md',
|
|
25
23
|
orientation: 'vertical',
|
|
26
24
|
})
|
|
@@ -113,7 +111,3 @@ function handleAdd() {
|
|
|
113
111
|
</button>
|
|
114
112
|
</div>
|
|
115
113
|
</template>
|
|
116
|
-
|
|
117
|
-
<style>
|
|
118
|
-
@import '../styles/components/sample-legend.css';
|
|
119
|
-
</style>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ref } from 'vue'
|
|
2
2
|
import type { SampleGroup } from '../types'
|
|
3
|
+
import { useReorderAnnouncer } from '../composables/useListReorder'
|
|
4
|
+
import { getSampleGroupMajorPrefix } from '../utils/sampleGroupPath'
|
|
3
5
|
import {
|
|
4
6
|
detectSampleGroupSeparator,
|
|
5
7
|
getSampleGroupParentPath,
|
|
@@ -23,6 +25,7 @@ export function useSampleSelectorDrag(groups: MutableSampleGroups) {
|
|
|
23
25
|
const draggingGroupKind = ref<GroupDragKind | null>(null)
|
|
24
26
|
const reorderTarget = ref<string | null>(null)
|
|
25
27
|
const reorderPosition = ref<GroupReorderPosition | null>(null)
|
|
28
|
+
const { announcement, announce, announcerStyle } = useReorderAnnouncer()
|
|
26
29
|
|
|
27
30
|
function handleDragStart(sample: string, sourceGroup: string | null, event: DragEvent) {
|
|
28
31
|
draggingSample.value = sample
|
|
@@ -143,7 +146,45 @@ export function useSampleSelectorDrag(groups: MutableSampleGroups) {
|
|
|
143
146
|
handleGroupDragEnd()
|
|
144
147
|
}
|
|
145
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Keyboard alternative to the group reorder drag. The hierarchy constrains a
|
|
151
|
+
* move to the dragged node's siblings (a leaf may not cross a parent), so this
|
|
152
|
+
* is not a flat list reorder and does not use `useListReorder`: Alt+ArrowUp /
|
|
153
|
+
* Alt+ArrowDown swap the node with its previous / next sibling instead.
|
|
154
|
+
*/
|
|
155
|
+
function handleGroupReorderKeydown(name: string, kind: GroupDragKind, event: KeyboardEvent) {
|
|
156
|
+
if (!event.altKey) return
|
|
157
|
+
const direction = event.key === 'ArrowUp' ? -1 : event.key === 'ArrowDown' ? 1 : 0
|
|
158
|
+
if (direction === 0) return
|
|
159
|
+
|
|
160
|
+
event.preventDefault()
|
|
161
|
+
const separator = detectSampleGroupSeparator(groups.value)
|
|
162
|
+
const siblings = kind === 'major'
|
|
163
|
+
? uniqueMajorPrefixes(groups.value, separator)
|
|
164
|
+
: groups.value
|
|
165
|
+
.filter(group =>
|
|
166
|
+
getSampleGroupParentPath(group.name, separator) ===
|
|
167
|
+
getSampleGroupParentPath(name, separator))
|
|
168
|
+
.map(group => group.name)
|
|
169
|
+
|
|
170
|
+
const currentIndex = siblings.indexOf(name)
|
|
171
|
+
const target = siblings[currentIndex + direction]
|
|
172
|
+
if (currentIndex === -1 || target === undefined) return
|
|
173
|
+
|
|
174
|
+
const position: GroupReorderPosition = direction === -1 ? 'before' : 'after'
|
|
175
|
+
const nextGroups = kind === 'major'
|
|
176
|
+
? reorderSampleMajorGroup(groups.value, name, target, position)
|
|
177
|
+
: reorderSampleGroup(groups.value, name, target, position)
|
|
178
|
+
|
|
179
|
+
if (nextGroups === groups.value) return
|
|
180
|
+
groups.value = nextGroups
|
|
181
|
+
announce(`${name} moved ${direction === -1 ? 'up' : 'down'} to position ${currentIndex + direction + 1} of ${siblings.length}.`)
|
|
182
|
+
}
|
|
183
|
+
|
|
146
184
|
return {
|
|
185
|
+
announcement,
|
|
186
|
+
announcerStyle,
|
|
187
|
+
handleGroupReorderKeydown,
|
|
147
188
|
draggingSample,
|
|
148
189
|
dragOverGroup,
|
|
149
190
|
draggingGroup,
|
|
@@ -162,3 +203,13 @@ export function useSampleSelectorDrag(groups: MutableSampleGroups) {
|
|
|
162
203
|
handleGroupDrop,
|
|
163
204
|
}
|
|
164
205
|
}
|
|
206
|
+
|
|
207
|
+
/** Major-group prefixes in tree order, de-duplicated. */
|
|
208
|
+
function uniqueMajorPrefixes(groups: SampleGroup[], separator: string): string[] {
|
|
209
|
+
const seen: string[] = []
|
|
210
|
+
for (const group of groups) {
|
|
211
|
+
const prefix = getSampleGroupMajorPrefix(group.name, separator)
|
|
212
|
+
if (!seen.includes(prefix)) seen.push(prefix)
|
|
213
|
+
}
|
|
214
|
+
return seen
|
|
215
|
+
}
|
|
@@ -72,6 +72,8 @@ const lcmsSamples = ['Liver', 'Kidney'].flatMap(tissue =>
|
|
|
72
72
|
|
|
73
73
|
function initState() {
|
|
74
74
|
return {
|
|
75
|
+
loading: false,
|
|
76
|
+
error: '',
|
|
75
77
|
samples: mockSamples,
|
|
76
78
|
selected: [] as string[],
|
|
77
79
|
groups: [] as SampleGroup[],
|
|
@@ -100,6 +102,8 @@ function handleSmartGroup(result: AutoGroupResult, state: { groups: SampleGroup[
|
|
|
100
102
|
<template #default="{ state }">
|
|
101
103
|
<div style="padding: 2rem; max-width: 400px;">
|
|
102
104
|
<SampleSelector
|
|
105
|
+
:loading="state.loading"
|
|
106
|
+
:error="state.error || null"
|
|
103
107
|
v-model="state.selected"
|
|
104
108
|
v-model:groups="state.groups"
|
|
105
109
|
:samples="state.samples"
|
|
@@ -110,6 +114,8 @@ function handleSmartGroup(result: AutoGroupResult, state: { groups: SampleGroup[
|
|
|
110
114
|
</div>
|
|
111
115
|
</template>
|
|
112
116
|
<template #controls="{ state }">
|
|
117
|
+
<HstCheckbox v-model="state.loading" title="Loading" />
|
|
118
|
+
<HstText v-model="state.error" title="Error Message" />
|
|
113
119
|
<HstCheckbox v-model="state.enableGrouping" title="Enable Grouping" />
|
|
114
120
|
<HstCheckbox v-model="state.enableSmartGroup" title="Enable Smart" />
|
|
115
121
|
</template>
|
|
@@ -34,6 +34,8 @@ export interface SampleGroupTreeContext {
|
|
|
34
34
|
onGroupDragOver: (name: string, kind: GroupDragKind, event: DragEvent) => void
|
|
35
35
|
onGroupDragLeave: (event: DragEvent) => void
|
|
36
36
|
onGroupDrop: (name: string, kind: GroupDragKind, event: DragEvent) => void
|
|
37
|
+
/** Alt+ArrowUp / Alt+ArrowDown reorder a group among its siblings. */
|
|
38
|
+
onGroupReorderKeydown: (name: string, kind: GroupDragKind, event: KeyboardEvent) => void
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/** Every concrete group name beneath a node, in tree order. */
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
removeSampleFromGroup as removeSampleFromSampleGroup,
|
|
26
26
|
removeSampleGroups,
|
|
27
27
|
} from './SampleSelector.groups'
|
|
28
|
+
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
28
29
|
|
|
29
30
|
interface Props {
|
|
30
31
|
samples?: string[]
|
|
@@ -35,6 +36,15 @@ interface Props {
|
|
|
35
36
|
experimentId?: number
|
|
36
37
|
designData?: Record<string, unknown>
|
|
37
38
|
autoloadExperimentData?: boolean
|
|
39
|
+
/** Replace the selector body with a loading placeholder. */
|
|
40
|
+
loading?: boolean
|
|
41
|
+
/**
|
|
42
|
+
* Error message. When set, the selector body is replaced by an error state.
|
|
43
|
+
* `null`/`undefined` renders normally.
|
|
44
|
+
*/
|
|
45
|
+
error?: string | null
|
|
46
|
+
/** Empty-state headline shown when there are no samples to select from. */
|
|
47
|
+
emptyMessage?: string
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -45,6 +55,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
45
55
|
experimentId: undefined,
|
|
46
56
|
designData: undefined,
|
|
47
57
|
autoloadExperimentData: true,
|
|
58
|
+
loading: false,
|
|
59
|
+
error: null,
|
|
60
|
+
emptyMessage: 'No samples available',
|
|
48
61
|
})
|
|
49
62
|
|
|
50
63
|
const emit = defineEmits<{
|
|
@@ -69,6 +82,8 @@ const internalGroups = computed({
|
|
|
69
82
|
})
|
|
70
83
|
|
|
71
84
|
const {
|
|
85
|
+
announcement: reorderAnnouncement,
|
|
86
|
+
announcerStyle,
|
|
72
87
|
draggingSample,
|
|
73
88
|
dragOverGroup,
|
|
74
89
|
draggingGroup,
|
|
@@ -81,6 +96,7 @@ const {
|
|
|
81
96
|
handleDragLeave,
|
|
82
97
|
handleDrop,
|
|
83
98
|
handleGroupDragStart,
|
|
99
|
+
handleGroupReorderKeydown,
|
|
84
100
|
handleGroupDragEnd,
|
|
85
101
|
handleGroupDragOver,
|
|
86
102
|
handleGroupDragLeave,
|
|
@@ -227,6 +243,7 @@ const treeContext = computed<SampleGroupTreeContext>(() => ({
|
|
|
227
243
|
onGroupDragOver: handleGroupDragOver,
|
|
228
244
|
onGroupDragLeave: handleGroupDragLeave,
|
|
229
245
|
onGroupDrop: handleGroupDrop,
|
|
246
|
+
onGroupReorderKeydown: handleGroupReorderKeydown,
|
|
230
247
|
}))
|
|
231
248
|
|
|
232
249
|
// New group
|
|
@@ -237,13 +254,36 @@ function addNewGroup() {
|
|
|
237
254
|
newGroupName.value = ''
|
|
238
255
|
}
|
|
239
256
|
|
|
257
|
+
/** True while a loading, error or empty placeholder stands in for the body. */
|
|
258
|
+
const isPlaceholderState = computed(() =>
|
|
259
|
+
props.loading || Boolean(props.error) || resolvedSamples.value.length === 0,
|
|
260
|
+
)
|
|
261
|
+
|
|
240
262
|
defineExpose({ handleSmartGroupApply })
|
|
241
263
|
</script>
|
|
242
264
|
|
|
243
265
|
<template>
|
|
244
266
|
<div class="mint-sample-selector">
|
|
267
|
+
<div v-if="error" class="mint-sample-selector__state">
|
|
268
|
+
<slot name="error" :error="error">
|
|
269
|
+
<DataStateInternal state="error" size="sm" :title="error" />
|
|
270
|
+
</slot>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div v-else-if="loading" class="mint-sample-selector__state">
|
|
274
|
+
<slot name="loading">
|
|
275
|
+
<DataStateInternal state="loading" size="sm" loading-label="Loading samples" />
|
|
276
|
+
</slot>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div v-else-if="resolvedSamples.length === 0" class="mint-sample-selector__state">
|
|
280
|
+
<slot name="empty">
|
|
281
|
+
<DataStateInternal state="empty" size="sm" :title="emptyMessage" />
|
|
282
|
+
</slot>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
245
285
|
<!-- Select All Row -->
|
|
246
|
-
<label class="mint-sample-selector__select-all">
|
|
286
|
+
<label v-if="!isPlaceholderState" class="mint-sample-selector__select-all">
|
|
247
287
|
<input
|
|
248
288
|
type="checkbox"
|
|
249
289
|
:checked="isAllSelected"
|
|
@@ -255,7 +295,7 @@ defineExpose({ handleSmartGroupApply })
|
|
|
255
295
|
</label>
|
|
256
296
|
|
|
257
297
|
<!-- Action Buttons Row -->
|
|
258
|
-
<div v-if="enableGrouping" class="mint-sample-selector__actions">
|
|
298
|
+
<div v-if="!isPlaceholderState && enableGrouping" class="mint-sample-selector__actions">
|
|
259
299
|
<div class="mint-sample-selector__actions-row">
|
|
260
300
|
<!-- Grouping Button -->
|
|
261
301
|
<BaseButton
|
|
@@ -291,7 +331,7 @@ defineExpose({ handleSmartGroupApply })
|
|
|
291
331
|
</div>
|
|
292
332
|
|
|
293
333
|
<!-- Grouped View -->
|
|
294
|
-
<div v-if="groupingEnabled" class="mint-sample-selector__grouped">
|
|
334
|
+
<div v-if="!isPlaceholderState && groupingEnabled" class="mint-sample-selector__grouped">
|
|
295
335
|
<!-- Groups Header -->
|
|
296
336
|
<div class="mint-sample-selector__groups-header">
|
|
297
337
|
<span class="mint-sample-selector__groups-title">Groups ({{ internalGroups.length }})</span>
|
|
@@ -320,7 +360,7 @@ defineExpose({ handleSmartGroupApply })
|
|
|
320
360
|
/>
|
|
321
361
|
|
|
322
362
|
<!-- Empty state -->
|
|
323
|
-
<div v-if="internalGroups.length === 0" class="mint-sample-
|
|
363
|
+
<div v-if="internalGroups.length === 0" class="mint-sample-selector__hint">
|
|
324
364
|
Click Smart to auto-group samples
|
|
325
365
|
</div>
|
|
326
366
|
</div>
|
|
@@ -394,7 +434,7 @@ defineExpose({ handleSmartGroupApply })
|
|
|
394
434
|
</div>
|
|
395
435
|
|
|
396
436
|
<!-- Flat View (when no groups) -->
|
|
397
|
-
<div v-if="!groupingEnabled" class="mint-sample-selector__flat">
|
|
437
|
+
<div v-if="!isPlaceholderState && !groupingEnabled" class="mint-sample-selector__flat">
|
|
398
438
|
<!-- Search -->
|
|
399
439
|
<div class="mint-sample-selector__search">
|
|
400
440
|
<svg class="mint-sample-selector__search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -427,7 +467,7 @@ defineExpose({ handleSmartGroupApply })
|
|
|
427
467
|
<span class="mint-sample-selector__flat-name">{{ sample }}</span>
|
|
428
468
|
</div>
|
|
429
469
|
|
|
430
|
-
<div v-if="filteredSamples.length === 0 && searchQuery.trim()" class="mint-sample-
|
|
470
|
+
<div v-if="filteredSamples.length === 0 && searchQuery.trim()" class="mint-sample-selector__hint">
|
|
431
471
|
No samples match "{{ searchQuery }}"
|
|
432
472
|
</div>
|
|
433
473
|
</div>
|
|
@@ -443,9 +483,8 @@ defineExpose({ handleSmartGroupApply })
|
|
|
443
483
|
:design-data="resolvedDesignData"
|
|
444
484
|
@apply="handleSmartGroupApply"
|
|
445
485
|
/>
|
|
486
|
+
|
|
487
|
+
<!-- Group reorder announcements for screen readers -->
|
|
488
|
+
<div role="status" aria-live="polite" :style="announcerStyle">{{ reorderAnnouncement }}</div>
|
|
446
489
|
</div>
|
|
447
490
|
</template>
|
|
448
|
-
|
|
449
|
-
<style>
|
|
450
|
-
@import '../styles/components/sample-selector.css';
|
|
451
|
-
</style>
|
|
@@ -54,6 +54,18 @@ function onHeaderDrop(event: DragEvent) {
|
|
|
54
54
|
if (dragKind.value === null) return
|
|
55
55
|
props.ctx.onGroupDrop(dragName.value, dragKind.value, event)
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
/** Enter/Space expand the node; Alt+ArrowUp/Down reorder it among its siblings. */
|
|
59
|
+
function onHeaderKeydown(event: KeyboardEvent) {
|
|
60
|
+
if (event.altKey) {
|
|
61
|
+
if (dragKind.value === null) return
|
|
62
|
+
props.ctx.onGroupReorderKeydown(dragName.value, dragKind.value, event)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
66
|
+
event.preventDefault()
|
|
67
|
+
props.ctx.toggleExpanded(props.node.key)
|
|
68
|
+
}
|
|
57
69
|
</script>
|
|
58
70
|
|
|
59
71
|
<template>
|
|
@@ -81,7 +93,14 @@ function onHeaderDrop(event: DragEvent) {
|
|
|
81
93
|
reorderEdge === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',
|
|
82
94
|
]"
|
|
83
95
|
:draggable="dragKind !== null"
|
|
96
|
+
role="button"
|
|
97
|
+
tabindex="0"
|
|
98
|
+
:aria-expanded="expanded"
|
|
99
|
+
:aria-label="dragKind !== null
|
|
100
|
+
? `${node.label}. Press Alt with the up or down arrow keys to reorder.`
|
|
101
|
+
: node.label"
|
|
84
102
|
@click="ctx.toggleExpanded(node.key)"
|
|
103
|
+
@keydown="onHeaderKeydown"
|
|
85
104
|
@dragstart="onHeaderDragStart"
|
|
86
105
|
@dragend="ctx.onGroupDragEnd"
|
|
87
106
|
@dragover="onHeaderDragOver"
|
|
@@ -86,6 +86,8 @@ const mockBlockedSlots: ScheduleBlockedSlot[] = [
|
|
|
86
86
|
|
|
87
87
|
function initState() {
|
|
88
88
|
return {
|
|
89
|
+
loading: false,
|
|
90
|
+
error: '',
|
|
89
91
|
modelValue: today,
|
|
90
92
|
view: 'week' as const,
|
|
91
93
|
events: mockEvents,
|
|
@@ -106,6 +108,8 @@ function initState() {
|
|
|
106
108
|
<template #default="{ state }">
|
|
107
109
|
<div style="padding: 1rem; height: 700px;">
|
|
108
110
|
<ScheduleCalendar
|
|
111
|
+
:loading="state.loading"
|
|
112
|
+
:error="state.error || null"
|
|
109
113
|
v-model="state.modelValue"
|
|
110
114
|
:view="state.view"
|
|
111
115
|
:events="state.events"
|
|
@@ -123,6 +127,8 @@ function initState() {
|
|
|
123
127
|
</div>
|
|
124
128
|
</template>
|
|
125
129
|
<template #controls="{ state }">
|
|
130
|
+
<HstCheckbox v-model="state.loading" title="Loading" />
|
|
131
|
+
<HstText v-model="state.error" title="Error Message" />
|
|
126
132
|
<HstSelect
|
|
127
133
|
v-model="state.view"
|
|
128
134
|
title="View"
|
|
@@ -7,11 +7,13 @@ import type {
|
|
|
7
7
|
ScheduleBlockedSlot,
|
|
8
8
|
} from '../types/components'
|
|
9
9
|
import { useScheduleDrag } from '../composables/useScheduleDrag'
|
|
10
|
+
import { useReorderAnnouncer } from '../composables/useListReorder'
|
|
10
11
|
import {
|
|
11
12
|
SCHEDULE_SLOT_HEIGHT,
|
|
12
13
|
parseScheduleModelDate,
|
|
13
14
|
useScheduleCalendarLayout,
|
|
14
15
|
} from '../composables/useScheduleCalendarLayout'
|
|
16
|
+
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
15
17
|
|
|
16
18
|
interface Props {
|
|
17
19
|
modelValue?: Date | string
|
|
@@ -28,6 +30,19 @@ interface Props {
|
|
|
28
30
|
showNavigation?: boolean
|
|
29
31
|
statusColors?: Record<string, string>
|
|
30
32
|
locale?: string
|
|
33
|
+
/** Replace the calendar grid with a loading placeholder. */
|
|
34
|
+
loading?: boolean
|
|
35
|
+
/**
|
|
36
|
+
* Error message. When set, the calendar grid is replaced by an error state.
|
|
37
|
+
* `null`/`undefined` renders normally.
|
|
38
|
+
*/
|
|
39
|
+
error?: string | null
|
|
40
|
+
/**
|
|
41
|
+
* Opt-in empty-state headline. An empty grid is meaningful on a calendar, so
|
|
42
|
+
* the empty state only replaces it when this message is supplied and there
|
|
43
|
+
* are no events.
|
|
44
|
+
*/
|
|
45
|
+
emptyMessage?: string
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -43,6 +58,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
43
58
|
showViewToggle: true,
|
|
44
59
|
showNavigation: true,
|
|
45
60
|
locale: 'en-US',
|
|
61
|
+
loading: false,
|
|
62
|
+
error: null,
|
|
46
63
|
})
|
|
47
64
|
|
|
48
65
|
const emit = defineEmits<{
|
|
@@ -57,6 +74,11 @@ const emit = defineEmits<{
|
|
|
57
74
|
|
|
58
75
|
const SLOT_HEIGHT = SCHEDULE_SLOT_HEIGHT
|
|
59
76
|
const currentView = ref<ScheduleView>(props.view)
|
|
77
|
+
|
|
78
|
+
/** Empty state is opt-in: it needs an `emptyMessage` and an event-free range. */
|
|
79
|
+
const showEmptyState = computed(() =>
|
|
80
|
+
Boolean(props.emptyMessage) && props.events.length === 0,
|
|
81
|
+
)
|
|
60
82
|
const currentDate = ref<Date>(parseScheduleModelDate(props.modelValue))
|
|
61
83
|
const nowTimer = ref<ReturnType<typeof setInterval>>()
|
|
62
84
|
const now = ref(new Date())
|
|
@@ -76,7 +98,9 @@ const weekStartsOnRef = computed(() => props.weekStartsOn)
|
|
|
76
98
|
const localeRef = computed(() => props.locale)
|
|
77
99
|
const showNowIndicatorRef = computed(() => props.showNowIndicator)
|
|
78
100
|
|
|
79
|
-
const {
|
|
101
|
+
const { announcement, announce, announcerStyle } = useReorderAnnouncer()
|
|
102
|
+
|
|
103
|
+
const { isDragging, ghost, startCreate, startMove, startResize, nudgeEvent } = useScheduleDrag({
|
|
80
104
|
slotDuration: slotDurationRef,
|
|
81
105
|
dayStartHour: dayStartRef,
|
|
82
106
|
dayEndHour: dayEndRef,
|
|
@@ -180,6 +204,35 @@ function onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIn
|
|
|
180
204
|
startResize(event, edge, e.clientY, dayIndex)
|
|
181
205
|
}
|
|
182
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Keyboard alternative to dragging an event: Alt+ArrowUp/Down move it by one
|
|
209
|
+
* slot, Shift+Alt+ArrowUp/Down shorten or lengthen it, Enter/Space open it.
|
|
210
|
+
*/
|
|
211
|
+
function onEventKeydown(event: ScheduleEvent, e: KeyboardEvent) {
|
|
212
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
213
|
+
e.preventDefault()
|
|
214
|
+
emit('event-click', event)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
if (!e.altKey) return
|
|
218
|
+
const direction = e.key === 'ArrowUp' ? -1 : e.key === 'ArrowDown' ? 1 : 0
|
|
219
|
+
if (direction === 0) return
|
|
220
|
+
|
|
221
|
+
e.preventDefault()
|
|
222
|
+
const mode = e.shiftKey ? 'resize' : 'move'
|
|
223
|
+
const applied = nudgeEvent(event, direction, mode)
|
|
224
|
+
announce(applied
|
|
225
|
+
? `${event.title} ${mode === 'move' ? 'moved' : 'resized'} to ${formatEventTime(event)}.`
|
|
226
|
+
: `${event.title} cannot move further.`)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Instructions announced with each event. */
|
|
230
|
+
function eventLabel(event: ScheduleEvent): string {
|
|
231
|
+
const base = `${event.title}, ${formatEventTime(event)}`
|
|
232
|
+
if (props.readonly || event.draggable === false) return base
|
|
233
|
+
return `${base}. Press Alt with the up or down arrow keys to reschedule it.`
|
|
234
|
+
}
|
|
235
|
+
|
|
183
236
|
function onMonthDayClick(date: Date) {
|
|
184
237
|
currentDate.value = date
|
|
185
238
|
emit('update:modelValue', date)
|
|
@@ -254,8 +307,27 @@ onUnmounted(() => {
|
|
|
254
307
|
</div>
|
|
255
308
|
</div>
|
|
256
309
|
|
|
310
|
+
<!-- Loading / error / empty placeholders replace the grid -->
|
|
311
|
+
<div v-if="error" class="mint-schedule__state">
|
|
312
|
+
<slot name="error" :error="error">
|
|
313
|
+
<DataStateInternal state="error" :title="error" />
|
|
314
|
+
</slot>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div v-else-if="loading" class="mint-schedule__state">
|
|
318
|
+
<slot name="loading">
|
|
319
|
+
<DataStateInternal state="loading" loading-label="Loading schedule" />
|
|
320
|
+
</slot>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div v-else-if="showEmptyState" class="mint-schedule__state">
|
|
324
|
+
<slot name="empty">
|
|
325
|
+
<DataStateInternal state="empty" :title="emptyMessage" />
|
|
326
|
+
</slot>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
257
329
|
<!-- Day / Week View -->
|
|
258
|
-
<template v-if="currentView === 'day' || currentView === 'week'">
|
|
330
|
+
<template v-else-if="currentView === 'day' || currentView === 'week'">
|
|
259
331
|
<!-- Day headers -->
|
|
260
332
|
<div v-if="currentView === 'week'" class="mint-schedule__day-headers">
|
|
261
333
|
<div class="mint-schedule__day-header-gutter" />
|
|
@@ -312,7 +384,11 @@ onUnmounted(() => {
|
|
|
312
384
|
:key="event.id"
|
|
313
385
|
:class="['mint-schedule__event', getStatusClass(event.status)]"
|
|
314
386
|
:style="getEventStyle(event, day) || undefined"
|
|
387
|
+
role="button"
|
|
388
|
+
tabindex="0"
|
|
389
|
+
:aria-label="eventLabel(event)"
|
|
315
390
|
@click="onEventClick(event, $event)"
|
|
391
|
+
@keydown="onEventKeydown(event, $event)"
|
|
316
392
|
@pointerdown="onEventPointerDown(event, dayIdx, $event)"
|
|
317
393
|
>
|
|
318
394
|
<slot name="event" :event="event">
|
|
@@ -364,7 +440,7 @@ onUnmounted(() => {
|
|
|
364
440
|
</template>
|
|
365
441
|
|
|
366
442
|
<!-- Month View -->
|
|
367
|
-
<template v-if="currentView === 'month'">
|
|
443
|
+
<template v-else-if="currentView === 'month'">
|
|
368
444
|
<div class="mint-schedule__month-grid">
|
|
369
445
|
<!-- Weekday headers -->
|
|
370
446
|
<div
|
|
@@ -406,9 +482,8 @@ onUnmounted(() => {
|
|
|
406
482
|
</div>
|
|
407
483
|
</div>
|
|
408
484
|
</template>
|
|
485
|
+
|
|
486
|
+
<!-- Reschedule announcements for screen readers -->
|
|
487
|
+
<div role="status" aria-live="polite" :style="announcerStyle">{{ announcement }}</div>
|
|
409
488
|
</div>
|
|
410
489
|
</template>
|
|
411
|
-
|
|
412
|
-
<style>
|
|
413
|
-
@import '../styles/components/schedule-calendar.css';
|
|
414
|
-
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import SecretInput from './SecretInput.vue'
|
|
4
|
+
import type { PluginSecretRef } from '../utils/pluginSecrets'
|
|
5
|
+
|
|
6
|
+
const sizes = ['sm', 'md', 'lg'] as const
|
|
7
|
+
|
|
8
|
+
const storedRef: PluginSecretRef = { $secret: 'sec_0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e', set: true }
|
|
9
|
+
const lockedRef: PluginSecretRef = { $secret: null, locked: true }
|
|
10
|
+
|
|
11
|
+
const stored = ref<PluginSecretRef | string | null>({ ...storedRef })
|
|
12
|
+
const unset = ref<PluginSecretRef | string | null>(null)
|
|
13
|
+
const locked = ref<PluginSecretRef | string | null>({ ...lockedRef })
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<Story title="Base Inputs/SecretInput">
|
|
18
|
+
<Variant title="Playground">
|
|
19
|
+
<template #default="{ state }">
|
|
20
|
+
<div style="padding: 2rem; max-width: 600px;">
|
|
21
|
+
<SecretInput
|
|
22
|
+
v-model="state.value"
|
|
23
|
+
:placeholder="state.placeholder"
|
|
24
|
+
:disabled="state.disabled"
|
|
25
|
+
:error="state.error"
|
|
26
|
+
:size="state.size"
|
|
27
|
+
/>
|
|
28
|
+
<p style="margin-top: 1rem; font-size: 0.875rem; color: var(--text-muted, #94a3b8);">
|
|
29
|
+
Submits: <code>{{ JSON.stringify(state.value) }}</code>
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<template #controls="{ state }">
|
|
35
|
+
<HstText v-model="state.placeholder" title="Placeholder" />
|
|
36
|
+
<HstSelect
|
|
37
|
+
v-model="state.size"
|
|
38
|
+
title="Size"
|
|
39
|
+
:options="sizes.map(size => ({ label: size, value: size }))"
|
|
40
|
+
/>
|
|
41
|
+
<HstCheckbox v-model="state.disabled" title="Disabled" />
|
|
42
|
+
<HstCheckbox v-model="state.error" title="Error" />
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<template #initState>
|
|
46
|
+
{{ {
|
|
47
|
+
value: { $secret: 'sec_0f2a4c6e8a0c2e4a6c8e0a2c4e6a8c0e', set: true },
|
|
48
|
+
placeholder: 'Enter a new value',
|
|
49
|
+
disabled: false,
|
|
50
|
+
error: false,
|
|
51
|
+
size: 'md',
|
|
52
|
+
} }}
|
|
53
|
+
</template>
|
|
54
|
+
</Variant>
|
|
55
|
+
|
|
56
|
+
<Variant title="States">
|
|
57
|
+
<div style="padding: 2rem; max-width: 600px; display: flex; flex-direction: column; gap: 1.5rem;">
|
|
58
|
+
<div>
|
|
59
|
+
<p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
|
|
60
|
+
Stored
|
|
61
|
+
</p>
|
|
62
|
+
<SecretInput v-model="stored" />
|
|
63
|
+
</div>
|
|
64
|
+
<div>
|
|
65
|
+
<p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
|
|
66
|
+
Not set
|
|
67
|
+
</p>
|
|
68
|
+
<SecretInput v-model="unset" />
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
<p style="margin: 0 0 0.5rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">
|
|
72
|
+
Provided by environment
|
|
73
|
+
</p>
|
|
74
|
+
<SecretInput v-model="locked" />
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</Variant>
|
|
78
|
+
</Story>
|
|
79
|
+
</template>
|