@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.0-beta.3
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 +218 -6
- package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
- package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
- package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
- package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
- package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
- package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
- package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
- package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
- package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
- package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
- package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
- package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
- package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
- package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
- package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
- package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
- package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
- package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
- package/dist/__tests__/components/FormCompatibility.test.d.ts +1 -0
- package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
- package/dist/__tests__/components/GroupingModal.test.d.ts +1 -0
- package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
- package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
- package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsButton.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
- package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
- package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
- package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
- package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
- package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
- package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
- package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
- package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
- package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
- package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
- package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
- package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
- package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
- package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
- package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
- package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
- package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
- package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
- package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
- package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
- package/dist/__tests__/templates/templates.test.d.ts +1 -0
- package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
- package/dist/auth-QQj2kkze.js.map +1 -0
- package/dist/components/ActionItem.vue.d.ts +32 -0
- package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
- package/dist/components/AppPageSelector.vue.d.ts +3 -6
- package/dist/components/AppPillNav.vue.d.ts +2 -2
- package/dist/components/AppSidebar.vue.d.ts +56 -3
- package/dist/components/AppToastContainer.vue.d.ts +2 -0
- package/dist/components/AppTopBar.vue.d.ts +41 -10
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseInput.vue.d.ts +2 -2
- package/dist/components/BasePill.vue.d.ts +2 -2
- package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
- package/dist/components/BaseSelect.vue.d.ts +3 -3
- package/dist/components/BaseTabs.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +117 -0
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +92 -0
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +82 -0
- package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
- package/dist/components/Breadcrumb.vue.d.ts +2 -2
- package/dist/components/Calendar.vue.d.ts +1 -1
- package/dist/components/CalendarGridPanel.vue.d.ts +25 -0
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/ConfirmDialog.vue.d.ts +2 -2
- package/dist/components/ControlWorkspaceView.vue.d.ts +130 -0
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +3 -3
- package/dist/components/Divider.vue.d.ts +1 -1
- package/dist/components/DropdownButton.vue.d.ts +3 -3
- package/dist/components/EmptyState.vue.d.ts +1 -2
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
- package/dist/components/FileUploader.vue.d.ts +1 -1
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +4 -4
- package/dist/components/FormBuilder.vue.d.ts +22 -8
- package/dist/components/FormFieldRenderer.vue.d.ts +7 -10
- package/dist/components/FormSection.vue.d.ts +11 -24
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +3 -3
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
- package/dist/components/RackEditor.vue.d.ts +2 -2
- package/dist/components/SampleLegend.vue.d.ts +2 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
- package/dist/components/SegmentedControl.vue.d.ts +2 -2
- package/dist/components/SequenceInput.vue.d.ts +3 -3
- package/dist/components/SettingsButton.vue.d.ts +2 -2
- package/dist/components/SettingsModal.vue.d.ts +13 -5
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +3 -2
- package/dist/components/TimePicker.vue.d.ts +3 -3
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.vue.d.ts +8 -8
- package/dist/components/index.d.ts +11 -1
- package/dist/components/index.js +3 -3
- package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +31 -0
- package/dist/components/internal/FormSectionRenderer.vue.d.ts +43 -0
- package/dist/{components-_XqPEhP9.js → components-D_Sr0adg.js} +7290 -6518
- package/dist/components-D_Sr0adg.js.map +1 -0
- package/dist/composables/index.d.ts +21 -2
- package/dist/composables/index.js +4 -3
- package/dist/composables/platformContextHelpers.d.ts +14 -0
- package/dist/composables/useBioTemplateComponents.d.ts +20 -0
- package/dist/composables/useBioTemplateControls.d.ts +6 -0
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +45 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +74 -0
- package/dist/composables/useBioTemplateWorkspace.d.ts +50 -0
- package/dist/composables/useCalendarGrid.d.ts +26 -0
- package/dist/composables/useControlSchema.d.ts +321 -0
- package/dist/composables/useDebouncedWatch.d.ts +20 -0
- package/dist/composables/useDropdownState.d.ts +19 -0
- package/dist/composables/useEventListener.d.ts +13 -0
- package/dist/composables/useExpansionSet.d.ts +21 -0
- package/dist/composables/useExperimentData.d.ts +10 -0
- package/dist/composables/useExperimentSave.d.ts +31 -2
- package/dist/composables/useExperimentSelector.d.ts +20 -0
- package/dist/composables/useForm.d.ts +2 -0
- package/dist/composables/useGroupAssignment.d.ts +31 -0
- package/dist/composables/useListSelection.d.ts +35 -0
- package/dist/composables/usePlatformContext.d.ts +21 -3
- package/dist/composables/usePluginApi.d.ts +7 -14
- package/dist/composables/usePluginClient.d.ts +109 -0
- package/dist/composables/usePluginConfig.d.ts +12 -0
- package/dist/composables/useRequestSyncState.d.ts +34 -0
- package/dist/composables/useSampleGroups.d.ts +32 -0
- package/dist/composables/useSelectionLimit.d.ts +17 -0
- package/dist/composables/useSortedItems.d.ts +32 -0
- package/dist/composables/useTemplateCollection.d.ts +58 -0
- package/dist/composables/useTextSearch.d.ts +18 -0
- package/dist/composables/useTimeUtils.d.ts +8 -0
- package/dist/{composables-tiZqLu1M.js → composables-C3dpXQN5.js} +228 -146
- package/dist/composables-C3dpXQN5.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +6 -5
- package/dist/install.d.ts +7 -2
- package/dist/install.js +2 -2
- package/dist/install.js.map +1 -1
- package/dist/stores/index.js +1 -1
- package/dist/stores/settings.d.ts +4 -1
- package/dist/styles.css +5235 -5977
- package/dist/templates/adapters.d.ts +43 -0
- package/dist/templates/builders.d.ts +63 -0
- package/dist/templates/catalog.d.ts +188 -0
- package/dist/templates/componentBindings.d.ts +58 -0
- package/dist/templates/controlSchemas.d.ts +25 -0
- package/dist/templates/index.d.ts +15 -0
- package/dist/templates/index.js +2 -0
- package/dist/templates/lookup.d.ts +4 -0
- package/dist/templates/packs.d.ts +18 -0
- package/dist/templates/presets.d.ts +90 -0
- package/dist/templates/types.d.ts +531 -0
- package/dist/templates-50NPjaxL.js +9333 -0
- package/dist/templates-50NPjaxL.js.map +1 -0
- package/dist/types/components.d.ts +26 -4
- package/dist/types/form-builder.d.ts +6 -8
- package/dist/types/index.d.ts +2 -2
- package/dist/types/platform.d.ts +7 -1
- package/dist/useScheduleDrag-D4oWdh41.js +4371 -0
- package/dist/useScheduleDrag-D4oWdh41.js.map +1 -0
- package/dist/utils/formModelSync.d.ts +5 -0
- package/dist/utils/items.d.ts +8 -0
- package/dist/utils/options.d.ts +6 -0
- package/dist/utils/pluginIcon.d.ts +9 -0
- package/package.json +7 -2
- package/src/__tests__/components/ActionItem.test.ts +99 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
- package/src/__tests__/components/AppPageSelector.test.ts +134 -0
- package/src/__tests__/components/AppPillNav.test.ts +78 -0
- package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
- package/src/__tests__/components/AppSidebar.test.ts +370 -0
- package/src/__tests__/components/AppToastContainer.test.ts +48 -0
- package/src/__tests__/components/AppTopBar.test.ts +383 -0
- package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
- package/src/__tests__/components/BaseSelect.test.ts +21 -0
- package/src/__tests__/components/BaseTabs.test.ts +25 -0
- package/src/__tests__/components/BatchProgressList.test.ts +52 -0
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +153 -0
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +161 -0
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +281 -0
- package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
- package/src/__tests__/components/Breadcrumb.test.ts +23 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
- package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
- package/src/__tests__/components/ControlWorkspaceView.test.ts +1031 -0
- package/src/__tests__/components/DataFrame.test.ts +11 -0
- package/src/__tests__/components/DatePicker.test.ts +45 -0
- package/src/__tests__/components/DateTimePicker.test.ts +48 -0
- package/src/__tests__/components/DropdownButton.test.ts +23 -0
- package/src/__tests__/components/EmptyState.test.ts +23 -0
- package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
- package/src/__tests__/components/FormBuilder.test.ts +296 -0
- package/src/__tests__/components/FormCompatibility.test.ts +94 -0
- package/src/__tests__/components/GroupAssigner.test.ts +30 -0
- package/src/__tests__/components/GroupingModal.test.ts +73 -0
- package/src/__tests__/components/MultiSelect.test.ts +48 -0
- package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
- package/src/__tests__/components/ReagentList.test.ts +82 -0
- package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
- package/src/__tests__/components/SampleSelector.test.ts +60 -0
- package/src/__tests__/components/SegmentedControl.test.ts +24 -0
- package/src/__tests__/components/SettingsButton.test.ts +44 -0
- package/src/__tests__/components/SettingsModal.test.ts +296 -0
- package/src/__tests__/components/TagsInput.test.ts +75 -0
- package/src/__tests__/components/ThemeToggle.test.ts +47 -0
- package/src/__tests__/components/TimePicker.test.ts +38 -0
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +122 -0
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +99 -0
- package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
- package/src/__tests__/composables/useControlSchema.test.ts +919 -0
- package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
- package/src/__tests__/composables/useDropdownState.test.ts +95 -0
- package/src/__tests__/composables/useEventListener.test.ts +116 -0
- package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
- package/src/__tests__/composables/useExperimentData.test.ts +4 -0
- package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
- package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
- package/src/__tests__/composables/useForm.test.ts +58 -0
- package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
- package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
- package/src/__tests__/composables/useListSelection.test.ts +66 -0
- package/src/__tests__/composables/usePluginClient.test.ts +444 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
- package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
- package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
- package/src/__tests__/composables/useSortedItems.test.ts +87 -0
- package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
- package/src/__tests__/composables/useTextSearch.test.ts +55 -0
- package/src/__tests__/composables/useTheme.test.ts +91 -0
- package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +229 -0
- package/src/__tests__/fixtures/templates/dose-response.json +81 -0
- package/src/__tests__/fixtures/templates/plate-map.json +54 -0
- package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
- package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
- package/src/__tests__/templates/templates.test.ts +1043 -0
- package/src/components/ActionItem.vue +82 -0
- package/src/components/AppAvatarMenu.vue +15 -69
- package/src/components/AppLayout.story.vue +25 -25
- package/src/components/AppPageSelector.vue +63 -94
- package/src/components/AppPillNav.vue +44 -39
- package/src/components/AppPluginSwitcher.vue +41 -145
- package/src/components/AppSidebar.story.vue +94 -0
- package/src/components/AppSidebar.vue +187 -12
- package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
- package/src/components/AppToastContainer.vue +62 -0
- package/src/components/AppTopBar.story.vue +7 -30
- package/src/components/AppTopBar.vue +251 -57
- package/src/components/BaseModal.vue +3 -5
- package/src/components/BaseRadioGroup.vue +7 -3
- package/src/components/BaseSelect.vue +11 -7
- package/src/components/BaseTabs.vue +6 -4
- package/src/components/BatchProgressList.vue +5 -8
- package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
- package/src/components/BioTemplateExperimentWorkspaceView.vue +337 -0
- package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
- package/src/components/BioTemplatePackWorkspaceView.vue +176 -0
- package/src/components/BioTemplatePresetWorkspaceView.story.vue +151 -0
- package/src/components/BioTemplatePresetWorkspaceView.vue +392 -0
- package/src/components/BioTemplateRenderer.story.vue +57 -0
- package/src/components/BioTemplateRenderer.vue +269 -0
- package/src/components/Breadcrumb.vue +14 -8
- package/src/components/CalendarGridPanel.vue +120 -0
- package/src/components/ConcentrationInput.vue +27 -64
- package/src/components/ControlWorkspaceView.story.vue +336 -0
- package/src/components/ControlWorkspaceView.vue +347 -0
- package/src/components/DataFrame.vue +34 -50
- package/src/components/DatePicker.vue +59 -192
- package/src/components/DateTimePicker.vue +50 -171
- package/src/components/DropdownButton.vue +14 -32
- package/src/components/EmptyState.vue +4 -2
- package/src/components/ExperimentPopover.vue +5 -22
- package/src/components/FormBuilder.vue +124 -27
- package/src/components/FormFieldRenderer.vue +15 -38
- package/src/components/FormSection.vue +20 -73
- package/src/components/GroupAssigner.vue +24 -56
- package/src/components/GroupingModal.story.vue +3 -3
- package/src/components/GroupingModal.vue +30 -391
- package/src/components/MultiSelect.vue +17 -12
- package/src/components/PlateMapEditor.vue +3 -8
- package/src/components/PluginIcon.vue +2 -22
- package/src/components/ProtocolStepEditor.vue +13 -22
- package/src/components/ReagentList.vue +25 -33
- package/src/components/SampleHierarchyTree.vue +12 -23
- package/src/components/SampleSelector.vue +42 -122
- package/src/components/SegmentedControl.vue +7 -3
- package/src/components/SettingsButton.story.vue +1 -1
- package/src/components/SettingsButton.vue +15 -27
- package/src/components/SettingsModal.story.vue +1 -1
- package/src/components/SettingsModal.vue +120 -29
- package/src/components/TagsInput.vue +29 -14
- package/src/components/ThemeToggle.vue +9 -7
- package/src/components/TimePicker.vue +19 -41
- package/src/components/ToastNotification.vue +4 -57
- package/src/components/Tooltip.vue +7 -12
- package/src/components/WellEditPopup.vue +3 -8
- package/src/components/WellPlate.vue +4 -10
- package/src/components/index.ts +11 -1
- package/src/components/internal/FormFieldRendererInternal.vue +50 -0
- package/src/components/internal/FormSectionRenderer.vue +78 -0
- package/src/composables/index.ts +212 -0
- package/src/composables/platformContextHelpers.ts +74 -0
- package/src/composables/useBioTemplateComponents.ts +93 -0
- package/src/composables/useBioTemplateControls.ts +41 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +181 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +337 -0
- package/src/composables/useBioTemplateWorkspace.ts +139 -0
- package/src/composables/useCalendarGrid.ts +140 -0
- package/src/composables/useControlSchema.ts +1274 -0
- package/src/composables/useDebouncedWatch.ts +119 -0
- package/src/composables/useDropdownState.ts +83 -0
- package/src/composables/useEventListener.ts +111 -0
- package/src/composables/useExpansionSet.ts +117 -0
- package/src/composables/useExperimentData.ts +20 -11
- package/src/composables/useExperimentSave.ts +202 -50
- package/src/composables/useExperimentSelector.ts +86 -72
- package/src/composables/useForm.ts +49 -4
- package/src/composables/useFormBuilder.ts +93 -42
- package/src/composables/useGroupAssignment.ts +148 -0
- package/src/composables/useListSelection.ts +158 -0
- package/src/composables/usePluginApi.ts +7 -14
- package/src/composables/usePluginClient.ts +425 -0
- package/src/composables/usePluginConfig.ts +34 -13
- package/src/composables/useRequestSyncState.ts +126 -0
- package/src/composables/useSampleGroups.ts +126 -0
- package/src/composables/useSelectionLimit.ts +57 -0
- package/src/composables/useSortedItems.ts +118 -0
- package/src/composables/useTemplateCollection.ts +229 -0
- package/src/composables/useTextSearch.ts +60 -0
- package/src/composables/useTheme.ts +2 -28
- package/src/composables/useTimeUtils.ts +26 -2
- package/src/composables/useWellPlateEditor.ts +13 -9
- package/src/index.ts +224 -4
- package/src/install.ts +11 -4
- package/src/stores/settings.ts +13 -9
- package/src/styles/components/app-page-selector.css +23 -0
- package/src/styles/components/app-pill-nav.css +7 -0
- package/src/styles/components/app-top-bar.css +34 -0
- package/src/styles/components/concentration-input.css +3 -142
- package/src/styles/components/empty-state.css +0 -16
- package/src/styles/components/settings-button.css +3 -66
- package/src/styles/components/theme-toggle.css +3 -66
- package/src/styles/index.css +0 -1
- package/src/templates/adapters.ts +785 -0
- package/src/templates/builders.ts +2149 -0
- package/src/templates/catalog.ts +245 -0
- package/src/templates/componentBindings.ts +615 -0
- package/src/templates/controlSchemas.ts +718 -0
- package/src/templates/index.ts +314 -0
- package/src/templates/lookup.ts +18 -0
- package/src/templates/packs.ts +156 -0
- package/src/templates/presets.ts +146 -0
- package/src/templates/types.ts +668 -0
- package/src/types/components.ts +41 -4
- package/src/types/form-builder.ts +7 -2
- package/src/types/index.ts +14 -0
- package/src/types/platform.ts +7 -1
- package/src/utils/formModelSync.ts +52 -0
- package/src/utils/items.ts +28 -0
- package/src/utils/options.ts +23 -0
- package/src/utils/pluginIcon.ts +30 -0
- package/dist/auth-DsI0rQ7_.js.map +0 -1
- package/dist/components-_XqPEhP9.js.map +0 -1
- package/dist/composables-tiZqLu1M.js.map +0 -1
- package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
- package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
- package/src/styles/components/grouping-modal.css +0 -323
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Tabbed settings modal with three usage modes:
|
|
4
4
|
*
|
|
5
|
-
* 1. Schema-driven (recommended) — pass `schema
|
|
6
|
-
*
|
|
5
|
+
* 1. Schema-driven (recommended) — pass `schema`, compact `controls`, or a
|
|
6
|
+
* complete `defineControlModel()` result plus `v-model:values`. Each
|
|
7
|
+
* group/section becomes a tab; fields auto-render via the SDK form field
|
|
7
8
|
* registry (text, select, number, toggle, molecule, concentration, …).
|
|
8
9
|
* Conditional visibility, validation, and dynamic options come for free.
|
|
9
10
|
*
|
|
@@ -19,20 +20,37 @@
|
|
|
19
20
|
*/
|
|
20
21
|
import { ref, computed, watch } from 'vue'
|
|
21
22
|
import BaseModal from './BaseModal.vue'
|
|
22
|
-
import
|
|
23
|
+
import FormFieldRendererInternal from './internal/FormFieldRendererInternal.vue'
|
|
23
24
|
import { useFormBuilder } from '../composables/useFormBuilder'
|
|
25
|
+
import {
|
|
26
|
+
controlsToSettingsSchema,
|
|
27
|
+
defineControlModel,
|
|
28
|
+
mergeControlWorkspaceOptions,
|
|
29
|
+
type ControlModel,
|
|
30
|
+
type ControlModelBinding,
|
|
31
|
+
type ControlSchema,
|
|
32
|
+
type ControlWorkspaceOptions,
|
|
33
|
+
} from '../composables/useControlSchema'
|
|
24
34
|
import { useSettingsStore, colorPalettes } from '../stores/settings'
|
|
35
|
+
import {
|
|
36
|
+
formSchemaFieldNames,
|
|
37
|
+
pickExistingRecordKeys,
|
|
38
|
+
pickRecordKeys,
|
|
39
|
+
recordValuesEqualForKeys,
|
|
40
|
+
} from '../utils/formModelSync'
|
|
25
41
|
import type {
|
|
26
42
|
ThemeMode,
|
|
27
43
|
ColorPalette,
|
|
28
44
|
TableDensity,
|
|
29
45
|
SettingsTab,
|
|
46
|
+
SettingsTabInput,
|
|
30
47
|
SettingsModalLayout,
|
|
31
48
|
SettingsModalSchema,
|
|
32
49
|
FormSchema,
|
|
33
50
|
FormSectionSchema,
|
|
34
51
|
FormEnhancements,
|
|
35
52
|
} from '../types'
|
|
53
|
+
import { normalizeItemInput } from '../utils/items'
|
|
36
54
|
|
|
37
55
|
// Map our settings groups onto the form-builder's flat-section shape.
|
|
38
56
|
// `title: ''` because the rail/pane header already shows the group name.
|
|
@@ -52,13 +70,19 @@ interface Props {
|
|
|
52
70
|
modelValue: boolean
|
|
53
71
|
title?: string
|
|
54
72
|
/** Manual tab descriptors. Ignored when `schema` is set (groups become tabs). */
|
|
55
|
-
tabs?:
|
|
73
|
+
tabs?: SettingsTabInput[]
|
|
56
74
|
showAppearance?: boolean
|
|
57
75
|
size?: 'md' | 'lg' | 'xl'
|
|
58
76
|
layout?: SettingsModalLayout
|
|
59
77
|
/** Declarative schema — fields auto-render via SDK form components. */
|
|
60
78
|
schema?: SettingsModalSchema
|
|
61
|
-
/**
|
|
79
|
+
/** Model returned by defineControlModel(), or a raw nested ControlModel for one-step settings generation. */
|
|
80
|
+
model?: ControlModel | ControlModelBinding
|
|
81
|
+
/** Compact controls model — converted to `schema` when `schema` is not set. */
|
|
82
|
+
controls?: ControlSchema
|
|
83
|
+
/** Conversion options for `controls`, such as section labels, columns, and shared initialValues. */
|
|
84
|
+
controlOptions?: ControlWorkspaceOptions
|
|
85
|
+
/** Two-way bound values when `schema`, `model`, or `controls` is set. */
|
|
62
86
|
values?: Record<string, unknown>
|
|
63
87
|
/** Optional dynamic enhancements (validators, dynamic options, callbacks). */
|
|
64
88
|
enhancements?: FormEnhancements<Record<string, unknown>>
|
|
@@ -70,6 +94,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
70
94
|
showAppearance: true,
|
|
71
95
|
size: 'lg',
|
|
72
96
|
layout: 'horizontal',
|
|
97
|
+
model: undefined,
|
|
73
98
|
})
|
|
74
99
|
|
|
75
100
|
const emit = defineEmits<{
|
|
@@ -95,35 +120,93 @@ const uid = `mint-settings-${Math.random().toString(36).slice(2, 9)}`
|
|
|
95
120
|
const tabId = (id: string) => `${uid}-tab-${id}`
|
|
96
121
|
const panelId = (id: string) => `${uid}-panel-${id}`
|
|
97
122
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
const resolvedModel = computed<ControlModelBinding | undefined>(() => {
|
|
124
|
+
if (props.model === undefined) return undefined
|
|
125
|
+
return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)
|
|
126
|
+
})
|
|
127
|
+
const resolvedControls = computed<ControlSchema | undefined>(() =>
|
|
128
|
+
props.controls ?? resolvedModel.value?.controls,
|
|
129
|
+
)
|
|
130
|
+
const resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>
|
|
131
|
+
mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)
|
|
132
|
+
)
|
|
133
|
+
const settingsSchema = computed<SettingsModalSchema | undefined>(() =>
|
|
134
|
+
props.schema ?? (
|
|
135
|
+
resolvedControls.value
|
|
136
|
+
? controlsToSettingsSchema(resolvedControls.value, resolvedControlOptions.value)
|
|
137
|
+
: undefined
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
const isSchemaDriven = computed(() => !!settingsSchema.value)
|
|
141
|
+
const resolvedValues = computed<Record<string, unknown>>(() => ({
|
|
142
|
+
...(resolvedControlOptions.value.initialValues ?? {}),
|
|
143
|
+
...(props.values ?? {}),
|
|
144
|
+
}))
|
|
145
|
+
|
|
146
|
+
// Schema-driven mode keeps one form-builder instance and syncs schema changes
|
|
147
|
+
// into it, so callers can swap groups/fields without remounting the modal.
|
|
148
|
+
const builder = useFormBuilder(
|
|
149
|
+
settingsSchema.value ? buildFlatSchema(settingsSchema.value) : { sections: [] },
|
|
150
|
+
resolvedValues.value,
|
|
151
|
+
props.enhancements,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
watch(
|
|
155
|
+
() => settingsSchema.value,
|
|
156
|
+
(schema) => {
|
|
157
|
+
if (!schema) {
|
|
158
|
+
builder.updateSchema({ sections: [] }, {})
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const flatSchema = buildFlatSchema(schema)
|
|
163
|
+
const fieldNames = formSchemaFieldNames(flatSchema)
|
|
164
|
+
const sourceValues = props.values === undefined
|
|
165
|
+
? {
|
|
166
|
+
...resolvedValues.value,
|
|
167
|
+
...(builder.form.data as Record<string, unknown>),
|
|
168
|
+
}
|
|
169
|
+
: resolvedValues.value
|
|
170
|
+
|
|
171
|
+
builder.updateSchema(flatSchema, pickExistingRecordKeys(sourceValues ?? {}, fieldNames))
|
|
172
|
+
},
|
|
173
|
+
{ deep: true },
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
watch(
|
|
177
|
+
() => ({ ...resolvedValues.value }),
|
|
178
|
+
(data) => {
|
|
179
|
+
if (!settingsSchema.value) return
|
|
180
|
+
const fieldNames = builderFieldNames()
|
|
181
|
+
if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return
|
|
182
|
+
builder.reset(pickRecordKeys(data, fieldNames))
|
|
183
|
+
},
|
|
184
|
+
{ deep: true },
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
watch(
|
|
188
|
+
() => ({ ...builder.form.data }),
|
|
189
|
+
(data) => {
|
|
190
|
+
if (settingsSchema.value) emit('update:values', data as Record<string, unknown>)
|
|
191
|
+
},
|
|
192
|
+
{ deep: true },
|
|
193
|
+
)
|
|
113
194
|
|
|
114
195
|
// Schema groups whose `condition` evaluates false against the current data are
|
|
115
196
|
// dropped from the rail entirely — same semantics as section visibility in
|
|
116
197
|
// FormBuilder. Manual `tabs` have no condition mechanism, so they pass through.
|
|
117
198
|
const visibleSchemaGroups = computed(() =>
|
|
118
|
-
|
|
119
|
-
?
|
|
199
|
+
settingsSchema.value
|
|
200
|
+
? settingsSchema.value.groups.filter((g) => builder.isSectionVisible(g.id))
|
|
120
201
|
: [],
|
|
121
202
|
)
|
|
122
203
|
|
|
204
|
+
const manualTabs = computed<SettingsTab[]>(() => props.tabs.map(normalizeItemInput))
|
|
205
|
+
|
|
123
206
|
const allTabs = computed<SettingsTab[]>(() => {
|
|
124
|
-
const base: SettingsTab[] =
|
|
207
|
+
const base: SettingsTab[] = settingsSchema.value
|
|
125
208
|
? visibleSchemaGroups.value.map((g) => ({ id: g.id, label: g.label, icon: g.icon, description: g.description }))
|
|
126
|
-
:
|
|
209
|
+
: manualTabs.value
|
|
127
210
|
return props.showAppearance ? [...base, APPEARANCE_TAB] : base
|
|
128
211
|
})
|
|
129
212
|
|
|
@@ -145,7 +228,7 @@ const activeGroup = computed(() =>
|
|
|
145
228
|
)
|
|
146
229
|
|
|
147
230
|
const activeGroupVisibleFields = computed(() => {
|
|
148
|
-
if (!activeGroup.value
|
|
231
|
+
if (!activeGroup.value) return []
|
|
149
232
|
return activeGroup.value.fields.filter((f) => builder.isFieldVisible(f.name))
|
|
150
233
|
})
|
|
151
234
|
|
|
@@ -165,6 +248,14 @@ function handleClose() {
|
|
|
165
248
|
emit('update:modelValue', false)
|
|
166
249
|
emit('close')
|
|
167
250
|
}
|
|
251
|
+
|
|
252
|
+
function builderFieldNames(): string[] {
|
|
253
|
+
return builder.fields.map(field => field.name)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {
|
|
257
|
+
return 'controls' in model && 'controlOptions' in model
|
|
258
|
+
}
|
|
168
259
|
</script>
|
|
169
260
|
|
|
170
261
|
<template>
|
|
@@ -243,7 +334,7 @@ function handleClose() {
|
|
|
243
334
|
|
|
244
335
|
<div :class="isVertical ? 'mint-settings-modal__pane-body' : null">
|
|
245
336
|
<div
|
|
246
|
-
v-if="
|
|
337
|
+
v-if="isSchemaDriven && activeGroup"
|
|
247
338
|
class="mint-settings-modal__group-grid"
|
|
248
339
|
:style="{ '--mint-settings-cols': activeGroup.columns ?? 1 }"
|
|
249
340
|
>
|
|
@@ -255,7 +346,7 @@ function handleClose() {
|
|
|
255
346
|
:form="builder.form"
|
|
256
347
|
:field-props="builder.form.getFieldProps(field.name)"
|
|
257
348
|
>
|
|
258
|
-
<
|
|
349
|
+
<FormFieldRendererInternal
|
|
259
350
|
:field="field"
|
|
260
351
|
:resolved-props="builder.getResolvedFieldProps(field)"
|
|
261
352
|
:form="builder.form"
|
|
@@ -265,8 +356,8 @@ function handleClose() {
|
|
|
265
356
|
</template>
|
|
266
357
|
</div>
|
|
267
358
|
|
|
268
|
-
<template v-else-if="!
|
|
269
|
-
<template v-for="tab in
|
|
359
|
+
<template v-else-if="!isSchemaDriven">
|
|
360
|
+
<template v-for="tab in manualTabs" :key="tab.id">
|
|
270
361
|
<div v-show="activeTab === tab.id">
|
|
271
362
|
<slot :name="`tab-${tab.id}`" />
|
|
272
363
|
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Lets users type and add tags with autocomplete suggestions, categories, and a max-tag cap. */
|
|
3
3
|
import { ref, computed } from 'vue'
|
|
4
|
+
import { useSelectionLimit } from '../composables/useSelectionLimit'
|
|
5
|
+
import { useDropdownState } from '../composables/useDropdownState'
|
|
4
6
|
|
|
5
7
|
interface TagSuggestion {
|
|
6
8
|
value: string
|
|
@@ -43,13 +45,23 @@ const emit = defineEmits<{
|
|
|
43
45
|
|
|
44
46
|
const inputValue = ref('')
|
|
45
47
|
const inputRef = ref<HTMLInputElement>()
|
|
46
|
-
const dropdownOpen = ref(false)
|
|
47
48
|
const highlightedIndex = ref(-1)
|
|
49
|
+
const {
|
|
50
|
+
isOpen: dropdownOpen,
|
|
51
|
+
rootRef,
|
|
52
|
+
open: openDropdown,
|
|
53
|
+
close: closeDropdown,
|
|
54
|
+
} = useDropdownState({
|
|
55
|
+
onClose: () => {
|
|
56
|
+
highlightedIndex.value = -1
|
|
57
|
+
},
|
|
58
|
+
})
|
|
48
59
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
const selectionLimit = useSelectionLimit({
|
|
61
|
+
count: () => props.modelValue.length,
|
|
62
|
+
max: () => props.maxTags,
|
|
52
63
|
})
|
|
64
|
+
const canAddMore = selectionLimit.canAddMore
|
|
53
65
|
|
|
54
66
|
const normalizedSuggestions = computed<TagSuggestion[]>(() => {
|
|
55
67
|
return props.suggestions.map(s => typeof s === 'string' ? { value: s } : s)
|
|
@@ -73,7 +85,7 @@ function categoryFor(tag: string): TagCategory | undefined {
|
|
|
73
85
|
function addTag(value: string) {
|
|
74
86
|
const trimmed = value.trim()
|
|
75
87
|
if (!trimmed) return
|
|
76
|
-
if (!
|
|
88
|
+
if (!selectionLimit.canAdd()) return
|
|
77
89
|
|
|
78
90
|
// If a category is active and user didn't already type a prefix, prepend it
|
|
79
91
|
let final = trimmed
|
|
@@ -85,8 +97,7 @@ function addTag(value: string) {
|
|
|
85
97
|
|
|
86
98
|
emit('update:modelValue', [...props.modelValue, final])
|
|
87
99
|
inputValue.value = ''
|
|
88
|
-
|
|
89
|
-
highlightedIndex.value = -1
|
|
100
|
+
closeDropdown()
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
function removeTag(index: number) {
|
|
@@ -100,7 +111,7 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
100
111
|
if (event.key === 'ArrowDown' && filteredSuggestions.value.length) {
|
|
101
112
|
event.preventDefault()
|
|
102
113
|
highlightedIndex.value = Math.min(highlightedIndex.value + 1, filteredSuggestions.value.length - 1)
|
|
103
|
-
|
|
114
|
+
openDropdown()
|
|
104
115
|
return
|
|
105
116
|
}
|
|
106
117
|
if (event.key === 'ArrowUp' && filteredSuggestions.value.length) {
|
|
@@ -118,8 +129,7 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
118
129
|
return
|
|
119
130
|
}
|
|
120
131
|
if (event.key === 'Escape') {
|
|
121
|
-
|
|
122
|
-
highlightedIndex.value = -1
|
|
132
|
+
closeDropdown()
|
|
123
133
|
return
|
|
124
134
|
}
|
|
125
135
|
if (event.key === 'Backspace' && !inputValue.value && props.modelValue.length > 0) {
|
|
@@ -128,13 +138,13 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
function handleFocus() {
|
|
131
|
-
if (filteredSuggestions.value.length)
|
|
141
|
+
if (filteredSuggestions.value.length) openDropdown()
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
function handleBlur() {
|
|
135
145
|
// Delay so a click on a suggestion fires first
|
|
136
146
|
setTimeout(() => {
|
|
137
|
-
|
|
147
|
+
closeDropdown()
|
|
138
148
|
}, 120)
|
|
139
149
|
}
|
|
140
150
|
|
|
@@ -147,7 +157,7 @@ function handlePaste(event: ClipboardEvent) {
|
|
|
147
157
|
const newTags = [...props.modelValue]
|
|
148
158
|
|
|
149
159
|
for (const tag of tags) {
|
|
150
|
-
if (!
|
|
160
|
+
if (!selectionLimit.canAdd(1, newTags.length)) break
|
|
151
161
|
if (!props.allowDuplicates && newTags.includes(tag)) continue
|
|
152
162
|
newTags.push(tag)
|
|
153
163
|
}
|
|
@@ -164,13 +174,18 @@ function setCategory(prefix: string) {
|
|
|
164
174
|
}
|
|
165
175
|
|
|
166
176
|
function onInputEvent() {
|
|
167
|
-
|
|
177
|
+
if (filteredSuggestions.value.length > 0) {
|
|
178
|
+
openDropdown()
|
|
179
|
+
} else {
|
|
180
|
+
closeDropdown()
|
|
181
|
+
}
|
|
168
182
|
highlightedIndex.value = -1
|
|
169
183
|
}
|
|
170
184
|
</script>
|
|
171
185
|
|
|
172
186
|
<template>
|
|
173
187
|
<div
|
|
188
|
+
ref="rootRef"
|
|
174
189
|
:class="[
|
|
175
190
|
'mint-tags-input',
|
|
176
191
|
`mint-tags-input--${size}`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Icon button that toggles between light and dark mode, swapping sun/moon icons to reflect the active theme. */
|
|
3
3
|
import { useTheme } from '../composables/useTheme'
|
|
4
|
+
import IconButton from './IconButton.vue'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
size?: 'sm' | 'md' | 'lg'
|
|
@@ -14,16 +15,17 @@ const { isDark, toggleTheme } = useTheme()
|
|
|
14
15
|
</script>
|
|
15
16
|
|
|
16
17
|
<template>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:
|
|
18
|
+
<IconButton
|
|
19
|
+
class="mint-theme-toggle"
|
|
20
|
+
variant="ghost"
|
|
21
|
+
:size="size"
|
|
22
|
+
:label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
|
|
21
23
|
@click="toggleTheme"
|
|
22
24
|
>
|
|
23
25
|
<!-- Lucide sun -->
|
|
24
26
|
<svg
|
|
25
27
|
v-if="isDark"
|
|
26
|
-
|
|
28
|
+
class="mint-theme-toggle__icon"
|
|
27
29
|
viewBox="0 0 24 24"
|
|
28
30
|
fill="none"
|
|
29
31
|
stroke="currentColor"
|
|
@@ -36,7 +38,7 @@ const { isDark, toggleTheme } = useTheme()
|
|
|
36
38
|
<!-- Lucide moon -->
|
|
37
39
|
<svg
|
|
38
40
|
v-else
|
|
39
|
-
|
|
41
|
+
class="mint-theme-toggle__icon"
|
|
40
42
|
viewBox="0 0 24 24"
|
|
41
43
|
fill="none"
|
|
42
44
|
stroke="currentColor"
|
|
@@ -46,7 +48,7 @@ const { isDark, toggleTheme } = useTheme()
|
|
|
46
48
|
>
|
|
47
49
|
<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401" />
|
|
48
50
|
</svg>
|
|
49
|
-
</
|
|
51
|
+
</IconButton>
|
|
50
52
|
</template>
|
|
51
53
|
|
|
52
54
|
<style>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Input that opens a scrollable time-slot dropdown in 12h or 24h format, with min/max range. */
|
|
3
|
-
import { ref, computed, watch, nextTick
|
|
4
|
-
import {
|
|
3
|
+
import { ref, computed, watch, nextTick } from 'vue'
|
|
4
|
+
import { useDropdownState } from '../composables/useDropdownState'
|
|
5
|
+
import {
|
|
6
|
+
compareTime,
|
|
7
|
+
findNearestTimeSlotIndex,
|
|
8
|
+
formatTimeSlot,
|
|
9
|
+
generateTimeSlots,
|
|
10
|
+
} from '../composables/useTimeUtils'
|
|
5
11
|
|
|
6
12
|
interface Props {
|
|
7
13
|
modelValue?: string
|
|
@@ -30,10 +36,9 @@ const emit = defineEmits<{
|
|
|
30
36
|
'update:modelValue': [value: string | undefined]
|
|
31
37
|
}>()
|
|
32
38
|
|
|
33
|
-
const
|
|
34
|
-
const containerRef = ref<HTMLDivElement>()
|
|
35
|
-
const listRef = ref<HTMLUListElement>()
|
|
39
|
+
const listRef = ref<HTMLUListElement | null>(null)
|
|
36
40
|
const highlightedIndex = ref(-1)
|
|
41
|
+
const { isOpen, rootRef, open, close, toggle } = useDropdownState()
|
|
37
42
|
|
|
38
43
|
const slots = computed(() => {
|
|
39
44
|
const start = props.min ?? '00:00'
|
|
@@ -43,8 +48,7 @@ const slots = computed(() => {
|
|
|
43
48
|
|
|
44
49
|
const displayValue = computed(() => {
|
|
45
50
|
if (!props.modelValue) return ''
|
|
46
|
-
|
|
47
|
-
return formatTime(hour, minute, props.format)
|
|
51
|
+
return formatTimeSlot(props.modelValue, props.format)
|
|
48
52
|
})
|
|
49
53
|
|
|
50
54
|
function isSlotDisabled(slot: string): boolean {
|
|
@@ -56,23 +60,17 @@ function isSlotDisabled(slot: string): boolean {
|
|
|
56
60
|
function selectSlot(slot: string) {
|
|
57
61
|
if (isSlotDisabled(slot) || props.disabled) return
|
|
58
62
|
emit('update:modelValue', slot)
|
|
59
|
-
|
|
63
|
+
close()
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
function clear() {
|
|
63
67
|
emit('update:modelValue', undefined)
|
|
64
|
-
|
|
68
|
+
close()
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
function toggleDropdown() {
|
|
68
72
|
if (props.disabled) return
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function handleClickOutside(event: MouseEvent) {
|
|
73
|
-
if (containerRef.value && !containerRef.value.contains(event.target as Node)) {
|
|
74
|
-
isOpen.value = false
|
|
75
|
-
}
|
|
73
|
+
toggle()
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
function scrollToActiveSlot() {
|
|
@@ -95,26 +93,14 @@ function scrollToActiveSlot() {
|
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
function findNearestSlotIndex(time: string): number {
|
|
98
|
-
|
|
99
|
-
let bestIndex = 0
|
|
100
|
-
let bestDiff = Infinity
|
|
101
|
-
for (let i = 0; i < slots.value.length; i++) {
|
|
102
|
-
const { hour: h1, minute: m1 } = parseTime(slots.value[i])
|
|
103
|
-
const { hour: h2, minute: m2 } = parseTime(time)
|
|
104
|
-
const minuteDiff = Math.abs((h1 * 60 + m1) - (h2 * 60 + m2))
|
|
105
|
-
if (minuteDiff < bestDiff) {
|
|
106
|
-
bestDiff = minuteDiff
|
|
107
|
-
bestIndex = i
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return bestIndex
|
|
96
|
+
return findNearestTimeSlotIndex(time, slots.value)
|
|
111
97
|
}
|
|
112
98
|
|
|
113
99
|
function handleKeydown(event: KeyboardEvent) {
|
|
114
100
|
if (!isOpen.value) {
|
|
115
101
|
if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
|
|
116
102
|
event.preventDefault()
|
|
117
|
-
|
|
103
|
+
open()
|
|
118
104
|
return
|
|
119
105
|
}
|
|
120
106
|
return
|
|
@@ -150,7 +136,7 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
150
136
|
}
|
|
151
137
|
case 'Escape': {
|
|
152
138
|
event.preventDefault()
|
|
153
|
-
|
|
139
|
+
close()
|
|
154
140
|
break
|
|
155
141
|
}
|
|
156
142
|
}
|
|
@@ -180,18 +166,10 @@ watch(isOpen, (open) => {
|
|
|
180
166
|
highlightedIndex.value = -1
|
|
181
167
|
}
|
|
182
168
|
})
|
|
183
|
-
|
|
184
|
-
onMounted(() => {
|
|
185
|
-
document.addEventListener('click', handleClickOutside)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
onUnmounted(() => {
|
|
189
|
-
document.removeEventListener('click', handleClickOutside)
|
|
190
|
-
})
|
|
191
169
|
</script>
|
|
192
170
|
|
|
193
171
|
<template>
|
|
194
|
-
<div ref="
|
|
172
|
+
<div ref="rootRef" class="mint-time-picker" @keydown="handleKeydown">
|
|
195
173
|
<div class="mint-time-picker__input-wrapper">
|
|
196
174
|
<input
|
|
197
175
|
type="text"
|
|
@@ -260,7 +238,7 @@ onUnmounted(() => {
|
|
|
260
238
|
]"
|
|
261
239
|
@click="selectSlot(slot)"
|
|
262
240
|
>
|
|
263
|
-
{{
|
|
241
|
+
{{ formatTimeSlot(slot, format) }}
|
|
264
242
|
</li>
|
|
265
243
|
</ul>
|
|
266
244
|
</div>
|
|
@@ -1,62 +1,9 @@
|
|
|
1
|
+
<!-- @deprecated Use AppToastContainer instead -->
|
|
1
2
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
import
|
|
4
|
-
import type { Toast } from '../types'
|
|
5
|
-
|
|
6
|
-
const { toasts, dismiss } = useToast()
|
|
7
|
-
|
|
8
|
-
const accentColors: Record<Toast['type'], string> = {
|
|
9
|
-
success: '#10B981',
|
|
10
|
-
error: '#EF4444',
|
|
11
|
-
warning: '#F59E0B',
|
|
12
|
-
info: '#3B82F6',
|
|
13
|
-
}
|
|
3
|
+
/** @deprecated Compatibility wrapper for the legacy toast container export. Use AppToastContainer instead. */
|
|
4
|
+
import AppToastContainer from './AppToastContainer.vue'
|
|
14
5
|
</script>
|
|
15
6
|
|
|
16
7
|
<template>
|
|
17
|
-
<
|
|
18
|
-
<div class="mint-toast__container">
|
|
19
|
-
<TransitionGroup name="toast">
|
|
20
|
-
<div
|
|
21
|
-
v-for="toast in toasts"
|
|
22
|
-
:key="toast.id"
|
|
23
|
-
:class="['mint-toast__item', `mint-toast__item--${toast.type}`]"
|
|
24
|
-
:style="{
|
|
25
|
-
borderLeftColor: accentColors[toast.type],
|
|
26
|
-
}"
|
|
27
|
-
role="alert"
|
|
28
|
-
@click="dismiss(toast.id)"
|
|
29
|
-
>
|
|
30
|
-
<!-- Lucide circle-check -->
|
|
31
|
-
<svg v-if="toast.type === 'success'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
|
|
32
|
-
<circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" />
|
|
33
|
-
</svg>
|
|
34
|
-
<!-- Lucide circle-x -->
|
|
35
|
-
<svg v-else-if="toast.type === 'error'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
|
|
36
|
-
<circle cx="12" cy="12" r="10" /><path d="m15 9-6 6" /><path d="m9 9 6 6" />
|
|
37
|
-
</svg>
|
|
38
|
-
<!-- Lucide triangle-alert -->
|
|
39
|
-
<svg v-else-if="toast.type === 'warning'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
|
|
40
|
-
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /><path d="M12 9v4" /><path d="M12 17h.01" />
|
|
41
|
-
</svg>
|
|
42
|
-
<!-- Lucide info -->
|
|
43
|
-
<svg v-else class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
|
|
44
|
-
<circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" />
|
|
45
|
-
</svg>
|
|
46
|
-
<span class="mint-toast__message">{{ toast.message }}</span>
|
|
47
|
-
<span
|
|
48
|
-
class="mint-toast__progress"
|
|
49
|
-
:style="{
|
|
50
|
-
backgroundColor: accentColors[toast.type],
|
|
51
|
-
animationDuration: `${toast.duration ?? 3500}ms`,
|
|
52
|
-
}"
|
|
53
|
-
/>
|
|
54
|
-
</div>
|
|
55
|
-
</TransitionGroup>
|
|
56
|
-
</div>
|
|
57
|
-
</Teleport>
|
|
8
|
+
<AppToastContainer />
|
|
58
9
|
</template>
|
|
59
|
-
|
|
60
|
-
<style>
|
|
61
|
-
@import '../styles/components/toast.css';
|
|
62
|
-
</style>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** Floating tooltip using @floating-ui with auto-flip, delay, optional keyboard shortcut badge, and body Teleport. */
|
|
3
3
|
import { ref, onBeforeUnmount, nextTick } from 'vue'
|
|
4
4
|
import { computePosition, flip, offset, shift, type Placement } from '@floating-ui/dom'
|
|
5
|
+
import { useEventListener } from '../composables/useEventListener'
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
text: string
|
|
@@ -42,22 +43,18 @@ async function updatePosition() {
|
|
|
42
43
|
actualPlacement.value = placement
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
window.removeEventListener('scroll', updatePosition, { capture: true })
|
|
52
|
-
window.removeEventListener('resize', updatePosition)
|
|
53
|
-
}
|
|
46
|
+
useEventListener(() => window, 'scroll', updatePosition, {
|
|
47
|
+
passive: true,
|
|
48
|
+
capture: true,
|
|
49
|
+
enabled: visible,
|
|
50
|
+
})
|
|
51
|
+
useEventListener(() => window, 'resize', updatePosition, { enabled: visible })
|
|
54
52
|
|
|
55
53
|
async function show() {
|
|
56
54
|
timeoutId = setTimeout(async () => {
|
|
57
55
|
visible.value = true
|
|
58
56
|
await nextTick()
|
|
59
57
|
await updatePosition()
|
|
60
|
-
addListeners()
|
|
61
58
|
}, props.delay)
|
|
62
59
|
}
|
|
63
60
|
|
|
@@ -65,12 +62,10 @@ function hide() {
|
|
|
65
62
|
clearTimeout(timeoutId)
|
|
66
63
|
timeoutId = undefined
|
|
67
64
|
visible.value = false
|
|
68
|
-
removeListeners()
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
onBeforeUnmount(() => {
|
|
72
68
|
clearTimeout(timeoutId)
|
|
73
|
-
removeListeners()
|
|
74
69
|
})
|
|
75
70
|
|
|
76
71
|
function resolvedMaxWidth(value: string | number | undefined): string | undefined {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref, watch, computed
|
|
2
|
+
import { ref, watch, computed } from 'vue'
|
|
3
3
|
import type { Well, WellEditData, WellEditField } from '../types'
|
|
4
|
+
import { useEventListener } from '../composables/useEventListener'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
wellId: string
|
|
@@ -83,13 +84,7 @@ function handleKeydown(e: KeyboardEvent) {
|
|
|
83
84
|
if (e.key === 'Escape') close()
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
document.addEventListener('keydown', handleKeydown)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
onUnmounted(() => {
|
|
91
|
-
document.removeEventListener('keydown', handleKeydown)
|
|
92
|
-
})
|
|
87
|
+
useEventListener(() => document, 'keydown', handleKeydown)
|
|
93
88
|
|
|
94
89
|
const sampleTypeButtons = [
|
|
95
90
|
{ type: 'sample', label: 'S', tooltip: 'Sample' },
|