@morscherlab/mint-sdk 1.4.0-beta.1 → 1.4.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ActionMenu-CScINahU.js → AlertBox-B2Bgr3ei.js} +40 -209
- package/dist/AlertBox-B2Bgr3ei.js.map +1 -0
- package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-mvm--GxN.js} +161 -110
- package/dist/AppSidebar-mvm--GxN.js.map +1 -0
- package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-nbJj51is.js} +2 -2
- package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-nbJj51is.js.map} +1 -1
- package/dist/ExperimentPopover-5T1fAq2L.js +337 -0
- package/dist/ExperimentPopover-5T1fAq2L.js.map +1 -0
- package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-65peG4Up.js} +198 -181
- package/dist/ExperimentSelectorModal-65peG4Up.js.map +1 -0
- package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-BtnnjWzs.js} +2 -2
- package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-BtnnjWzs.js.map} +1 -1
- package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-DO3IZ7qz.js} +9 -9
- package/dist/SettingsModal-DO3IZ7qz.js.map +1 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
- package/dist/components/AuditTrail.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +2 -2
- package/dist/components/ChartContainer.vue.d.ts +2 -0
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/ExperimentPopover.vue.d.ts +18 -0
- package/dist/components/ExperimentSelectorModal.vue.d.ts +0 -4
- package/dist/components/FilePicker.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +2 -2
- package/dist/components/PluginIcon.vue.d.ts +2 -2
- package/dist/components/SampleSelector.vue.d.ts +6 -6
- package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
- package/dist/components/StepWizard.vue.d.ts +3 -0
- package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +1 -1
- package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
- package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
- package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
- package/dist/composables/createClient.d.ts +2 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/pluginIdentity.d.ts +24 -0
- package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
- package/dist/composables/useAppExperiment.d.ts +8 -0
- package/dist/composables/useCalendarGrid.d.ts +1 -1
- package/dist/composables/useExperimentSelector.d.ts +6 -2
- package/dist/composables/usePlatform.d.ts +1 -0
- package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-B8CTXagP.js} +2 -2
- package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-B8CTXagP.js.map} +1 -1
- package/dist/{data-C2PwI_dn.js → data-CgILxDLW.js} +178 -124
- package/dist/data-CgILxDLW.js.map +1 -0
- package/dist/data.js +8 -8
- package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-Cqp6qNrR.js} +3 -3
- package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-Cqp6qNrR.js.map} +1 -1
- package/dist/index.js +19 -19
- package/dist/{install-DAID6c2-.js → install-CsPFJSx3.js} +15 -14
- package/dist/{install-DAID6c2-.js.map → install-CsPFJSx3.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-DLwRl_Ql.js → lab-MHzp-_J8.js} +128 -147
- package/dist/lab-MHzp-_J8.js.map +1 -0
- package/dist/lab.js +4 -3
- package/dist/{records-BzTcxekG.js → records-B6gpM_3e.js} +472 -274
- package/dist/records-B6gpM_3e.js.map +1 -0
- package/dist/{runtime-D6gVat6r.js → runtime-8bs8EogX.js} +27 -31
- package/dist/runtime-8bs8EogX.js.map +1 -0
- package/dist/runtime.js +6 -6
- package/dist/{shell-5ju97nOS.js → shell-CywENwBT.js} +5 -5
- package/dist/{shell-5ju97nOS.js.map → shell-CywENwBT.js.map} +1 -1
- package/dist/shell.js +5 -5
- package/dist/styles.css +1606 -1666
- package/dist/types/componentWorkflowTypes.d.ts +2 -0
- package/dist/types/generated/platformContext.d.ts +1 -0
- package/dist/types/generated-ui.d.ts +4 -0
- package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-8yWMu-7_.js} +60 -2
- package/dist/useDropdownState-8yWMu-7_.js.map +1 -0
- package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-DHnxKFAF.js} +3 -3
- package/dist/{useExperimentSamples-CxcnxZ8h.js.map → useExperimentSamples-DHnxKFAF.js.map} +1 -1
- package/dist/{useListReorder-CcbEYxX-.js → useListReorder-BitdUhxn.js} +109 -277
- package/dist/useListReorder-BitdUhxn.js.map +1 -0
- package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-DiwCt77M.js} +71 -57
- package/dist/useRackEditor-DiwCt77M.js.map +1 -0
- package/dist/{useRealtime-PalKue84.js → useRealtime-C3iy9QkU.js} +7 -4
- package/dist/useRealtime-C3iy9QkU.js.map +1 -0
- package/dist/useRequest-D506eHi8.js +244 -0
- package/dist/useRequest-D506eHi8.js.map +1 -0
- package/dist/utils/time.d.ts +7 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.test.ts +51 -2
- package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
- package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
- package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
- package/src/__tests__/components/BaseSelect.test.ts +2 -1
- package/src/__tests__/components/BatchProgressList.test.ts +16 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
- package/src/__tests__/components/ChartContainer.test.ts +11 -0
- package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
- package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
- package/src/__tests__/components/DatePicker.test.ts +21 -0
- package/src/__tests__/components/DatePicker.time.test.ts +2 -2
- package/src/__tests__/components/EmptyState.test.ts +1 -1
- package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +119 -13
- package/src/__tests__/components/GeneratedPluginView.test.ts +22 -1
- package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
- package/src/__tests__/components/PluginWorkspaceView.test.ts +7 -0
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
- package/src/__tests__/components/SampleSelector.test.ts +13 -8
- package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
- package/src/__tests__/components/StepWizard.test.ts +11 -0
- package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
- package/src/__tests__/components/deprecationTags.test.ts +1 -1
- package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
- package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
- package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
- package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
- package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
- package/src/__tests__/styles/cssTokens.test.ts +2 -2
- package/src/__tests__/styles/menusShellRefresh.test.ts +14 -10
- package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
- package/src/__tests__/utils/time.test.ts +18 -1
- package/src/components/AdductText.story.vue +7 -0
- package/src/components/AdductText.vue +1 -1
- package/src/components/AlertBox.story.vue +2 -2
- package/src/components/AlertBox.vue +18 -16
- package/src/components/AppSidebar.story.vue +0 -28
- package/src/components/AppTopBar.story.vue +47 -12
- package/src/components/AppTopBar.vue +118 -114
- package/src/components/ArtifactSaveDialog.story.vue +23 -0
- package/src/components/ArtifactSaveDialog.vue +102 -12
- package/src/components/AuditTrail.story.vue +2 -1
- package/src/components/AuditTrail.vue +2 -21
- package/src/components/Avatar.vue +10 -14
- package/src/components/BaseSelect.multiple.story.vue +13 -2
- package/src/components/BaseSelect.vue +4 -3
- package/src/components/BatchProgressList.vue +35 -25
- package/src/components/ChartContainer.story.vue +2 -1
- package/src/components/ChartContainer.vue +8 -6
- package/src/components/ConfirmDialog.story.vue +1 -1
- package/src/components/EmptyState.vue +1 -1
- package/src/components/ExperimentPopover.story.vue +165 -53
- package/src/components/ExperimentPopover.vue +169 -187
- package/src/components/ExperimentSelectorModal.story.vue +40 -36
- package/src/components/ExperimentSelectorModal.vue +124 -110
- package/src/components/FormField.story.vue +8 -48
- package/src/components/GeneratedPluginView.vue +7 -1
- package/src/components/InstrumentStateBadge.vue +1 -1
- package/src/components/JobsStatusTray.vue +1 -1
- package/src/components/LoadingSpinner.vue +1 -1
- package/src/components/NumberInput.vue +3 -6
- package/src/components/PluginIcon.story.vue +2 -10
- package/src/components/PluginIcon.vue +2 -2
- package/src/components/PluginWorkspaceView.story.vue +1 -1
- package/src/components/RuntimeUpdateNotice.vue +1 -1
- package/src/components/SampleSelector.story.vue +1 -1
- package/src/components/SampleSelector.vue +27 -37
- package/src/components/SampleSelectorGroupNode.vue +1 -1
- package/src/components/SectionCard.story.vue +2 -2
- package/src/components/SequenceProgressBar.story.vue +9 -0
- package/src/components/SequenceProgressBar.vue +6 -1
- package/src/components/SettingsModal.schema.ts +1 -1
- package/src/components/SettingsModal.vue +2 -2
- package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
- package/src/components/SmartGroupManual.story.vue +1 -1
- package/src/components/StepWizard.story.vue +33 -0
- package/src/components/StepWizard.vue +8 -1
- package/src/components/WellPlate.story.vue +3 -1
- package/src/components/WellPlate.vue +7 -0
- package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
- package/src/components/internal/DatePickerDateInternal.vue +16 -40
- package/src/components/internal/DatePickerDateTimeInternal.vue +3 -13
- package/src/components/internal/DatePickerTimeInternal.vue +5 -8
- package/src/components/internal/ResourcePickerListInternal.vue +45 -85
- package/src/components/internal/SelectMultipleInternal.vue +182 -65
- package/src/components/internal/SelectSearchableInternal.vue +2 -3
- package/src/composables/createClient.ts +2 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/pluginIdentity.ts +31 -0
- package/src/composables/pluginWorkspaceModel.ts +2 -2
- package/src/composables/useAppExperiment.ts +20 -0
- package/src/composables/useCalendarGrid.ts +4 -3
- package/src/composables/useExperimentSelector.ts +77 -31
- package/src/composables/usePluginWorkspace.ts +1 -1
- package/src/styles/components/adduct-text.css +13 -5
- package/src/styles/components/alert-box.css +39 -58
- package/src/styles/components/app-layout.css +6 -5
- package/src/styles/components/app-page-selector.css +3 -1
- package/src/styles/components/app-pill-nav.css +11 -12
- package/src/styles/components/app-sidebar.css +9 -18
- package/src/styles/components/app-top-bar.css +57 -55
- package/src/styles/components/artifact-save-dialog.css +153 -2
- package/src/styles/components/audit-trail.css +16 -19
- package/src/styles/components/avatar.css +6 -4
- package/src/styles/components/batch-progress-list.css +38 -36
- package/src/styles/components/button.css +28 -59
- package/src/styles/components/calendar.css +12 -12
- package/src/styles/components/chart-container.css +34 -23
- package/src/styles/components/checkbox.css +2 -12
- package/src/styles/components/collapsible-card.css +27 -29
- package/src/styles/components/confirm-dialog.css +13 -14
- package/src/styles/components/dataframe.css +12 -4
- package/src/styles/components/date-picker.css +80 -92
- package/src/styles/components/datetime-picker.css +31 -43
- package/src/styles/components/divider.css +20 -20
- package/src/styles/components/dose-calculator.css +3 -3
- package/src/styles/components/empty-state.css +20 -17
- package/src/styles/components/experiment-code-badge.css +2 -0
- package/src/styles/components/experiment-popover.css +174 -329
- package/src/styles/components/experiment-selector-modal.css +75 -56
- package/src/styles/components/file-picker.css +3 -3
- package/src/styles/components/file-uploader.css +38 -49
- package/src/styles/components/form-builder.css +3 -3
- package/src/styles/components/formula-input.css +1 -1
- package/src/styles/components/icon-button.css +18 -24
- package/src/styles/components/input.css +5 -4
- package/src/styles/components/instrument-monitor.css +13 -15
- package/src/styles/components/jobs-status-tray.css +31 -25
- package/src/styles/components/modal.css +2 -4
- package/src/styles/components/molecule-input.css +2 -2
- package/src/styles/components/multi-select.css +65 -117
- package/src/styles/components/number-input.css +26 -19
- package/src/styles/components/pill.css +3 -2
- package/src/styles/components/plate-editor.css +0 -1
- package/src/styles/components/plugin-icon.css +26 -23
- package/src/styles/components/popover.css +2 -2
- package/src/styles/components/presence-avatars.css +1 -1
- package/src/styles/components/protocol-step-editor.css +1 -3
- package/src/styles/components/resource-card.css +2 -2
- package/src/styles/components/resource-picker-list.css +6 -57
- package/src/styles/components/sample-hierarchy-tree.css +4 -4
- package/src/styles/components/sample-selector.css +95 -79
- package/src/styles/components/schedule-calendar.css +8 -8
- package/src/styles/components/searchable-select.css +12 -12
- package/src/styles/components/section-card.css +28 -47
- package/src/styles/components/segmented-control.css +18 -15
- package/src/styles/components/select.css +3 -3
- package/src/styles/components/sequence-input.css +2 -2
- package/src/styles/components/sequence-progress-bar.css +17 -8
- package/src/styles/components/smart-group.css +18 -18
- package/src/styles/components/step-wizard.css +52 -16
- package/src/styles/components/tabs.css +2 -2
- package/src/styles/components/textarea.css +3 -3
- package/src/styles/components/time-picker.css +36 -21
- package/src/styles/components/toast.css +12 -10
- package/src/styles/components/toggle.css +1 -11
- package/src/styles/components/tooltip.css +10 -9
- package/src/styles/components/unit-input.css +8 -7
- package/src/styles/components/well-plate.css +9 -2
- package/src/styles/variables.css +106 -75
- package/src/types/componentWorkflowTypes.ts +2 -0
- package/src/types/generated/platformContext.ts +1 -0
- package/src/types/generated-ui.ts +4 -0
- package/src/utils/time.ts +21 -0
- package/dist/ActionMenu-CScINahU.js.map +0 -1
- package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
- package/dist/BaseModal-DfRIuVoj.js.map +0 -1
- package/dist/ExperimentPopover-C4sGw-is.js +0 -359
- package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
- package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
- package/dist/SettingsModal-CSLE31yg.js.map +0 -1
- package/dist/data-C2PwI_dn.js.map +0 -1
- package/dist/lab-DLwRl_Ql.js.map +0 -1
- package/dist/records-BzTcxekG.js.map +0 -1
- package/dist/runtime-D6gVat6r.js.map +0 -1
- package/dist/useListReorder-CcbEYxX-.js.map +0 -1
- package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
- package/dist/useRealtime-PalKue84.js.map +0 -1
- package/dist/useRequest-D5JDfsgZ.js +0 -76
- package/dist/useRequest-D5JDfsgZ.js.map +0 -1
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
import { ref, computed, watch, onUnmounted, useTemplateRef } from 'vue'
|
|
2
|
+
/** Top-bar control for the attached experiment: a trigger joined to an optional Save button, with a summary panel (change, open, detach). */
|
|
3
|
+
import { ref, computed, watch, nextTick, onUnmounted, useTemplateRef } from 'vue'
|
|
4
|
+
import { ArrowLeftRight, Check, ChevronDown, CircleAlert, CloudUpload, ExternalLink, Link, LoaderCircle, Unlink } from 'lucide-vue-next'
|
|
4
5
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
5
6
|
import { format as formatStatus } from '../utils/experimentStatus'
|
|
7
|
+
import { formatRelativeTime } from '../utils/time'
|
|
6
8
|
import ConfirmDialog from './ConfirmDialog.vue'
|
|
7
9
|
|
|
8
10
|
interface Props {
|
|
9
11
|
experimentName?: string
|
|
10
12
|
experimentCode?: string
|
|
11
13
|
experimentStatus?: string
|
|
14
|
+
/** Platform id of the bound experiment; enables "Open in MINT". */
|
|
15
|
+
experimentId?: number | null
|
|
16
|
+
experimentProject?: string
|
|
17
|
+
experimentOwner?: string
|
|
18
|
+
/** ISO timestamp of the last update, shown as relative time. */
|
|
19
|
+
experimentUpdatedAt?: string
|
|
12
20
|
/** An experiment is bound but the platform has not returned its record yet. */
|
|
13
21
|
isResolving?: boolean
|
|
14
22
|
/** Message explaining why the bound experiment could not be resolved. */
|
|
@@ -19,18 +27,30 @@ interface Props {
|
|
|
19
27
|
saveLoading?: boolean
|
|
20
28
|
saveSuccessMessage?: string
|
|
21
29
|
saveDisabledMessage?: string
|
|
30
|
+
saveLabel?: string
|
|
31
|
+
/** Unsaved changes: marks the Save button with a dot. */
|
|
32
|
+
dirty?: boolean
|
|
22
33
|
confirmSave?: boolean
|
|
23
34
|
confirmTitle?: string
|
|
24
35
|
confirmMessage?: string
|
|
36
|
+
/** Empty-state trigger label. */
|
|
37
|
+
placeholder?: string
|
|
38
|
+
/** The trigger name truncates at this width (px); its title shows code and name in full. */
|
|
39
|
+
maxNameWidth?: number
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
const props = withDefaults(defineProps<Props>(), {
|
|
43
|
+
experimentId: null,
|
|
28
44
|
isResolving: false,
|
|
29
45
|
showSave: false,
|
|
30
46
|
showDetach: false,
|
|
31
47
|
saveDisabled: false,
|
|
32
48
|
saveLoading: false,
|
|
49
|
+
saveLabel: 'Save',
|
|
50
|
+
dirty: false,
|
|
33
51
|
confirmSave: true,
|
|
52
|
+
placeholder: 'Attach experiment',
|
|
53
|
+
maxNameWidth: 200,
|
|
34
54
|
})
|
|
35
55
|
|
|
36
56
|
// An experiment is bound but has no name to show — either still resolving or
|
|
@@ -38,17 +58,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
38
58
|
// platform, so these states say what is happening instead of inventing a label.
|
|
39
59
|
const isUnresolved = computed(() => props.isResolving || !!props.error)
|
|
40
60
|
const hasExperiment = computed(() => !!props.experimentName || !!props.experimentCode)
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
if (props.isResolving) return 'Loading experiment…'
|
|
44
|
-
if (props.error) return 'Could not load this experiment'
|
|
45
|
-
return hasExperiment.value ? 'Linked experiment context' : 'Link to an MINT experiment'
|
|
46
|
-
})
|
|
61
|
+
const isEmpty = computed(() => !isUnresolved.value && !hasExperiment.value)
|
|
62
|
+
const hasSave = computed(() => props.showSave && !isUnresolved.value && hasExperiment.value)
|
|
47
63
|
|
|
48
64
|
const triggerLabel = computed(() => {
|
|
49
65
|
if (props.isResolving) return 'Loading experiment'
|
|
50
66
|
if (props.error) return 'Experiment unavailable'
|
|
51
|
-
return props.
|
|
67
|
+
return [props.experimentCode, props.experimentName].filter(Boolean).join(' · ') || props.placeholder
|
|
52
68
|
})
|
|
53
69
|
|
|
54
70
|
const emit = defineEmits<{
|
|
@@ -65,9 +81,38 @@ const { isOpen, close, toggle } = useDropdownState({
|
|
|
65
81
|
const showSuccess = ref(false)
|
|
66
82
|
const showConfirm = ref(false)
|
|
67
83
|
|
|
84
|
+
// The panel hangs from the group's left edge; when that would cross the
|
|
85
|
+
// viewport's right edge (a control at the end of the top bar) it hangs from the right.
|
|
86
|
+
const panel = useTemplateRef<HTMLElement>('panel')
|
|
87
|
+
const panelAlignEnd = ref(false)
|
|
88
|
+
watch(isOpen, async (open) => {
|
|
89
|
+
if (!open) return
|
|
90
|
+
panelAlignEnd.value = false
|
|
91
|
+
await nextTick()
|
|
92
|
+
const right = panel.value?.getBoundingClientRect().right ?? 0
|
|
93
|
+
panelAlignEnd.value = right > document.documentElement.clientWidth
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const saveTitle = computed(() => {
|
|
97
|
+
if (props.saveDisabled && props.saveDisabledMessage) return props.saveDisabledMessage
|
|
98
|
+
if (showSuccess.value && props.saveSuccessMessage) return props.saveSuccessMessage
|
|
99
|
+
return `${props.saveLabel} to experiment`
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// A function, not a computed: "Updated" is relative to now, so it is
|
|
103
|
+
// re-evaluated each time the panel renders.
|
|
104
|
+
function metaRows(): [string, string][] {
|
|
105
|
+
return ([
|
|
106
|
+
['Project', props.experimentProject],
|
|
107
|
+
['Status', props.experimentStatus && formatStatus(props.experimentStatus)],
|
|
108
|
+
['Owner', props.experimentOwner],
|
|
109
|
+
['Updated', props.experimentUpdatedAt && formatRelativeTime(props.experimentUpdatedAt)],
|
|
110
|
+
] as [string, string | undefined][]).filter((row): row is [string, string] => !!row[1])
|
|
111
|
+
}
|
|
112
|
+
|
|
68
113
|
function handleTriggerClick() {
|
|
69
|
-
if (
|
|
70
|
-
|
|
114
|
+
if (isEmpty.value) {
|
|
115
|
+
emit('select')
|
|
71
116
|
return
|
|
72
117
|
}
|
|
73
118
|
toggle()
|
|
@@ -80,6 +125,7 @@ function handleSelect() {
|
|
|
80
125
|
|
|
81
126
|
function handleSave() {
|
|
82
127
|
if (props.saveDisabled || props.saveLoading) return
|
|
128
|
+
close()
|
|
83
129
|
if (props.confirmSave) {
|
|
84
130
|
showConfirm.value = true
|
|
85
131
|
} else {
|
|
@@ -117,201 +163,137 @@ onUnmounted(() => {
|
|
|
117
163
|
</script>
|
|
118
164
|
|
|
119
165
|
<template>
|
|
120
|
-
<div ref="popover" class="mint-experiment-popover">
|
|
121
|
-
<!--
|
|
122
|
-
<
|
|
166
|
+
<div ref="popover" :class="['mint-experiment-popover', { 'mint-experiment-popover--save': hasSave }]">
|
|
167
|
+
<!-- Trigger: code + name when attached, opens the panel; the empty state selects directly. -->
|
|
168
|
+
<button
|
|
169
|
+
ref="trigger"
|
|
170
|
+
type="button"
|
|
123
171
|
:class="[
|
|
124
|
-
'mint-experiment-
|
|
125
|
-
{ 'mint-experiment-
|
|
172
|
+
'mint-experiment-popover__trigger',
|
|
173
|
+
{ 'mint-experiment-popover__trigger--active': isOpen },
|
|
174
|
+
{ 'mint-experiment-popover__trigger--empty': isEmpty },
|
|
175
|
+
{ 'mint-experiment-popover__trigger--loading': isResolving },
|
|
176
|
+
{ 'mint-experiment-popover__trigger--error': !isResolving && !!error },
|
|
126
177
|
]"
|
|
178
|
+
:title="triggerLabel"
|
|
179
|
+
:aria-label="isUnresolved ? triggerLabel : undefined"
|
|
180
|
+
:aria-busy="isResolving || undefined"
|
|
181
|
+
:aria-expanded="isEmpty ? undefined : isOpen"
|
|
182
|
+
@click.stop="handleTriggerClick"
|
|
127
183
|
>
|
|
128
|
-
<!--
|
|
129
|
-
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
stroke-linecap="round"
|
|
150
|
-
stroke-linejoin="round"
|
|
151
|
-
stroke-width="1.75"
|
|
152
|
-
d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
|
|
153
|
-
/>
|
|
154
|
-
</svg>
|
|
155
|
-
<!-- Resolving and error come first: with an experiment bound but no name
|
|
156
|
-
yet, "No experiment" would be a lie. -->
|
|
157
|
-
<span v-if="isResolving" class="mint-experiment-popover__trigger-skeleton" aria-hidden="true" />
|
|
158
|
-
<span v-else-if="error" class="mint-experiment-popover__trigger-text">Experiment unavailable</span>
|
|
159
|
-
<!-- Name preferred, code as fallback, "No experiment" as empty state -->
|
|
160
|
-
<span v-else-if="experimentName" class="mint-experiment-popover__trigger-text">{{ experimentName }}</span>
|
|
161
|
-
<span v-else-if="experimentCode" class="mint-experiment-popover__trigger-code">{{ experimentCode }}</span>
|
|
162
|
-
<span v-else class="mint-experiment-popover__trigger-text">No experiment</span>
|
|
163
|
-
<!-- Chevron -->
|
|
164
|
-
<svg class="mint-experiment-popover__trigger-chevron" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
165
|
-
<path d="m6 9 6 6 6-6" />
|
|
166
|
-
</svg>
|
|
167
|
-
</button>
|
|
184
|
+
<!-- Resolving and error come first: with an experiment bound but no name
|
|
185
|
+
yet, the empty placeholder would be a lie. -->
|
|
186
|
+
<span v-if="isResolving" class="mint-experiment-popover__trigger-skeleton" aria-hidden="true" />
|
|
187
|
+
<template v-else-if="error">
|
|
188
|
+
<CircleAlert class="mint-experiment-popover__trigger-icon" :size="14" aria-hidden="true" />
|
|
189
|
+
<span class="mint-experiment-popover__trigger-text">Experiment unavailable</span>
|
|
190
|
+
</template>
|
|
191
|
+
<template v-else-if="hasExperiment">
|
|
192
|
+
<span v-if="experimentCode" class="mint-exp-code mint-exp-code--sm">{{ experimentCode }}</span>
|
|
193
|
+
<span
|
|
194
|
+
v-if="experimentName"
|
|
195
|
+
class="mint-experiment-popover__trigger-text"
|
|
196
|
+
:style="{ maxWidth: `${maxNameWidth}px` }"
|
|
197
|
+
>{{ experimentName }}</span>
|
|
198
|
+
</template>
|
|
199
|
+
<template v-else>
|
|
200
|
+
<Link class="mint-experiment-popover__trigger-icon" :size="14" aria-hidden="true" />
|
|
201
|
+
<span class="mint-experiment-popover__trigger-text">{{ placeholder }}</span>
|
|
202
|
+
</template>
|
|
203
|
+
<ChevronDown v-if="!isEmpty" class="mint-experiment-popover__trigger-chevron" :size="13" aria-hidden="true" />
|
|
204
|
+
</button>
|
|
168
205
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<!-- Save icon -->
|
|
191
|
-
<svg v-else class="mint-experiment-popover__save-trigger-icon" aria-hidden="true" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
192
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
|
|
193
|
-
</svg>
|
|
194
|
-
</button>
|
|
195
|
-
</div>
|
|
206
|
+
<!-- Save: joined to the trigger at top-bar level, never inside the panel. -->
|
|
207
|
+
<button
|
|
208
|
+
v-if="hasSave"
|
|
209
|
+
type="button"
|
|
210
|
+
:class="[
|
|
211
|
+
'mint-experiment-popover__save',
|
|
212
|
+
{ 'mint-experiment-popover__save--loading': saveLoading },
|
|
213
|
+
{ 'mint-experiment-popover__save--success': showSuccess },
|
|
214
|
+
{ 'mint-experiment-popover__save--disabled': saveDisabled && !showSuccess },
|
|
215
|
+
]"
|
|
216
|
+
:disabled="saveLoading || (saveDisabled && !showSuccess)"
|
|
217
|
+
:title="saveTitle"
|
|
218
|
+
:aria-label="dirty ? `${saveTitle} (unsaved changes)` : saveTitle"
|
|
219
|
+
@click.stop="handleSave"
|
|
220
|
+
>
|
|
221
|
+
<LoaderCircle v-if="saveLoading" class="mint-experiment-popover__save-spinner" :size="14" aria-hidden="true" />
|
|
222
|
+
<Check v-else-if="showSuccess" :size="14" aria-hidden="true" />
|
|
223
|
+
<CloudUpload v-else :size="14" aria-hidden="true" />
|
|
224
|
+
<span>{{ saveLoading ? 'Saving…' : saveLabel }}</span>
|
|
225
|
+
<span v-if="dirty && !saveLoading" class="mint-experiment-popover__dirty" aria-hidden="true" />
|
|
226
|
+
</button>
|
|
196
227
|
|
|
197
|
-
<!--
|
|
198
|
-
<div
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
228
|
+
<!-- Summary panel: header, meta, then menu rows. No buttons inside. -->
|
|
229
|
+
<div
|
|
230
|
+
v-if="isOpen"
|
|
231
|
+
ref="panel"
|
|
232
|
+
:class="['mint-experiment-popover__panel', { 'mint-experiment-popover__panel--end': panelAlignEnd }]"
|
|
233
|
+
>
|
|
234
|
+
<!-- Bound, still resolving — placeholders stand in for the record. -->
|
|
235
|
+
<div v-if="isResolving" class="mint-experiment-popover__head" aria-busy="true">
|
|
236
|
+
<span class="mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code" />
|
|
237
|
+
<span class="mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name" />
|
|
205
238
|
</div>
|
|
206
239
|
|
|
207
|
-
<!-- Bound,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
stroke-linecap="round"
|
|
215
|
-
stroke-linejoin="round"
|
|
216
|
-
stroke-width="1.75"
|
|
217
|
-
d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
|
|
218
|
-
/>
|
|
219
|
-
</svg>
|
|
220
|
-
</div>
|
|
221
|
-
<div class="mint-experiment-popover__card-info">
|
|
222
|
-
<span class="mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code" />
|
|
223
|
-
<span class="mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name" />
|
|
224
|
-
<span class="mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--status" />
|
|
225
|
-
</div>
|
|
226
|
-
</div>
|
|
227
|
-
<div class="mint-experiment-popover__card-actions">
|
|
228
|
-
<button type="button" class="mint-experiment-popover__change-btn" @click="handleSelect">
|
|
229
|
-
Change
|
|
230
|
-
</button>
|
|
231
|
-
<button v-if="showDetach" type="button" class="mint-experiment-popover__detach-btn" @click="handleDetach">
|
|
232
|
-
Detach
|
|
233
|
-
</button>
|
|
234
|
-
</div>
|
|
240
|
+
<!-- Bound, resolution failed — Detach stays reachable below. -->
|
|
241
|
+
<div v-else-if="error" class="mint-experiment-popover__head mint-experiment-popover__head--error" role="alert">
|
|
242
|
+
<b class="mint-experiment-popover__name">
|
|
243
|
+
<CircleAlert :size="14" aria-hidden="true" />
|
|
244
|
+
Experiment unavailable
|
|
245
|
+
</b>
|
|
246
|
+
<span class="mint-experiment-popover__message">{{ error }}</span>
|
|
235
247
|
</div>
|
|
236
248
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<div class="mint-experiment-popover__card-icon mint-experiment-popover__card-icon--error">
|
|
242
|
-
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
243
|
-
<path
|
|
244
|
-
stroke-linecap="round"
|
|
245
|
-
stroke-linejoin="round"
|
|
246
|
-
stroke-width="1.75"
|
|
247
|
-
d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
|
|
248
|
-
/>
|
|
249
|
-
</svg>
|
|
250
|
-
</div>
|
|
251
|
-
<div class="mint-experiment-popover__card-info">
|
|
252
|
-
<div class="mint-experiment-popover__card-name">Experiment unavailable</div>
|
|
253
|
-
<div class="mint-experiment-popover__card-message">{{ error }}</div>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
<div class="mint-experiment-popover__card-actions">
|
|
257
|
-
<button type="button" class="mint-experiment-popover__change-btn" @click="handleSelect">
|
|
258
|
-
Change
|
|
259
|
-
</button>
|
|
260
|
-
<button v-if="showDetach" type="button" class="mint-experiment-popover__detach-btn" @click="handleDetach">
|
|
261
|
-
Detach
|
|
262
|
-
</button>
|
|
249
|
+
<template v-else>
|
|
250
|
+
<div class="mint-experiment-popover__head">
|
|
251
|
+
<span v-if="experimentCode" class="mint-exp-code mint-exp-code--sm">{{ experimentCode }}</span>
|
|
252
|
+
<b v-if="experimentName" class="mint-experiment-popover__name">{{ experimentName }}</b>
|
|
263
253
|
</div>
|
|
264
|
-
|
|
254
|
+
<dl v-if="metaRows().length" class="mint-experiment-popover__meta">
|
|
255
|
+
<template v-for="[label, value] in metaRows()" :key="label">
|
|
256
|
+
<dt>{{ label }}</dt>
|
|
257
|
+
<dd>{{ value }}</dd>
|
|
258
|
+
</template>
|
|
259
|
+
</dl>
|
|
260
|
+
</template>
|
|
265
261
|
|
|
266
|
-
|
|
267
|
-
<div v-else-if="!experimentName" class="mint-experiment-popover__body">
|
|
268
|
-
<button type="button" class="mint-experiment-popover__select-btn" @click="handleSelect">
|
|
269
|
-
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
270
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
|
271
|
-
</svg>
|
|
272
|
-
Select Experiment
|
|
273
|
-
</button>
|
|
274
|
-
</div>
|
|
262
|
+
<div class="mint-experiment-popover__separator" role="separator" />
|
|
275
263
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
<
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
</button>
|
|
301
|
-
<button v-if="showDetach" type="button" class="mint-experiment-popover__detach-btn" @click="handleDetach">
|
|
302
|
-
Detach
|
|
303
|
-
</button>
|
|
304
|
-
</div>
|
|
305
|
-
</div>
|
|
264
|
+
<button type="button" class="mint-experiment-popover__option" @click="handleSelect">
|
|
265
|
+
<ArrowLeftRight class="mint-experiment-popover__option-icon" :size="14" aria-hidden="true" />
|
|
266
|
+
Change experiment…
|
|
267
|
+
</button>
|
|
268
|
+
<a
|
|
269
|
+
v-if="experimentId != null"
|
|
270
|
+
class="mint-experiment-popover__option"
|
|
271
|
+
:href="`/experiments/${experimentId}`"
|
|
272
|
+
target="_blank"
|
|
273
|
+
rel="noopener"
|
|
274
|
+
@click="close()"
|
|
275
|
+
>
|
|
276
|
+
<ExternalLink class="mint-experiment-popover__option-icon" :size="14" aria-hidden="true" />
|
|
277
|
+
Open in MINT
|
|
278
|
+
</a>
|
|
279
|
+
<button
|
|
280
|
+
v-if="showDetach"
|
|
281
|
+
type="button"
|
|
282
|
+
class="mint-experiment-popover__option mint-experiment-popover__option--danger"
|
|
283
|
+
@click="handleDetach"
|
|
284
|
+
>
|
|
285
|
+
<Unlink class="mint-experiment-popover__option-icon" :size="14" aria-hidden="true" />
|
|
286
|
+
Detach
|
|
287
|
+
</button>
|
|
306
288
|
</div>
|
|
307
289
|
|
|
308
290
|
<!-- Save confirmation dialog -->
|
|
309
291
|
<ConfirmDialog
|
|
310
292
|
v-model:open="showConfirm"
|
|
311
293
|
:title="confirmTitle ?? 'Save to Experiment'"
|
|
312
|
-
:message="confirmMessage ?? `Save current data to ${experimentName}?`"
|
|
294
|
+
:message="confirmMessage ?? `Save current data to ${experimentName ?? experimentCode}?`"
|
|
313
295
|
tone="info"
|
|
314
|
-
confirm-label="
|
|
296
|
+
:confirm-label="saveLabel"
|
|
315
297
|
:loading="saveLoading"
|
|
316
298
|
@confirm="handleConfirmSave"
|
|
317
299
|
/>
|
|
@@ -10,6 +10,7 @@ const mockExperiments: ExperimentSummary[] = [
|
|
|
10
10
|
{
|
|
11
11
|
id: 1,
|
|
12
12
|
experiment_code: 'DR-HeLa-DOX-001',
|
|
13
|
+
created_by: 1,
|
|
13
14
|
name: 'Doxorubicin dose-response in HeLa cells',
|
|
14
15
|
status: 'completed',
|
|
15
16
|
experiment_type: 'dose_response',
|
|
@@ -34,6 +35,7 @@ const mockExperiments: ExperimentSummary[] = [
|
|
|
34
35
|
{
|
|
35
36
|
id: 3,
|
|
36
37
|
experiment_code: 'DR-PANC1-GEM-003',
|
|
38
|
+
created_by: 1,
|
|
37
39
|
name: 'Gemcitabine IC50 in PANC-1',
|
|
38
40
|
status: 'ongoing',
|
|
39
41
|
experiment_type: 'dose_response',
|
|
@@ -70,6 +72,7 @@ const mockExperiments: ExperimentSummary[] = [
|
|
|
70
72
|
{
|
|
71
73
|
id: 6,
|
|
72
74
|
experiment_code: 'SYN-HeLa-COMBO-006',
|
|
75
|
+
created_by: 1,
|
|
73
76
|
name: 'DOX + 5-FU synergy screen in HeLa',
|
|
74
77
|
status: 'planned',
|
|
75
78
|
experiment_type: 'synergy',
|
|
@@ -106,6 +109,7 @@ const mockExperiments: ExperimentSummary[] = [
|
|
|
106
109
|
{
|
|
107
110
|
id: 9,
|
|
108
111
|
experiment_code: 'CV-JURKAT-VIN-009',
|
|
112
|
+
created_by: 1,
|
|
109
113
|
name: 'Vincristine titration Jurkat T-cells',
|
|
110
114
|
status: 'ongoing',
|
|
111
115
|
experiment_type: 'cell_viability',
|
|
@@ -128,9 +132,9 @@ const mockExperiments: ExperimentSummary[] = [
|
|
|
128
132
|
]
|
|
129
133
|
|
|
130
134
|
const mockExperimentTypes = [
|
|
131
|
-
{
|
|
132
|
-
{
|
|
133
|
-
{
|
|
135
|
+
{ value: 'dose_response', label: 'Dose response' },
|
|
136
|
+
{ value: 'cell_viability', label: 'Cell viability' },
|
|
137
|
+
{ value: 'synergy', label: 'Synergy' },
|
|
134
138
|
]
|
|
135
139
|
|
|
136
140
|
const mockProjects = [
|
|
@@ -152,6 +156,7 @@ function applyMockFilters(
|
|
|
152
156
|
const expType = params.get('experiment_type')
|
|
153
157
|
const project = params.get('project')
|
|
154
158
|
const search = params.get('search')?.toLowerCase()
|
|
159
|
+
const mine = params.get('mine') === 'true'
|
|
155
160
|
const createdAfter = params.get('created_after')
|
|
156
161
|
const sortBy = (params.get('sort_by') ?? 'created_at') as ExperimentSortField
|
|
157
162
|
const sortOrder = params.get('sort_order') ?? 'desc'
|
|
@@ -159,6 +164,7 @@ function applyMockFilters(
|
|
|
159
164
|
if (status) filtered = filtered.filter((e) => e.status === status)
|
|
160
165
|
if (expType) filtered = filtered.filter((e) => e.experiment_type === expType)
|
|
161
166
|
if (project) filtered = filtered.filter((e) => e.project === project)
|
|
167
|
+
if (mine) filtered = filtered.filter((e) => e.created_by === 1)
|
|
162
168
|
if (search) {
|
|
163
169
|
filtered = filtered.filter(
|
|
164
170
|
(e) =>
|
|
@@ -208,7 +214,24 @@ onMounted(async () => {
|
|
|
208
214
|
if (url.includes('/projects')) {
|
|
209
215
|
config.adapter = () =>
|
|
210
216
|
Promise.resolve({
|
|
211
|
-
data: mockProjects,
|
|
217
|
+
data: { projects: mockProjects, total: mockProjects.length },
|
|
218
|
+
status: 200,
|
|
219
|
+
statusText: 'OK',
|
|
220
|
+
headers: {},
|
|
221
|
+
config,
|
|
222
|
+
})
|
|
223
|
+
return config
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Status counts: the list filters without status (the route never receives one).
|
|
227
|
+
if (/^(\/api)?\/experiments\/stats(\?|$)/.test(url)) {
|
|
228
|
+
const params = new URLSearchParams(url.split('?')[1] ?? '')
|
|
229
|
+
const filtered = applyMockFilters(mockExperiments, params)
|
|
230
|
+
const by_status: Record<string, number> = { planned: 0, ongoing: 0, completed: 0, cancelled: 0 }
|
|
231
|
+
for (const e of filtered) by_status[e.status] = (by_status[e.status] ?? 0) + 1
|
|
232
|
+
config.adapter = () =>
|
|
233
|
+
Promise.resolve({
|
|
234
|
+
data: { total: filtered.length, by_status },
|
|
212
235
|
status: 200,
|
|
213
236
|
statusText: 'OK',
|
|
214
237
|
headers: {},
|
|
@@ -246,9 +269,8 @@ onMounted(async () => {
|
|
|
246
269
|
// -- Story state --
|
|
247
270
|
const isOpen = ref(false)
|
|
248
271
|
const isOpenEmpty = ref(false)
|
|
249
|
-
const isOpenGrouped = ref(false)
|
|
250
272
|
const isOpenWithSelection = ref(false)
|
|
251
|
-
const
|
|
273
|
+
const isOpenFilters = ref(false)
|
|
252
274
|
|
|
253
275
|
// The modal commits to the shared store rather than emitting; stories read the
|
|
254
276
|
// selection back from the same place a plugin would.
|
|
@@ -262,7 +284,7 @@ const selectedExperiment = computed(() => experimentStore.current)
|
|
|
262
284
|
<template #default="{ state }">
|
|
263
285
|
<div style="padding: 2rem;">
|
|
264
286
|
<button
|
|
265
|
-
style="padding: 0.5rem 1rem; background: var(--color-primary
|
|
287
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary); color: var(--text-on-accent); border: none; border-radius: var(--radius-md); cursor: pointer; font-size: 0.875rem;"
|
|
266
288
|
@click="isOpen = true"
|
|
267
289
|
>
|
|
268
290
|
Open Experiment Selector
|
|
@@ -287,6 +309,7 @@ const selectedExperiment = computed(() => experimentStore.current)
|
|
|
287
309
|
v-model:open="isOpen"
|
|
288
310
|
:experiment-type="state.experimentType || undefined"
|
|
289
311
|
:title="state.title"
|
|
312
|
+
:size="state.size || undefined"
|
|
290
313
|
:current-experiment-id="selectedExperiment?.id ?? null"
|
|
291
314
|
/>
|
|
292
315
|
</div>
|
|
@@ -294,47 +317,28 @@ const selectedExperiment = computed(() => experimentStore.current)
|
|
|
294
317
|
|
|
295
318
|
<template #controls="{ state }">
|
|
296
319
|
<HstCheckbox v-model="isOpen" title="Open" />
|
|
320
|
+
<HstSelect v-model="state.size" title="Size (default lg)" :options="['', 'md', 'lg', 'xl', 'full']" />
|
|
297
321
|
<HstText v-model="state.title" title="Title" />
|
|
298
322
|
<HstText v-model="state.experimentType" title="Experiment Type" />
|
|
299
323
|
</template>
|
|
300
324
|
</Variant>
|
|
301
325
|
|
|
302
|
-
<Variant title="
|
|
326
|
+
<Variant title="Active Filters">
|
|
303
327
|
<div style="padding: 2rem;">
|
|
304
|
-
<p style="font-size: 0.8125rem; color: var(--text-muted
|
|
305
|
-
|
|
328
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted); margin: 0 0 1rem;">
|
|
329
|
+
Pick a status (each option shows its count), pick a project or press Mine: the trigger
|
|
330
|
+
fills with the accent tint and "Clear N" appears. Mine keeps the experiments of user 1.
|
|
306
331
|
</p>
|
|
307
332
|
<button
|
|
308
|
-
style="padding: 0.5rem 1rem; background: var(--color-primary
|
|
309
|
-
@click="
|
|
333
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary); color: var(--text-on-accent); border: none; border-radius: var(--radius-md); cursor: pointer; font-size: 0.875rem;"
|
|
334
|
+
@click="isOpenFilters = true"
|
|
310
335
|
>
|
|
311
336
|
Open With Filters
|
|
312
337
|
</button>
|
|
313
338
|
|
|
314
339
|
<ExperimentSelectorModal
|
|
315
|
-
v-model:open="
|
|
316
|
-
title="Select Experiment"
|
|
317
|
-
:show-filters="true"
|
|
318
|
-
/>
|
|
319
|
-
</div>
|
|
320
|
-
</Variant>
|
|
321
|
-
|
|
322
|
-
<Variant title="Grouped by Project">
|
|
323
|
-
<div style="padding: 2rem;">
|
|
324
|
-
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
325
|
-
Opens with group-by-project enabled by default.
|
|
326
|
-
</p>
|
|
327
|
-
<button
|
|
328
|
-
style="padding: 0.5rem 1rem; background: var(--color-primary, #3b82f6); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
329
|
-
@click="isOpenGrouped = true"
|
|
330
|
-
>
|
|
331
|
-
Open Grouped Selector
|
|
332
|
-
</button>
|
|
333
|
-
|
|
334
|
-
<ExperimentSelectorModal
|
|
335
|
-
v-model:open="isOpenGrouped"
|
|
340
|
+
v-model:open="isOpenFilters"
|
|
336
341
|
title="Select Experiment"
|
|
337
|
-
:group-by-project="true"
|
|
338
342
|
/>
|
|
339
343
|
</div>
|
|
340
344
|
</Variant>
|
|
@@ -345,7 +349,7 @@ const selectedExperiment = computed(() => experimentStore.current)
|
|
|
345
349
|
Shows the "Clear selection" footer when an experiment is selected.
|
|
346
350
|
</p>
|
|
347
351
|
<button
|
|
348
|
-
style="padding: 0.5rem 1rem; background: var(--color-primary
|
|
352
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary); color: var(--text-on-accent); border: none; border-radius: var(--radius-md); cursor: pointer; font-size: 0.875rem;"
|
|
349
353
|
@click="isOpenWithSelection = true"
|
|
350
354
|
>
|
|
351
355
|
Open Selector (with selection)
|
|
@@ -365,7 +369,7 @@ const selectedExperiment = computed(() => experimentStore.current)
|
|
|
365
369
|
Filters to a non-existent experiment type, showing the empty state.
|
|
366
370
|
</p>
|
|
367
371
|
<button
|
|
368
|
-
style="padding: 0.5rem 1rem; background: var(--color-primary
|
|
372
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary); color: var(--text-on-accent); border: none; border-radius: var(--radius-md); cursor: pointer; font-size: 0.875rem;"
|
|
369
373
|
@click="isOpenEmpty = true"
|
|
370
374
|
>
|
|
371
375
|
Open Experiment Selector (Empty)
|