@morscherlab/mint-sdk 1.2.0-beta.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/{ExperimentPopover-CfOdh5Rm.js → ExperimentPopover-CJdNlCZb.js} +24 -15
- package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DvIagrxY.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
- package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
- package/dist/{SettingsModal-W3TYfnP9.js → SettingsModal-BYR20I4c.js} +5 -5
- package/dist/SettingsModal-BYR20I4c.js.map +1 -0
- package/dist/{EmptyState-NBqf2_8z.js → Skeleton-BAF3CKY7.js} +111 -93
- package/dist/Skeleton-BAF3CKY7.js.map +1 -0
- package/dist/__tests__/components/DataStates.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
- package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
- package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
- package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
- package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/apiErrors.d.ts +26 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/BaseInput.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +1 -1
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +2 -2
- package/dist/components/DateTimePicker.vue.d.ts +3 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
- package/dist/components/ExperimentPopover.vue.d.ts +1 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
- package/dist/components/FileBrowserModal.vue.d.ts +99 -0
- package/dist/components/FilePicker.vue.d.ts +32 -0
- package/dist/components/FileUploader.vue.d.ts +5 -2
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/MobileSupportGate.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlotlyChart.vue.d.ts +59 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
- package/dist/components/ReagentList.vue.d.ts +29 -1
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
- package/dist/components/SampleSelector.drag.d.ts +13 -0
- package/dist/components/SampleSelector.tree.d.ts +2 -0
- package/dist/components/SampleSelector.vue.d.ts +34 -1
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +1 -1
- package/dist/components/TimePicker.vue.d.ts +2 -1
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +1 -1
- package/dist/components/WellPlate.geometry.d.ts +15 -0
- package/dist/components/WellPlate.interaction.d.ts +13 -0
- package/dist/components/WellPlate.vue.d.ts +30 -1
- package/dist/components/fileBrowserPresentation.d.ts +10 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
- package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
- package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
- package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
- package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
- package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
- package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
- package/dist/{components-B80gV0P6.js → components-DIum5hkx.js} +5415 -2753
- package/dist/components-DIum5hkx.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/filePicker/localFiles.d.ts +3 -0
- package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
- package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
- package/dist/composables/filePicker/validation.d.ts +5 -0
- package/dist/composables/index.d.ts +3 -0
- package/dist/composables/index.js +10 -10
- package/dist/composables/useApi.d.ts +14 -7
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +9 -0
- package/dist/composables/useFileBrowser.d.ts +55 -0
- package/dist/composables/useFilePicker.d.ts +189 -0
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +2 -2
- package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
- package/dist/composables/usePluginConfig.d.ts +3 -1
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/{composables-C8mmD5ta.js → composables-mFXsTA4g.js} +307 -74
- package/dist/composables-mFXsTA4g.js.map +1 -0
- package/dist/{useApi-CDBq6z23.js → errors-BLuqMee5.js} +178 -16
- package/dist/errors-BLuqMee5.js.map +1 -0
- package/dist/{experiment-Y9Iz7KYi.js → experiment-CVDRAnqp.js} +3 -8
- package/dist/experiment-CVDRAnqp.js.map +1 -0
- package/dist/{experiment-utils-DV3DrvE_.js → experiment-utils-CV2Wza3z.js} +1 -1
- package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -15
- package/dist/install.js +2 -7
- package/dist/install.js.map +1 -1
- package/dist/permissions.d.ts +42 -1
- package/dist/permissions.js +20 -10
- package/dist/permissions.js.map +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +3 -3
- package/dist/stores/settings.d.ts +31 -1
- package/dist/styles.css +15162 -32475
- package/dist/templates/index.js +2 -4
- package/dist/{templates-CK72aKO1.js → templates-CmGfmBRA.js} +291 -85
- package/dist/templates-CmGfmBRA.js.map +1 -0
- package/dist/types/fileBrowserTypes.d.ts +77 -0
- package/dist/types/filePicker.d.ts +77 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/{useControlSchema-_OVhavKq.js → useControlSchema-DldpxNOP.js} +234 -1121
- package/dist/useControlSchema-DldpxNOP.js.map +1 -0
- package/dist/{useDropdownState-D5G4UvWd.js → useDropdownState-C7hRiVQf.js} +22 -6
- package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
- package/dist/{useExperimentSelector-DSEckf_g.js → useExperimentSelector-CEcIaueJ.js} +4 -4
- package/dist/{useExperimentSelector-DSEckf_g.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
- package/dist/{useFormBuilder-Bd714C7y.js → useFormBuilder-z5L_dUIy.js} +2 -2
- package/dist/{useFormBuilder-Bd714C7y.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/{useProtocolTemplates-Dq5Pzwyd.js → validation-uwoJGEb6.js} +231 -158
- package/dist/validation-uwoJGEb6.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/apiErrors.test.ts +81 -0
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/DataFrame.test.ts +183 -11
- package/src/__tests__/components/DataStates.test.ts +176 -0
- package/src/__tests__/components/DatePicker.test.ts +76 -0
- package/src/__tests__/components/DateTimePicker.test.ts +36 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
- package/src/__tests__/components/FilePicker.state.test.ts +287 -0
- package/src/__tests__/components/FilePicker.test.ts +487 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PlotlyChart.test.ts +305 -0
- package/src/__tests__/components/RackEditor.test.ts +35 -0
- package/src/__tests__/components/ReagentList.test.ts +64 -2
- package/src/__tests__/components/RetiredStyles.test.ts +31 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
- package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
- package/src/__tests__/components/SegmentedControl.test.ts +107 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
- package/src/__tests__/composables/useApi.test.ts +92 -9
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
- package/src/__tests__/composables/usePluginClient.test.ts +3 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/stores/settings.test.ts +71 -1
- package/src/__tests__/styles/cssTokens.test.ts +131 -0
- package/src/__tests__/utils/permissions.test.ts +68 -0
- package/src/apiErrors.ts +85 -0
- package/src/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +2 -14
- package/src/components/AppLayout.vue +0 -4
- package/src/components/AppPluginSwitcher.story.vue +87 -0
- package/src/components/AppPluginSwitcher.vue +2 -5
- package/src/components/AppSidebar.vue +0 -4
- package/src/components/AppToastContainer.vue +6 -8
- package/src/components/AppTopBar.vue +0 -4
- package/src/components/ArtifactSaveDialog.vue +0 -4
- package/src/components/ArtifactSelectorModal.vue +0 -4
- package/src/components/AuditTrail.story.vue +18 -0
- package/src/components/AuditTrail.vue +27 -7
- package/src/components/AutoGroupModal.vue +0 -4
- package/src/components/Avatar.vue +0 -4
- package/src/components/BaseButton.vue +0 -4
- package/src/components/BaseCheckbox.vue +0 -4
- package/src/components/BaseInput.vue +0 -4
- package/src/components/BaseModal.vue +0 -4
- package/src/components/BasePill.vue +0 -4
- package/src/components/BaseRadioGroup.vue +0 -4
- package/src/components/BaseSelect.vue +0 -4
- package/src/components/BaseSlider.vue +0 -4
- package/src/components/BaseTabs.vue +0 -4
- package/src/components/BaseTextarea.vue +0 -4
- package/src/components/BaseToggle.vue +0 -4
- package/src/components/BatchProgressList.story.vue +18 -0
- package/src/components/BatchProgressList.vue +32 -4
- package/src/components/Breadcrumb.vue +0 -4
- package/src/components/Calendar.vue +0 -4
- package/src/components/ChartContainer.story.vue +1 -1
- package/src/components/ChartContainer.vue +0 -4
- package/src/components/ChemicalFormula.vue +0 -4
- package/src/components/CollapsibleCard.vue +0 -4
- package/src/components/ColorSlider.vue +0 -4
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/DataFrame.story.vue +14 -0
- package/src/components/DataFrame.vue +97 -23
- package/src/components/DatePicker.vue +73 -27
- package/src/components/DateTimePicker.vue +45 -17
- package/src/components/Divider.vue +0 -4
- package/src/components/DoseCalculator.vue +0 -4
- package/src/components/DropdownButton.vue +2 -14
- package/src/components/EmptyState.vue +0 -4
- package/src/components/ExperimentCodeBadge.vue +0 -4
- package/src/components/ExperimentDataViewer.story.vue +3 -0
- package/src/components/ExperimentDataViewer.vue +33 -13
- package/src/components/ExperimentPopover.vue +12 -7
- package/src/components/ExperimentSelectorModal.vue +0 -4
- package/src/components/ExperimentTimeline.story.vue +18 -0
- package/src/components/ExperimentTimeline.vue +81 -75
- package/src/components/FileBrowserModal.story.vue +203 -0
- package/src/components/FileBrowserModal.vue +384 -0
- package/src/components/FilePicker.story.vue +44 -0
- package/src/components/FilePicker.vue +284 -0
- package/src/components/FileUploader.vue +32 -18
- package/src/components/FitPanel.vue +0 -4
- package/src/components/FormActions.story.vue +96 -0
- package/src/components/FormActions.vue +0 -4
- package/src/components/FormBuilder.vue +0 -4
- package/src/components/FormField.vue +0 -4
- package/src/components/FormulaInput.vue +0 -4
- package/src/components/GeneratedPathInput.story.vue +68 -0
- package/src/components/GeneratedPlotlyResult.story.vue +96 -0
- package/src/components/GeneratedPlotlyResult.vue +34 -69
- package/src/components/GeneratedPluginView.story.vue +145 -0
- package/src/components/GeneratedPluginView.vue +10 -10
- package/src/components/GeneratedResultRenderer.story.vue +117 -0
- package/src/components/GeneratedResultRenderer.vue +4 -9
- package/src/components/GroupAssigner.vue +67 -5
- package/src/components/IconButton.vue +0 -4
- package/src/components/InstrumentAlertLog.vue +0 -4
- package/src/components/InstrumentStateBadge.vue +0 -4
- package/src/components/InstrumentStatusCard.vue +0 -4
- package/src/components/JobsStatusTray.vue +0 -4
- package/src/components/LcmsSequenceTable.vue +81 -50
- package/src/components/LoadingSpinner.vue +0 -4
- package/src/components/MobileSupportGate.vue +0 -4
- package/src/components/MoleculeInput.vue +0 -4
- package/src/components/MultiSelect.vue +0 -4
- package/src/components/NumberInput.vue +0 -4
- package/src/components/PlateMapEditor.vue +3 -14
- package/src/components/PlotlyChart.story.vue +61 -0
- package/src/components/PlotlyChart.vue +233 -0
- package/src/components/PluginIcon.vue +0 -4
- package/src/components/ProgressBar.vue +0 -4
- package/src/components/ProtocolStepEditor.vue +2 -4
- package/src/components/RackEditor.vue +51 -70
- package/src/components/ReagentEditor.vue +0 -4
- package/src/components/ReagentList.story.vue +18 -0
- package/src/components/ReagentList.vue +127 -82
- package/src/components/ResourceCard.vue +0 -4
- package/src/components/RuntimeUpdateNotice.story.vue +49 -0
- package/src/components/RuntimeUpdateNotice.vue +4 -4
- package/src/components/SampleHierarchyTree.story.vue +18 -0
- package/src/components/SampleHierarchyTree.vue +32 -9
- package/src/components/SampleLegend.vue +2 -8
- package/src/components/SampleSelector.drag.ts +51 -0
- package/src/components/SampleSelector.story.vue +6 -0
- package/src/components/SampleSelector.tree.ts +2 -0
- package/src/components/SampleSelector.vue +49 -10
- package/src/components/SampleSelectorGroupNode.vue +19 -0
- package/src/components/ScheduleCalendar.story.vue +6 -0
- package/src/components/ScheduleCalendar.vue +82 -7
- package/src/components/ScientificNumber.vue +0 -4
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +50 -7
- package/src/components/SequenceInput.vue +0 -4
- package/src/components/SequenceProgressBar.story.vue +110 -0
- package/src/components/SequenceProgressBar.vue +0 -4
- package/src/components/SettingsModal.vue +0 -4
- package/src/components/Skeleton.vue +0 -4
- package/src/components/SmartGroupFieldRecipe.vue +8 -6
- package/src/components/SmartGroupManual.vue +0 -4
- package/src/components/StatusIndicator.vue +0 -4
- package/src/components/StepWizard.vue +0 -4
- package/src/components/TagsInput.vue +4 -6
- package/src/components/ThemeToggle.vue +0 -4
- package/src/components/TimePicker.vue +36 -7
- package/src/components/TimeRangeInput.vue +0 -4
- package/src/components/Tooltip.story.vue +3 -3
- package/src/components/Tooltip.vue +0 -4
- package/src/components/UnitInput.vue +0 -4
- package/src/components/WellPlate.geometry.ts +60 -1
- package/src/components/WellPlate.interaction.ts +44 -0
- package/src/components/WellPlate.story.vue +8 -0
- package/src/components/WellPlate.vue +53 -7
- package/src/components/fileBrowserPresentation.ts +39 -0
- package/src/components/index.ts +5 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
- package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
- package/src/components/internal/DataStateInternal.vue +74 -0
- package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
- package/src/components/internal/FileBrowserListingInternal.vue +197 -0
- package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
- package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
- package/src/components/internal/FilePickerTreeInternal.vue +238 -0
- package/src/components/internal/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +0 -4
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/filePicker/localFiles.ts +58 -0
- package/src/composables/filePicker/usePickerNavigation.ts +220 -0
- package/src/composables/filePicker/usePickerSelection.ts +259 -0
- package/src/composables/filePicker/validation.ts +41 -0
- package/src/composables/index.ts +17 -0
- package/src/composables/useApi.ts +57 -13
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +14 -2
- package/src/composables/useFileBrowser.ts +314 -0
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFilePicker.ts +366 -0
- package/src/composables/useGeneratedAnalysis.ts +18 -26
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +2 -2
- package/src/composables/useOptimisticMutation.ts +1 -15
- package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
- package/src/composables/usePluginClient.ts +3 -0
- package/src/composables/usePluginConfig.ts +3 -1
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +1 -15
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +5 -29
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/index.ts +3 -0
- package/src/permissions.ts +48 -9
- package/src/stores/experiment.ts +2 -7
- package/src/stores/index.ts +9 -1
- package/src/stores/settings.ts +93 -1
- package/src/styles/components/audit-trail.css +5 -5
- package/src/styles/components/batch-progress-list.css +7 -0
- package/src/styles/components/color-slider.css +2 -2
- package/src/styles/components/data-state.css +30 -0
- package/src/styles/components/dataframe.css +29 -35
- package/src/styles/components/experiment-data-viewer.css +4 -4
- package/src/styles/components/experiment-timeline.css +3 -30
- package/src/styles/components/file-browser-modal.css +489 -0
- package/src/styles/components/file-picker.css +502 -0
- package/src/styles/components/form-builder.css +5 -5
- package/src/styles/components/icon-button.css +9 -0
- package/src/styles/components/lcms-sequence-table.css +16 -1
- package/src/styles/components/multi-select.css +1 -1
- package/src/styles/components/reagent-list.css +38 -31
- package/src/styles/components/sample-hierarchy-tree.css +5 -19
- package/src/styles/components/sample-selector.css +9 -2
- package/src/styles/components/schedule-calendar.css +8 -0
- package/src/styles/components/scientific-number.css +1 -1
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +11 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/doseResponseAdapters.ts +2 -13
- package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
- package/src/templates/plateMapBuilder.ts +2 -2
- package/src/templates/qpcrPlateBuilder.ts +1 -1
- package/src/templates/templateValidators.ts +1 -1
- package/src/templates/timeCourseBuilder.ts +2 -2
- package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
- package/src/types/fileBrowserTypes.ts +85 -0
- package/src/types/filePicker.ts +72 -0
- package/src/types/index.ts +14 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/dist/EmptyState-NBqf2_8z.js.map +0 -1
- package/dist/ExperimentPopover-CfOdh5Rm.js.map +0 -1
- package/dist/ExperimentSelectorModal-DvIagrxY.js.map +0 -1
- package/dist/SettingsModal-W3TYfnP9.js.map +0 -1
- package/dist/components-B80gV0P6.js.map +0 -1
- package/dist/composables/autoGroupConstants.d.ts +0 -1
- package/dist/composables/autoGroupGrouping.d.ts +0 -3
- package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
- package/dist/composables-C8mmD5ta.js.map +0 -1
- package/dist/experiment-Y9Iz7KYi.js.map +0 -1
- package/dist/experiment-utils-DV3DrvE_.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-CK72aKO1.js.map +0 -1
- package/dist/useApi-CDBq6z23.js.map +0 -1
- package/dist/useControlSchema-_OVhavKq.js.map +0 -1
- package/dist/useDropdownState-D5G4UvWd.js.map +0 -1
- package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +0 -1
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
- package/src/composables/autoGroupConstants.ts +0 -4
- package/src/composables/autoGroupGrouping.ts +0 -148
- package/src/composables/useAutoGroupInputSources.ts +0 -147
- package/src/templates/builderDefaults.ts +0 -11
- package/src/templates/plateGeometry.ts +0 -62
- /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
|
@@ -21,6 +21,8 @@ const allCompleted = [
|
|
|
21
21
|
|
|
22
22
|
function initState() {
|
|
23
23
|
return {
|
|
24
|
+
loading: false,
|
|
25
|
+
error: '',
|
|
24
26
|
items: mixedItems,
|
|
25
27
|
showSummary: true,
|
|
26
28
|
title: 'Processing Raw Data Files',
|
|
@@ -35,6 +37,8 @@ function initState() {
|
|
|
35
37
|
<template #default="{ state }">
|
|
36
38
|
<div style="padding: 2rem; max-width: 500px;">
|
|
37
39
|
<BatchProgressList
|
|
40
|
+
:loading="state.loading"
|
|
41
|
+
:error="state.error || null"
|
|
38
42
|
:items="state.items"
|
|
39
43
|
:show-summary="state.showSummary"
|
|
40
44
|
:title="state.title"
|
|
@@ -45,6 +49,8 @@ function initState() {
|
|
|
45
49
|
</div>
|
|
46
50
|
</template>
|
|
47
51
|
<template #controls="{ state }">
|
|
52
|
+
<HstCheckbox v-model="state.loading" title="Loading" />
|
|
53
|
+
<HstText v-model="state.error" title="Error Message" />
|
|
48
54
|
<HstCheckbox v-model="state.showSummary" title="Show Summary" />
|
|
49
55
|
<HstText v-model="state.title" title="Title" />
|
|
50
56
|
<HstCheckbox v-model="state.autoScroll" title="Auto Scroll" />
|
|
@@ -88,5 +94,17 @@ function initState() {
|
|
|
88
94
|
/>
|
|
89
95
|
</div>
|
|
90
96
|
</Variant>
|
|
97
|
+
|
|
98
|
+
<Variant title="Loading">
|
|
99
|
+
<div style="padding: 2rem; max-width: 600px;">
|
|
100
|
+
<BatchProgressList :items="[]" loading />
|
|
101
|
+
</div>
|
|
102
|
+
</Variant>
|
|
103
|
+
|
|
104
|
+
<Variant title="Error">
|
|
105
|
+
<div style="padding: 2rem; max-width: 600px;">
|
|
106
|
+
<BatchProgressList :items="[]" error="Could not load the batch." />
|
|
107
|
+
</div>
|
|
108
|
+
</Variant>
|
|
91
109
|
</Story>
|
|
92
110
|
</template>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { computed, ref, watch, nextTick } from 'vue'
|
|
4
4
|
import type { BatchItem, BatchSummary } from '../types'
|
|
5
5
|
import { useExpansionSet } from '../composables/useExpansionSet'
|
|
6
|
+
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
items: BatchItem[]
|
|
@@ -10,11 +11,23 @@ interface Props {
|
|
|
10
11
|
title?: string
|
|
11
12
|
maxHeight?: string
|
|
12
13
|
autoScroll?: boolean
|
|
14
|
+
/** Replace the item list with a loading placeholder. */
|
|
15
|
+
loading?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* Error message. When set, the item list is replaced by an error state.
|
|
18
|
+
* `null`/`undefined` renders normally.
|
|
19
|
+
*/
|
|
20
|
+
error?: string | null
|
|
21
|
+
/** Empty-state headline shown when there are no batch items. */
|
|
22
|
+
emptyMessage?: string
|
|
13
23
|
}
|
|
14
24
|
|
|
15
25
|
const props = withDefaults(defineProps<Props>(), {
|
|
16
26
|
showSummary: true,
|
|
17
27
|
autoScroll: true,
|
|
28
|
+
loading: false,
|
|
29
|
+
error: null,
|
|
30
|
+
emptyMessage: 'No items in this batch',
|
|
18
31
|
})
|
|
19
32
|
|
|
20
33
|
const emit = defineEmits<{
|
|
@@ -91,7 +104,26 @@ watch(
|
|
|
91
104
|
</div>
|
|
92
105
|
</slot>
|
|
93
106
|
|
|
107
|
+
<div v-if="error" class="mint-batch-progress__state">
|
|
108
|
+
<slot name="error" :error="error">
|
|
109
|
+
<DataStateInternal state="error" size="sm" :title="error" />
|
|
110
|
+
</slot>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div v-else-if="loading" class="mint-batch-progress__state">
|
|
114
|
+
<slot name="loading">
|
|
115
|
+
<DataStateInternal state="loading" size="sm" loading-label="Loading batch" />
|
|
116
|
+
</slot>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div v-else-if="items.length === 0" class="mint-batch-progress__state">
|
|
120
|
+
<slot name="empty">
|
|
121
|
+
<DataStateInternal state="empty" size="sm" :title="emptyMessage" />
|
|
122
|
+
</slot>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
94
125
|
<div
|
|
126
|
+
v-else
|
|
95
127
|
ref="listRef"
|
|
96
128
|
class="mint-batch-progress__list"
|
|
97
129
|
:style="maxHeight ? { maxHeight, overflowY: 'auto' } : {}"
|
|
@@ -175,7 +207,3 @@ watch(
|
|
|
175
207
|
</div>
|
|
176
208
|
</div>
|
|
177
209
|
</template>
|
|
178
|
-
|
|
179
|
-
<style>
|
|
180
|
-
@import '../styles/components/batch-progress-list.css';
|
|
181
|
-
</style>
|
|
@@ -75,7 +75,7 @@ import ChartContainer from './ChartContainer.vue'
|
|
|
75
75
|
<button type="button" style="padding: 0.25rem 0.75rem; font-size: 0.8125rem; border: 1px solid var(--border-color, #e2e8f0); border-radius: 0.375rem; background: var(--bg-card, #fff); color: var(--text-primary, #1e293b); cursor: pointer;">
|
|
76
76
|
Linear
|
|
77
77
|
</button>
|
|
78
|
-
<button type="button" style="padding: 0.25rem 0.75rem; font-size: 0.8125rem; border: 1px solid var(--
|
|
78
|
+
<button type="button" style="padding: 0.25rem 0.75rem; font-size: 0.8125rem; border: 1px solid var(--color-primary); border-radius: 0.375rem; background: var(--color-primary); color: #fff; cursor: pointer;">
|
|
79
79
|
Log
|
|
80
80
|
</button>
|
|
81
81
|
</div>
|
|
@@ -37,6 +37,7 @@ const pagination = ref<PaginationState>({ page: 1, pageSize: 5, total: data.leng
|
|
|
37
37
|
<template #default="{ state }">
|
|
38
38
|
<div style="padding: 2rem; max-width: 900px; margin: 0 auto;">
|
|
39
39
|
<DataFrame
|
|
40
|
+
:error="state.error || null"
|
|
40
41
|
:data="data"
|
|
41
42
|
:columns="columns"
|
|
42
43
|
row-key="id"
|
|
@@ -55,6 +56,8 @@ const pagination = ref<PaginationState>({ page: 1, pageSize: 5, total: data.leng
|
|
|
55
56
|
</template>
|
|
56
57
|
|
|
57
58
|
<template #controls="{ state }">
|
|
59
|
+
|
|
60
|
+
<HstText v-model="state.error" title="Error Message" />
|
|
58
61
|
<HstSelect
|
|
59
62
|
v-model="state.size"
|
|
60
63
|
title="Size"
|
|
@@ -144,5 +147,16 @@ const pagination = ref<PaginationState>({ page: 1, pageSize: 5, total: data.leng
|
|
|
144
147
|
/>
|
|
145
148
|
</div>
|
|
146
149
|
</Variant>
|
|
150
|
+
|
|
151
|
+
<Variant title="Error">
|
|
152
|
+
<div style="padding: 2rem; max-width: 900px; margin: 0 auto;">
|
|
153
|
+
<DataFrame
|
|
154
|
+
:data="data"
|
|
155
|
+
:columns="columns"
|
|
156
|
+
row-key="id"
|
|
157
|
+
error="Could not load the result table."
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
</Variant>
|
|
147
161
|
</Story>
|
|
148
162
|
</template>
|
|
@@ -5,6 +5,14 @@ import type { DataFrameColumn, SortState, PaginationState } from '../types'
|
|
|
5
5
|
import { useSortedItems } from '../composables/useSortedItems'
|
|
6
6
|
import { useTextSearch } from '../composables/useTextSearch'
|
|
7
7
|
import { useListSelection } from '../composables/useListSelection'
|
|
8
|
+
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
9
|
+
import LoadingSpinner from './LoadingSpinner.vue'
|
|
10
|
+
import { tableDensityToSize, tryUseSettingsStore } from '../stores/settings'
|
|
11
|
+
|
|
12
|
+
/** Inbox glyph kept from the pre-1.2 hand-rolled empty state. */
|
|
13
|
+
const EMPTY_ICON_PATH
|
|
14
|
+
= 'M22 12L16 12L14 15L10 15L8 12L2 12'
|
|
15
|
+
+ 'M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* DataFrame - Data table with sorting, searching, pagination, and row selection.
|
|
@@ -34,7 +42,11 @@ interface Props {
|
|
|
34
42
|
striped?: boolean
|
|
35
43
|
/** Show table borders */
|
|
36
44
|
bordered?: boolean
|
|
37
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Table size. Left unset, it follows the platform's `tableDensity` setting
|
|
47
|
+
* (compact → `sm`, normal → `md`, comfortable → `lg`) and falls back to
|
|
48
|
+
* `md` when no settings store is installed.
|
|
49
|
+
*/
|
|
38
50
|
size?: 'sm' | 'md' | 'lg'
|
|
39
51
|
/** Make header sticky on scroll */
|
|
40
52
|
stickyHeader?: boolean
|
|
@@ -42,6 +54,11 @@ interface Props {
|
|
|
42
54
|
maxHeight?: string | number
|
|
43
55
|
/** Show loading overlay */
|
|
44
56
|
loading?: boolean
|
|
57
|
+
/**
|
|
58
|
+
* Error message. When set, the table body is replaced by an error state
|
|
59
|
+
* instead of the empty state. `null`/`undefined` renders normally.
|
|
60
|
+
*/
|
|
61
|
+
error?: string | null
|
|
45
62
|
/** Empty state message */
|
|
46
63
|
emptyText?: string
|
|
47
64
|
/** Enable column sorting */
|
|
@@ -75,9 +92,9 @@ interface Props {
|
|
|
75
92
|
const props = withDefaults(defineProps<Props>(), {
|
|
76
93
|
striped: true,
|
|
77
94
|
bordered: true,
|
|
78
|
-
size: 'md',
|
|
79
95
|
stickyHeader: false,
|
|
80
96
|
loading: false,
|
|
97
|
+
error: null,
|
|
81
98
|
emptyText: 'No data',
|
|
82
99
|
sortable: false,
|
|
83
100
|
pagination: false,
|
|
@@ -108,6 +125,13 @@ const emit = defineEmits<{
|
|
|
108
125
|
'delete-row': [row: Record<string, unknown>, index: number]
|
|
109
126
|
}>()
|
|
110
127
|
|
|
128
|
+
const settings = tryUseSettingsStore()
|
|
129
|
+
|
|
130
|
+
/** Explicit `size` wins; otherwise the user's table density, else `md`. */
|
|
131
|
+
const size = computed<'sm' | 'md' | 'lg'>(() =>
|
|
132
|
+
props.size ?? (settings ? tableDensityToSize(settings.tableDensity) : 'md'),
|
|
133
|
+
)
|
|
134
|
+
|
|
111
135
|
const searchQuery = ref('')
|
|
112
136
|
const internalSort = ref<SortState | null>(null)
|
|
113
137
|
|
|
@@ -219,6 +243,21 @@ function isColumnSortable(col: DataFrameColumn): boolean {
|
|
|
219
243
|
return col.sortable === true || (props.sortable && col.sortable !== false)
|
|
220
244
|
}
|
|
221
245
|
|
|
246
|
+
/** `aria-sort` for the header cell, omitted on columns that cannot be sorted. */
|
|
247
|
+
function columnAriaSort(col: DataFrameColumn): 'ascending' | 'descending' | 'none' | undefined {
|
|
248
|
+
if (!isColumnSortable(col)) return undefined
|
|
249
|
+
if (activeSort.value?.key !== col.key || !activeSort.value.direction) return 'none'
|
|
250
|
+
return activeSort.value.direction === 'asc' ? 'ascending' : 'descending'
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Native buttons activate on Enter/Space, but the default is prevented here so
|
|
254
|
+
// the header sorts exactly once per key press in every engine.
|
|
255
|
+
function handleSortKeydown(event: KeyboardEvent, col: DataFrameColumn) {
|
|
256
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
257
|
+
event.preventDefault()
|
|
258
|
+
handleSort(col)
|
|
259
|
+
}
|
|
260
|
+
|
|
222
261
|
// Selection
|
|
223
262
|
const pageRowKeys = computed(() =>
|
|
224
263
|
paginatedData.value.map((row, i) => getRowKey(row, i))
|
|
@@ -264,6 +303,35 @@ function columnStyle(col: DataFrameColumn) {
|
|
|
264
303
|
}
|
|
265
304
|
}
|
|
266
305
|
|
|
306
|
+
const RESIZE_KEY_STEP = 16
|
|
307
|
+
|
|
308
|
+
function columnMinWidth(col: DataFrameColumn): number {
|
|
309
|
+
return Math.max(MIN_COL_WIDTH, typeof col.minWidth === 'number' ? col.minWidth : 0)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Current px width of a column when one is known (resized, or a numeric `width`). */
|
|
313
|
+
function columnWidthValue(col: DataFrameColumn): number | undefined {
|
|
314
|
+
const width = activeWidths.value[col.key] ?? col.width
|
|
315
|
+
return typeof width === 'number' ? width : undefined
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function applyColumnWidth(key: string, width: number) {
|
|
319
|
+
const widths = { ...activeWidths.value, [key]: width }
|
|
320
|
+
if (props.columnWidths === undefined) internalWidths.value = widths
|
|
321
|
+
emit('update:columnWidths', widths)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Left/Right resize the column by a fixed step; the handle is a `role="slider"`. */
|
|
325
|
+
function handleResizeKeydown(event: KeyboardEvent, col: DataFrameColumn) {
|
|
326
|
+
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return
|
|
327
|
+
event.preventDefault()
|
|
328
|
+
const th = (event.target as HTMLElement).closest('th')
|
|
329
|
+
const base =
|
|
330
|
+
columnWidthValue(col) ?? (th instanceof HTMLElement ? th.offsetWidth : MIN_COL_WIDTH)
|
|
331
|
+
const delta = event.key === 'ArrowRight' ? RESIZE_KEY_STEP : -RESIZE_KEY_STEP
|
|
332
|
+
applyColumnWidth(col.key, Math.max(columnMinWidth(col), base + delta))
|
|
333
|
+
}
|
|
334
|
+
|
|
267
335
|
let resizeState: { key: string; startX: number; startWidth: number; min: number } | null = null
|
|
268
336
|
|
|
269
337
|
function startResize(col: DataFrameColumn, event: MouseEvent) {
|
|
@@ -272,8 +340,7 @@ function startResize(col: DataFrameColumn, event: MouseEvent) {
|
|
|
272
340
|
const th = (event.target as HTMLElement).closest('th')
|
|
273
341
|
const startWidth =
|
|
274
342
|
th instanceof HTMLElement ? th.offsetWidth : (typeof col.width === 'number' ? col.width : MIN_COL_WIDTH)
|
|
275
|
-
|
|
276
|
-
resizeState = { key: col.key, startX: event.clientX, startWidth, min }
|
|
343
|
+
resizeState = { key: col.key, startX: event.clientX, startWidth, min: columnMinWidth(col) }
|
|
277
344
|
window.addEventListener('mousemove', onResizeMove)
|
|
278
345
|
window.addEventListener('mouseup', stopResize)
|
|
279
346
|
}
|
|
@@ -281,9 +348,7 @@ function startResize(col: DataFrameColumn, event: MouseEvent) {
|
|
|
281
348
|
function onResizeMove(event: MouseEvent) {
|
|
282
349
|
if (!resizeState) return
|
|
283
350
|
const next = Math.max(resizeState.min, resizeState.startWidth + (event.clientX - resizeState.startX))
|
|
284
|
-
|
|
285
|
-
if (props.columnWidths === undefined) internalWidths.value = widths
|
|
286
|
-
emit('update:columnWidths', widths)
|
|
351
|
+
applyColumnWidth(resizeState.key, next)
|
|
287
352
|
}
|
|
288
353
|
|
|
289
354
|
function stopResize() {
|
|
@@ -355,8 +420,15 @@ const wrapperStyle = computed(() => {
|
|
|
355
420
|
},
|
|
356
421
|
]"
|
|
357
422
|
:style="columnStyle(col)"
|
|
358
|
-
|
|
423
|
+
:aria-sort="columnAriaSort(col)"
|
|
359
424
|
>
|
|
425
|
+
<component
|
|
426
|
+
:is="isColumnSortable(col) ? 'button' : 'div'"
|
|
427
|
+
:type="isColumnSortable(col) ? 'button' : undefined"
|
|
428
|
+
class="mint-dataframe__th-button"
|
|
429
|
+
@click="isColumnSortable(col) ? handleSort(col) : undefined"
|
|
430
|
+
@keydown="isColumnSortable(col) ? handleSortKeydown($event, col) : undefined"
|
|
431
|
+
>
|
|
360
432
|
<slot :name="`header-${col.key}`" :column="col">
|
|
361
433
|
<div class="mint-dataframe__th-content">
|
|
362
434
|
<span>{{ col.label }}</span>
|
|
@@ -380,13 +452,18 @@ const wrapperStyle = computed(() => {
|
|
|
380
452
|
</svg>
|
|
381
453
|
</div>
|
|
382
454
|
</slot>
|
|
455
|
+
</component>
|
|
383
456
|
<span
|
|
384
457
|
v-if="resizable"
|
|
385
458
|
class="mint-dataframe__resize-handle"
|
|
386
|
-
role="
|
|
459
|
+
role="slider"
|
|
460
|
+
tabindex="0"
|
|
387
461
|
aria-orientation="vertical"
|
|
388
|
-
aria-label="Resize column"
|
|
462
|
+
:aria-label="`Resize ${col.label} column`"
|
|
463
|
+
:aria-valuemin="columnMinWidth(col)"
|
|
464
|
+
:aria-valuenow="columnWidthValue(col)"
|
|
389
465
|
@mousedown="startResize(col, $event)"
|
|
466
|
+
@keydown="handleResizeKeydown($event, col)"
|
|
390
467
|
@click.stop
|
|
391
468
|
/>
|
|
392
469
|
</th>
|
|
@@ -459,23 +536,24 @@ const wrapperStyle = computed(() => {
|
|
|
459
536
|
</tbody>
|
|
460
537
|
</table>
|
|
461
538
|
|
|
539
|
+
<!-- Error state -->
|
|
540
|
+
<div v-if="error" class="mint-dataframe__state">
|
|
541
|
+
<slot name="error" :error="error">
|
|
542
|
+
<DataStateInternal state="error" :title="error" />
|
|
543
|
+
</slot>
|
|
544
|
+
</div>
|
|
545
|
+
|
|
462
546
|
<!-- Empty state -->
|
|
463
|
-
<div v-if="paginatedData.length === 0 && !loading" class="mint-
|
|
547
|
+
<div v-else-if="paginatedData.length === 0 && !loading" class="mint-dataframe__state">
|
|
464
548
|
<slot name="empty">
|
|
465
|
-
<
|
|
466
|
-
<polyline points="22 12 16 12 14 15 10 15 8 12 2 12" /><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
|
|
467
|
-
</svg>
|
|
468
|
-
<p class="mint-dataframe__empty-text">{{ emptyText }}</p>
|
|
549
|
+
<DataStateInternal state="empty" :title="emptyText" :icon-path="EMPTY_ICON_PATH" />
|
|
469
550
|
</slot>
|
|
470
551
|
</div>
|
|
471
552
|
|
|
472
553
|
<!-- Loading overlay -->
|
|
473
554
|
<div v-if="loading" class="mint-dataframe__loading">
|
|
474
555
|
<slot name="loading">
|
|
475
|
-
<
|
|
476
|
-
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" opacity="0.25" />
|
|
477
|
-
<path d="M12 2a10 10 0 0 1 10 10" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
|
478
|
-
</svg>
|
|
556
|
+
<LoadingSpinner size="lg" />
|
|
479
557
|
</slot>
|
|
480
558
|
</div>
|
|
481
559
|
</div>
|
|
@@ -514,7 +592,3 @@ const wrapperStyle = computed(() => {
|
|
|
514
592
|
</div>
|
|
515
593
|
</div>
|
|
516
594
|
</template>
|
|
517
|
-
|
|
518
|
-
<style>
|
|
519
|
-
@import '../styles/components/dataframe.css';
|
|
520
|
-
</style>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Input that opens a calendar dropdown for selecting a single date, with clearable support. */
|
|
3
|
-
import {
|
|
3
|
+
import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom'
|
|
4
|
+
import { ref, computed, nextTick, onBeforeUnmount, watch } from 'vue'
|
|
4
5
|
import { useCalendarGrid, type CalendarGridDay } from '../composables/useCalendarGrid'
|
|
5
6
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
6
|
-
import {
|
|
7
|
+
import { useFocusTrap } from '../composables/useFocusTrap'
|
|
7
8
|
import CalendarGridPanelInternal from './internal/CalendarGridPanelInternal.vue'
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
@@ -28,12 +29,14 @@ const emit = defineEmits<{
|
|
|
28
29
|
'update:modelValue': [value: string | undefined]
|
|
29
30
|
}>()
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
const dropdownRef = ref<HTMLDivElement | null>(null)
|
|
32
|
+
const dropdownRef = ref<HTMLElement | null>(null)
|
|
33
33
|
const dropdownStyle = ref<Record<string, string>>({})
|
|
34
|
-
const { isOpen, rootRef: containerRef, close, toggle } = useDropdownState({
|
|
34
|
+
const { isOpen, rootRef: containerRef, triggerRef, open, close, toggle } = useDropdownState({
|
|
35
35
|
insideRefs: [dropdownRef],
|
|
36
36
|
})
|
|
37
|
+
// The panel is a non-modal dialog: Tab cycles inside it while it is open, and
|
|
38
|
+
// focus returns to the input when it closes.
|
|
39
|
+
useFocusTrap({ containerRef: dropdownRef, active: isOpen })
|
|
37
40
|
const {
|
|
38
41
|
weekDays,
|
|
39
42
|
monthYear,
|
|
@@ -88,41 +91,88 @@ function toggleCalendar() {
|
|
|
88
91
|
toggle()
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
function
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
+
function handleTriggerKeydown(event: KeyboardEvent) {
|
|
95
|
+
if (props.disabled || isOpen.value) return
|
|
96
|
+
if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
|
|
97
|
+
event.preventDefault()
|
|
98
|
+
open()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// floating-ui keeps the panel anchored (flip/shift) while the page scrolls,
|
|
103
|
+
// instead of the panel closing on every scroll or resize.
|
|
104
|
+
let stopAutoUpdate: (() => void) | null = null
|
|
105
|
+
|
|
106
|
+
async function updateDropdownPosition() {
|
|
107
|
+
const trigger = triggerRef.value
|
|
108
|
+
const panel = dropdownRef.value
|
|
109
|
+
if (!trigger || !panel) return
|
|
110
|
+
|
|
111
|
+
const { x, y } = await computePosition(trigger, panel, {
|
|
112
|
+
placement: 'bottom-start',
|
|
113
|
+
strategy: 'fixed',
|
|
114
|
+
middleware: [offset(4), flip(), shift({ padding: 8 })],
|
|
115
|
+
})
|
|
94
116
|
dropdownStyle.value = {
|
|
95
117
|
position: 'fixed',
|
|
96
|
-
top: `${
|
|
97
|
-
left: `${
|
|
98
|
-
width: `${Math.max(
|
|
118
|
+
top: `${y}px`,
|
|
119
|
+
left: `${x}px`,
|
|
120
|
+
width: `${Math.max(trigger.getBoundingClientRect().width, 288)}px`,
|
|
99
121
|
zIndex: '9999',
|
|
100
122
|
}
|
|
101
123
|
}
|
|
102
124
|
|
|
103
|
-
function
|
|
104
|
-
|
|
125
|
+
function stopPositioning() {
|
|
126
|
+
stopAutoUpdate?.()
|
|
127
|
+
stopAutoUpdate = null
|
|
105
128
|
}
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
130
|
+
function startPositioning() {
|
|
131
|
+
const trigger = triggerRef.value
|
|
132
|
+
const panel = dropdownRef.value
|
|
133
|
+
if (!trigger || !panel) return
|
|
134
|
+
stopPositioning()
|
|
135
|
+
stopAutoUpdate = autoUpdate(trigger, panel, () => void updateDropdownPosition())
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Moves focus into the calendar grid: selected day, else today, else the first day. */
|
|
139
|
+
function focusCalendar() {
|
|
140
|
+
const panel = dropdownRef.value
|
|
141
|
+
if (!panel) return
|
|
142
|
+
const target =
|
|
143
|
+
panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??
|
|
144
|
+
panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??
|
|
145
|
+
panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')
|
|
146
|
+
target?.focus()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
watch(isOpen, async (opened) => {
|
|
150
|
+
if (!opened) {
|
|
151
|
+
stopPositioning()
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
if (selectedDate.value) {
|
|
155
|
+
setCurrentMonthFromDate(selectedDate.value)
|
|
113
156
|
}
|
|
157
|
+
await nextTick()
|
|
158
|
+
startPositioning()
|
|
159
|
+
// A second tick so this focus lands after the focus trap's initial focus.
|
|
160
|
+
await nextTick()
|
|
161
|
+
focusCalendar()
|
|
114
162
|
})
|
|
115
163
|
|
|
116
|
-
|
|
117
|
-
useEventListener(() => window, 'resize', handleScrollOrResize)
|
|
164
|
+
onBeforeUnmount(stopPositioning)
|
|
118
165
|
</script>
|
|
119
166
|
|
|
120
167
|
<template>
|
|
121
168
|
<div ref="containerRef" class="mint-date-picker">
|
|
122
|
-
<div
|
|
169
|
+
<div class="mint-date-picker__input-wrapper">
|
|
123
170
|
<input
|
|
171
|
+
ref="triggerRef"
|
|
124
172
|
type="text"
|
|
125
173
|
readonly
|
|
174
|
+
aria-haspopup="dialog"
|
|
175
|
+
:aria-expanded="isOpen"
|
|
126
176
|
:value="displayValue"
|
|
127
177
|
:placeholder="placeholder || 'Select date'"
|
|
128
178
|
:disabled="disabled"
|
|
@@ -133,6 +183,7 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
|
|
|
133
183
|
disabled ? 'mint-date-picker__input--disabled' : '',
|
|
134
184
|
]"
|
|
135
185
|
@click="toggleCalendar"
|
|
186
|
+
@keydown="handleTriggerKeydown"
|
|
136
187
|
/>
|
|
137
188
|
<div class="mint-date-picker__icon">
|
|
138
189
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -156,7 +207,6 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
|
|
|
156
207
|
class="mint-date-picker__dropdown"
|
|
157
208
|
:style="dropdownStyle"
|
|
158
209
|
role="dialog"
|
|
159
|
-
aria-modal="true"
|
|
160
210
|
aria-label="Date picker"
|
|
161
211
|
>
|
|
162
212
|
<CalendarGridPanelInternal
|
|
@@ -191,7 +241,3 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
|
|
|
191
241
|
</Teleport>
|
|
192
242
|
</div>
|
|
193
243
|
</template>
|
|
194
|
-
|
|
195
|
-
<style>
|
|
196
|
-
@import '../styles/components/date-picker.css';
|
|
197
|
-
</style>
|