@morscherlab/mint-sdk 1.0.0-beta.3 → 1.0.0-beta.5
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 +9 -2
- package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
- package/dist/__tests__/composables/useApi.test.d.ts +1 -0
- package/dist/components/AppContainer.vue.d.ts +1 -1
- package/dist/components/AppLayout.vue.d.ts +20 -1
- package/dist/components/AppSidebar.vue.d.ts +57 -5
- package/dist/components/AppTopBar.vue.d.ts +7 -25
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +3 -1
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -0
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +5 -0
- package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
- package/dist/components/ControlWorkspaceView.vue.d.ts +24 -7
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
- package/dist/components/FormBuilder.vue.d.ts +9 -9
- package/dist/components/PlateMapEditor.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/WellPlate.vue.d.ts +2 -2
- package/dist/components/index.d.ts +3 -12
- package/dist/components/index.js +3 -3
- package/dist/components/{AppPageSelector.vue.d.ts → internal/AppTopBarPageSelectorInternal.vue.d.ts} +1 -1
- package/dist/components/{AppPillNav.vue.d.ts → internal/AppTopBarPillNavInternal.vue.d.ts} +3 -1
- package/dist/components/{CalendarGridPanel.vue.d.ts → internal/CalendarGridPanelInternal.vue.d.ts} +1 -1
- package/dist/components/internal/FormSectionRenderer.vue.d.ts +4 -4
- package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
- package/dist/{components-D_Sr0adg.js → components-DihbSJjU.js} +5932 -5408
- package/dist/components-DihbSJjU.js.map +1 -0
- package/dist/composables/experiment-utils.d.ts +8 -0
- package/dist/composables/index.d.ts +5 -7
- package/dist/composables/index.js +4 -4
- package/dist/composables/useAppExperiment.d.ts +31 -2
- package/dist/composables/useBioTemplateComponents.d.ts +5 -3
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +3 -2
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +6 -5
- package/dist/composables/useBioTemplateWorkspace.d.ts +5 -4
- package/dist/composables/useControlSchema.d.ts +43 -21
- package/dist/composables/usePluginClient.d.ts +5 -2
- package/dist/{composables-C3dpXQN5.js → composables-BcgZ6diz.js} +40 -28
- package/dist/composables-BcgZ6diz.js.map +1 -0
- package/dist/index.d.ts +5 -12
- package/dist/index.js +5 -5
- package/dist/install.js +2 -2
- package/dist/styles.css +5637 -5663
- package/dist/templates/adapters.d.ts +7 -1
- package/dist/templates/catalog.d.ts +5 -5
- package/dist/templates/componentBindings.d.ts +13 -0
- package/dist/templates/index.d.ts +5 -5
- package/dist/templates/index.js +2 -2
- package/dist/templates/presets.d.ts +4 -4
- package/dist/templates/types.d.ts +4 -1
- package/dist/{templates-50NPjaxL.js → templates-Cyt0Suwf.js} +322 -73
- package/dist/templates-Cyt0Suwf.js.map +1 -0
- package/dist/types/components.d.ts +6 -25
- package/dist/types/index.d.ts +1 -1
- package/dist/{useScheduleDrag-D4oWdh41.js → useExperimentData-CM6Y0u5L.js} +400 -357
- package/dist/useExperimentData-CM6Y0u5L.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionItem.test.ts +6 -6
- package/src/__tests__/components/AppLayout.test.ts +44 -0
- package/src/__tests__/components/AppSidebar.test.ts +130 -2
- package/src/__tests__/components/AppToastContainer.test.ts +0 -11
- package/src/__tests__/components/AppTopBar.test.ts +189 -120
- package/src/__tests__/components/{AppPageSelector.test.ts → AppTopBarPageSelector.test.ts} +8 -8
- package/src/__tests__/components/{AppPillNav.test.ts → AppTopBarPillNav.test.ts} +53 -6
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +7 -1
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +32 -1
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +48 -1
- package/src/__tests__/components/BioTemplateRenderer.test.ts +25 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +3 -3
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +278 -0
- package/src/__tests__/components/ControlWorkspaceView.test.ts +134 -63
- package/src/__tests__/components/DateTimePicker.test.ts +2 -2
- package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
- package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
- package/src/__tests__/composables/experiment-utils.test.ts +30 -0
- package/src/__tests__/composables/useApi.test.ts +30 -0
- package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +7 -4
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +7 -7
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +6 -1
- package/src/__tests__/composables/useControlSchema.test.ts +151 -37
- package/src/__tests__/composables/usePluginClient.test.ts +99 -2
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +120 -25
- package/src/__tests__/templates/templates.test.ts +56 -0
- package/src/components/AppAvatarMenu.vue +3 -3
- package/src/components/AppLayout.story.vue +39 -0
- package/src/components/AppLayout.vue +83 -2
- package/src/components/AppPluginSwitcher.vue +5 -5
- package/src/components/AppSidebar.story.vue +113 -5
- package/src/components/AppSidebar.vue +147 -27
- package/src/components/AppTopBar.story.vue +2 -5
- package/src/components/AppTopBar.vue +35 -425
- package/src/components/BioTemplateExperimentWorkspaceView.story.vue +2 -2
- package/src/components/BioTemplateExperimentWorkspaceView.vue +6 -0
- package/src/components/BioTemplatePackWorkspaceView.story.vue +4 -4
- package/src/components/BioTemplatePackWorkspaceView.vue +1 -0
- package/src/components/BioTemplatePresetWorkspaceView.story.vue +14 -2
- package/src/components/BioTemplatePresetWorkspaceView.vue +12 -3
- package/src/components/BioTemplateRenderer.story.vue +2 -2
- package/src/components/BioTemplateRenderer.vue +15 -227
- package/src/components/ComponentBindingRenderer.story.vue +87 -0
- package/src/components/ComponentBindingRenderer.vue +317 -0
- package/src/components/ControlWorkspaceView.story.vue +20 -9
- package/src/components/ControlWorkspaceView.vue +43 -12
- package/src/components/DatePicker.vue +2 -2
- package/src/components/DateTimePicker.vue +2 -2
- package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
- package/src/components/DoseDesignWorkspaceView.vue +255 -0
- package/src/components/ExperimentPopover.story.vue +2 -2
- package/src/components/ExperimentPopover.vue +2 -6
- package/src/components/ExperimentSelectorModal.vue +6 -5
- package/src/components/FormBuilder.story.vue +190 -0
- package/src/components/PluginWorkspaceView.story.vue +334 -0
- package/src/components/PluginWorkspaceView.vue +708 -0
- package/src/components/SettingsModal.story.vue +87 -0
- package/src/components/WellPlate.vue +2 -2
- package/src/components/index.ts +3 -12
- package/src/components/{AppPageSelector.vue → internal/AppTopBarPageSelectorInternal.vue} +9 -9
- package/src/components/internal/AppTopBarPillNavInternal.vue +194 -0
- package/src/components/{CalendarGridPanel.vue → internal/CalendarGridPanelInternal.vue} +1 -1
- package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +3 -3
- package/src/composables/experiment-utils.ts +26 -0
- package/src/composables/index.ts +21 -7
- package/src/composables/useApi.ts +9 -2
- package/src/composables/useAppExperiment.ts +85 -13
- package/src/composables/useBioTemplateComponents.ts +12 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +6 -2
- package/src/composables/useBioTemplatePresetWorkspace.ts +10 -21
- package/src/composables/useBioTemplateWorkspace.ts +6 -4
- package/src/composables/useControlSchema.ts +157 -69
- package/src/composables/usePluginClient.ts +50 -9
- package/src/index.ts +6 -563
- package/src/styles/components/app-layout.css +82 -0
- package/src/styles/components/app-page-selector.css +1 -1
- package/src/styles/components/app-pill-nav.css +71 -1
- package/src/styles/components/app-sidebar.css +119 -0
- package/src/styles/components/app-top-bar.css +0 -235
- package/src/styles/components/experiment-popover.css +2 -2
- package/src/styles/index.css +0 -1
- package/src/templates/adapters.ts +193 -0
- package/src/templates/catalog.ts +5 -5
- package/src/templates/componentBindings.ts +90 -3
- package/src/templates/index.ts +10 -0
- package/src/templates/packs.ts +10 -1
- package/src/templates/presets.ts +14 -4
- package/src/templates/types.ts +4 -0
- package/src/types/components.ts +6 -31
- package/src/types/index.ts +2 -6
- package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
- package/dist/components/FormFieldRenderer.vue.d.ts +0 -28
- package/dist/components/FormSection.vue.d.ts +0 -30
- package/dist/components/GroupingModal.vue.d.ts +0 -12
- package/dist/components/SettingsButton.vue.d.ts +0 -30
- package/dist/components/ToastNotification.vue.d.ts +0 -2
- package/dist/components-D_Sr0adg.js.map +0 -1
- package/dist/composables/usePluginApi.d.ts +0 -22
- package/dist/composables-C3dpXQN5.js.map +0 -1
- package/dist/templates-50NPjaxL.js.map +0 -1
- package/dist/useScheduleDrag-D4oWdh41.js.map +0 -1
- package/src/__tests__/components/FormCompatibility.test.ts +0 -94
- package/src/__tests__/components/GroupingModal.test.ts +0 -73
- package/src/__tests__/components/SettingsButton.test.ts +0 -44
- package/src/__tests__/composables/usePluginApi.test.ts +0 -81
- package/src/components/AppPillNav.vue +0 -71
- package/src/components/FormFieldRenderer.vue +0 -35
- package/src/components/FormSection.vue +0 -37
- package/src/components/GroupingModal.story.vue +0 -52
- package/src/components/GroupingModal.vue +0 -61
- package/src/components/SettingsButton.story.vue +0 -58
- package/src/components/SettingsButton.vue +0 -64
- package/src/components/ToastNotification.vue +0 -9
- package/src/composables/usePluginApi.ts +0 -32
- package/src/styles/components/settings-button.css +0 -31
- /package/dist/__tests__/components/{AppPageSelector.test.d.ts → AppTopBarPageSelector.test.d.ts} +0 -0
- /package/dist/__tests__/components/{AppPillNav.test.d.ts → AppTopBarPillNav.test.d.ts} +0 -0
- /package/dist/__tests__/components/{FormCompatibility.test.d.ts → ComponentBindingRenderer.test.d.ts} +0 -0
- /package/dist/__tests__/components/{GroupingModal.test.d.ts → DoseDesignWorkspaceView.test.d.ts} +0 -0
- /package/dist/__tests__/components/{SettingsButton.test.d.ts → PluginWorkspaceView.test.d.ts} +0 -0
- /package/dist/components/{ActionItem.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +0 -0
- /package/src/components/{ActionItem.vue → internal/ActionItemInternal.vue} +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design. */
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import type { TopBarVariant } from '../types'
|
|
5
|
+
import type { FormEnhancements } from '../types/form-builder'
|
|
6
|
+
import type {
|
|
7
|
+
ControlModel,
|
|
8
|
+
ControlModelBinding,
|
|
9
|
+
ControlSchema,
|
|
10
|
+
ControlWorkspaceOptions,
|
|
11
|
+
DoseDesignControlModelOptions,
|
|
12
|
+
UseControlWorkspaceReturn,
|
|
13
|
+
} from '../composables/useControlSchema'
|
|
14
|
+
import { defineDoseDesignControlModel } from '../composables/useControlSchema'
|
|
15
|
+
import ControlWorkspaceView from './ControlWorkspaceView.vue'
|
|
16
|
+
import DoseCalculator from './DoseCalculator.vue'
|
|
17
|
+
import WellPlate from './WellPlate.vue'
|
|
18
|
+
|
|
19
|
+
type DoseDesignSidebarVariant = 'default' | 'analysis'
|
|
20
|
+
type ComponentProps = Record<string, unknown>
|
|
21
|
+
type ComponentPropsById = Record<string, ComponentProps>
|
|
22
|
+
type ComponentBinding = { id: string, component: string, props: ComponentProps }
|
|
23
|
+
type ComponentBindingsById = Record<string, ComponentBinding>
|
|
24
|
+
type ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>
|
|
25
|
+
|
|
26
|
+
interface DoseDesignWorkspaceSlotProps {
|
|
27
|
+
workspace: ResolvedControlWorkspace
|
|
28
|
+
bindings: ResolvedControlWorkspace['bindings']
|
|
29
|
+
values: ResolvedControlWorkspace['values']
|
|
30
|
+
componentBindings: ComponentBinding[]
|
|
31
|
+
componentBindingsById: ComponentBindingsById
|
|
32
|
+
componentPropsById: ComponentPropsById
|
|
33
|
+
wellPlateProps: ComponentProps
|
|
34
|
+
doseCalculatorProps: ComponentProps
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface Props {
|
|
38
|
+
/** Model returned by defineDoseDesignControlModel(), or a custom compatible ControlWorkspace model. */
|
|
39
|
+
model?: ControlModel | ControlModelBinding
|
|
40
|
+
/** Workspace returned by useControlWorkspace(). Use for full manual control. */
|
|
41
|
+
workspace?: UseControlWorkspaceReturn<ControlSchema>
|
|
42
|
+
/** Options used when this view creates the default dose-design model internally. */
|
|
43
|
+
doseDesignOptions?: DoseDesignControlModelOptions
|
|
44
|
+
/** Options passed to the internally generated ControlWorkspaceView workspace. */
|
|
45
|
+
controlOptions?: ControlWorkspaceOptions
|
|
46
|
+
/** Initial values for the internally generated workspace. */
|
|
47
|
+
initialValues?: Record<string, unknown>
|
|
48
|
+
/** External values for the internally generated workspace. Supports default v-model. */
|
|
49
|
+
modelValue?: Record<string, unknown>
|
|
50
|
+
/** External values for the internally generated workspace. Supports v-model:values. */
|
|
51
|
+
values?: Record<string, unknown>
|
|
52
|
+
/** Named component props id for the generated WellPlate binding. */
|
|
53
|
+
plateId?: string
|
|
54
|
+
/** Named component props id for the generated DoseCalculator binding. */
|
|
55
|
+
doseId?: string
|
|
56
|
+
/** Extra props merged into the generated WellPlate binding. */
|
|
57
|
+
wellPlateProps?: ComponentProps
|
|
58
|
+
/** Extra props merged into the generated DoseCalculator binding. */
|
|
59
|
+
doseCalculatorProps?: ComponentProps
|
|
60
|
+
/** AppTopBar title. */
|
|
61
|
+
title?: string
|
|
62
|
+
/** AppTopBar subtitle. */
|
|
63
|
+
subtitle?: string
|
|
64
|
+
/** AppTopBar visual variant. */
|
|
65
|
+
topBarVariant?: TopBarVariant
|
|
66
|
+
/** AppSidebar/AppLayout sidebar width. */
|
|
67
|
+
sidebarWidth?: string
|
|
68
|
+
/** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */
|
|
69
|
+
sidebarVariant?: DoseDesignSidebarVariant
|
|
70
|
+
/** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */
|
|
71
|
+
responsiveSidebar?: boolean
|
|
72
|
+
/** Sidebar position in AppLayout. */
|
|
73
|
+
sidebarPosition?: 'left' | 'right'
|
|
74
|
+
/** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */
|
|
75
|
+
sidebarTitle?: string
|
|
76
|
+
/** Optional AppSidebar chrome subtitle for active experiment/run context. */
|
|
77
|
+
sidebarSubtitle?: string
|
|
78
|
+
/** Optional compact badge/count rendered in the AppSidebar chrome header. */
|
|
79
|
+
sidebarBadge?: string | number
|
|
80
|
+
/** Floating AppLayout style. */
|
|
81
|
+
floating?: boolean
|
|
82
|
+
/** Compact AppSidebar density. */
|
|
83
|
+
dense?: boolean
|
|
84
|
+
/** Whether AppTopBar should show generated settings. */
|
|
85
|
+
showSettings?: boolean
|
|
86
|
+
/** Render FormBuilder actions in the default generated forms. */
|
|
87
|
+
showFormActions?: boolean
|
|
88
|
+
/** Runtime FormBuilder enhancements passed to generated forms. */
|
|
89
|
+
formEnhancements?: FormEnhancements<Record<string, unknown>>
|
|
90
|
+
/** Loading/saving state passed to generated forms. */
|
|
91
|
+
formLoading?: boolean
|
|
92
|
+
/** Disabled state passed to generated forms. */
|
|
93
|
+
formDisabled?: boolean
|
|
94
|
+
/** Readonly state passed to generated forms. */
|
|
95
|
+
formReadonly?: boolean
|
|
96
|
+
/** FormBuilder size in generated forms. */
|
|
97
|
+
formSize?: 'sm' | 'md' | 'lg'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
101
|
+
model: undefined,
|
|
102
|
+
workspace: undefined,
|
|
103
|
+
doseDesignOptions: () => ({}),
|
|
104
|
+
controlOptions: () => ({}),
|
|
105
|
+
initialValues: undefined,
|
|
106
|
+
modelValue: undefined,
|
|
107
|
+
values: undefined,
|
|
108
|
+
plateId: undefined,
|
|
109
|
+
doseId: undefined,
|
|
110
|
+
wellPlateProps: () => ({}),
|
|
111
|
+
doseCalculatorProps: () => ({}),
|
|
112
|
+
title: 'Dose Design Workspace',
|
|
113
|
+
subtitle: undefined,
|
|
114
|
+
topBarVariant: 'card',
|
|
115
|
+
sidebarWidth: '320px',
|
|
116
|
+
sidebarVariant: 'analysis',
|
|
117
|
+
responsiveSidebar: true,
|
|
118
|
+
sidebarPosition: 'left',
|
|
119
|
+
sidebarTitle: undefined,
|
|
120
|
+
sidebarSubtitle: undefined,
|
|
121
|
+
sidebarBadge: undefined,
|
|
122
|
+
floating: false,
|
|
123
|
+
dense: true,
|
|
124
|
+
showSettings: true,
|
|
125
|
+
showFormActions: false,
|
|
126
|
+
formEnhancements: undefined,
|
|
127
|
+
formLoading: false,
|
|
128
|
+
formDisabled: false,
|
|
129
|
+
formReadonly: false,
|
|
130
|
+
formSize: 'md',
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const emit = defineEmits<{
|
|
134
|
+
/** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */
|
|
135
|
+
'update:modelValue': [values: Record<string, unknown>]
|
|
136
|
+
/** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */
|
|
137
|
+
'update:values': [values: Record<string, unknown>]
|
|
138
|
+
/** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */
|
|
139
|
+
submit: [values: Record<string, unknown>]
|
|
140
|
+
/** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */
|
|
141
|
+
cancel: []
|
|
142
|
+
}>()
|
|
143
|
+
|
|
144
|
+
defineSlots<{
|
|
145
|
+
default?: (props: DoseDesignWorkspaceSlotProps) => unknown
|
|
146
|
+
plate?: (props: DoseDesignWorkspaceSlotProps) => unknown
|
|
147
|
+
dose?: (props: DoseDesignWorkspaceSlotProps) => unknown
|
|
148
|
+
}>()
|
|
149
|
+
|
|
150
|
+
const resolvedModel = computed<ControlModel | ControlModelBinding>(() =>
|
|
151
|
+
props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
function resolvedPlateId(): string {
|
|
155
|
+
return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolvedDoseId(): string {
|
|
159
|
+
return props.doseId ?? props.doseDesignOptions.componentProps?.doseId ?? 'dose'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function wellPlateBinding(componentPropsById: ComponentPropsById): ComponentProps {
|
|
163
|
+
return {
|
|
164
|
+
size: 'fill',
|
|
165
|
+
selectionMode: 'multiple',
|
|
166
|
+
...(componentPropsById[resolvedPlateId()] ?? {}),
|
|
167
|
+
...props.wellPlateProps,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function doseCalculatorBinding(componentPropsById: ComponentPropsById): ComponentProps {
|
|
172
|
+
return {
|
|
173
|
+
...(componentPropsById[resolvedDoseId()] ?? {}),
|
|
174
|
+
...props.doseCalculatorProps,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function slotProps(slotProps: {
|
|
179
|
+
workspace: ResolvedControlWorkspace
|
|
180
|
+
bindings: ResolvedControlWorkspace['bindings']
|
|
181
|
+
values: ResolvedControlWorkspace['values']
|
|
182
|
+
componentBindings: ComponentBinding[]
|
|
183
|
+
componentBindingsById: ComponentBindingsById
|
|
184
|
+
componentPropsById: ComponentPropsById
|
|
185
|
+
}): DoseDesignWorkspaceSlotProps {
|
|
186
|
+
return {
|
|
187
|
+
...slotProps,
|
|
188
|
+
wellPlateProps: wellPlateBinding(slotProps.componentPropsById),
|
|
189
|
+
doseCalculatorProps: doseCalculatorBinding(slotProps.componentPropsById),
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
</script>
|
|
193
|
+
|
|
194
|
+
<template>
|
|
195
|
+
<ControlWorkspaceView
|
|
196
|
+
:model="resolvedModel"
|
|
197
|
+
:workspace="workspace"
|
|
198
|
+
:control-options="controlOptions"
|
|
199
|
+
:initial-values="initialValues"
|
|
200
|
+
:model-value="modelValue"
|
|
201
|
+
:values="values"
|
|
202
|
+
:title="title"
|
|
203
|
+
:subtitle="subtitle"
|
|
204
|
+
:top-bar-variant="topBarVariant"
|
|
205
|
+
:sidebar-width="sidebarWidth"
|
|
206
|
+
:sidebar-variant="sidebarVariant"
|
|
207
|
+
:responsive-sidebar="responsiveSidebar"
|
|
208
|
+
:sidebar-position="sidebarPosition"
|
|
209
|
+
:sidebar-title="sidebarTitle"
|
|
210
|
+
:sidebar-subtitle="sidebarSubtitle"
|
|
211
|
+
:sidebar-badge="sidebarBadge"
|
|
212
|
+
:floating="floating"
|
|
213
|
+
:dense="dense"
|
|
214
|
+
:show-settings="showSettings"
|
|
215
|
+
:show-form-actions="showFormActions"
|
|
216
|
+
:form-enhancements="formEnhancements"
|
|
217
|
+
:form-loading="formLoading"
|
|
218
|
+
:form-disabled="formDisabled"
|
|
219
|
+
:form-readonly="formReadonly"
|
|
220
|
+
:form-size="formSize"
|
|
221
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
222
|
+
@update:values="emit('update:values', $event)"
|
|
223
|
+
@submit="emit('submit', $event)"
|
|
224
|
+
@cancel="emit('cancel')"
|
|
225
|
+
>
|
|
226
|
+
<template #default="workspaceSlotProps">
|
|
227
|
+
<slot v-bind="slotProps(workspaceSlotProps)">
|
|
228
|
+
<div class="mint-dose-design-workspace">
|
|
229
|
+
<slot name="plate" v-bind="slotProps(workspaceSlotProps)">
|
|
230
|
+
<WellPlate v-bind="wellPlateBinding(workspaceSlotProps.componentPropsById)" />
|
|
231
|
+
</slot>
|
|
232
|
+
<slot name="dose" v-bind="slotProps(workspaceSlotProps)">
|
|
233
|
+
<DoseCalculator v-bind="doseCalculatorBinding(workspaceSlotProps.componentPropsById)" />
|
|
234
|
+
</slot>
|
|
235
|
+
</div>
|
|
236
|
+
</slot>
|
|
237
|
+
</template>
|
|
238
|
+
</ControlWorkspaceView>
|
|
239
|
+
</template>
|
|
240
|
+
|
|
241
|
+
<style scoped>
|
|
242
|
+
.mint-dose-design-workspace {
|
|
243
|
+
display: grid;
|
|
244
|
+
grid-template-columns: minmax(360px, 1fr) minmax(300px, 420px);
|
|
245
|
+
gap: 1rem;
|
|
246
|
+
align-items: start;
|
|
247
|
+
min-width: 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@media (max-width: 900px) {
|
|
251
|
+
.mint-dose-design-workspace {
|
|
252
|
+
grid-template-columns: minmax(0, 1fr);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
</style>
|
|
@@ -283,7 +283,7 @@ const STATUSES = ['planned', 'ongoing', 'completed', 'ready_to_extract', 'proces
|
|
|
283
283
|
</div>
|
|
284
284
|
</div>
|
|
285
285
|
<p style="margin-top: 1rem; font-size: 0.75rem; color: var(--text-muted); text-align: center;">
|
|
286
|
-
Same 2.375rem height and refresh-design motion as AppPluginSwitcher,
|
|
286
|
+
Same 2.375rem height and refresh-design motion as AppPluginSwitcher, AppTopBar page selector, AppAvatarMenu.
|
|
287
287
|
</p>
|
|
288
288
|
</div>
|
|
289
289
|
</Variant>
|
|
@@ -363,7 +363,7 @@ optionally pairs with an inline Save button as a split unit.
|
|
|
363
363
|
|
|
364
364
|
### Refresh-design alignment
|
|
365
365
|
|
|
366
|
-
- **Height** `2.375rem` — matches `AppPluginSwitcher` / `
|
|
366
|
+
- **Height** `2.375rem` — matches `AppPluginSwitcher` / `AppTopBar page selector` so the
|
|
367
367
|
topbar's right cluster reads at one optical baseline.
|
|
368
368
|
- **Surface** `--bg-card` + `--border-color` — consistent with the white-card +
|
|
369
369
|
border language.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** Floating popover showing the active experiment with save, detach, and select actions. */
|
|
3
3
|
import { ref, watch, onUnmounted } from 'vue'
|
|
4
4
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
5
|
+
import { formatExperimentStatus } from '../composables/experiment-utils'
|
|
5
6
|
import ConfirmDialog from './ConfirmDialog.vue'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
@@ -80,11 +81,6 @@ watch(() => props.saveSuccessMessage, (msg) => {
|
|
|
80
81
|
onUnmounted(() => {
|
|
81
82
|
if (successTimer) clearTimeout(successTimer)
|
|
82
83
|
})
|
|
83
|
-
|
|
84
|
-
// Format status for display (e.g., "ready_to_extract" -> "Ready to extract")
|
|
85
|
-
function formatStatus(status: string): string {
|
|
86
|
-
return status.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase())
|
|
87
|
-
}
|
|
88
84
|
</script>
|
|
89
85
|
|
|
90
86
|
<template>
|
|
@@ -191,7 +187,7 @@ function formatStatus(status: string): string {
|
|
|
191
187
|
<span v-if="experimentCode" class="mint-experiment-popover__card-code">{{ experimentCode }}</span>
|
|
192
188
|
<div class="mint-experiment-popover__card-name">{{ experimentName }}</div>
|
|
193
189
|
<div v-if="experimentStatus" class="mint-experiment-popover__card-status">
|
|
194
|
-
{{
|
|
190
|
+
{{ formatExperimentStatus(experimentStatus) }}
|
|
195
191
|
</div>
|
|
196
192
|
</div>
|
|
197
193
|
</div>
|
|
@@ -5,8 +5,9 @@ import type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'
|
|
|
5
5
|
import { useExperimentSelector } from '../composables/useExperimentSelector'
|
|
6
6
|
import {
|
|
7
7
|
formatExperimentDate,
|
|
8
|
+
formatExperimentStatus,
|
|
9
|
+
getExperimentStatusVariant,
|
|
8
10
|
EXPERIMENT_STATUS_OPTIONS,
|
|
9
|
-
EXPERIMENT_STATUS_VARIANT_MAP,
|
|
10
11
|
DATE_PRESET_OPTIONS,
|
|
11
12
|
SORT_OPTIONS,
|
|
12
13
|
} from '../composables/experiment-utils'
|
|
@@ -326,8 +327,8 @@ watch(
|
|
|
326
327
|
<span>{{ formatExperimentDate(exp.created_at) }}</span>
|
|
327
328
|
</div>
|
|
328
329
|
</div>
|
|
329
|
-
<BasePill :variant="
|
|
330
|
-
{{ exp.status }}
|
|
330
|
+
<BasePill :variant="getExperimentStatusVariant(exp.status)" size="sm">
|
|
331
|
+
{{ formatExperimentStatus(exp.status) }}
|
|
331
332
|
</BasePill>
|
|
332
333
|
</div>
|
|
333
334
|
</template>
|
|
@@ -362,8 +363,8 @@ watch(
|
|
|
362
363
|
<span>{{ formatExperimentDate(exp.created_at) }}</span>
|
|
363
364
|
</div>
|
|
364
365
|
</div>
|
|
365
|
-
<BasePill :variant="
|
|
366
|
-
{{ exp.status }}
|
|
366
|
+
<BasePill :variant="getExperimentStatusVariant(exp.status)" size="sm">
|
|
367
|
+
{{ formatExperimentStatus(exp.status) }}
|
|
367
368
|
</BasePill>
|
|
368
369
|
</div>
|
|
369
370
|
</div>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import FormBuilder from './FormBuilder.vue'
|
|
4
|
+
import { defineControlModel, defineControls } from '../composables/useControlSchema'
|
|
5
|
+
|
|
6
|
+
type FormSize = 'sm' | 'md' | 'lg'
|
|
7
|
+
|
|
8
|
+
const analysisModel = defineControlModel({
|
|
9
|
+
views: {
|
|
10
|
+
analysis: {
|
|
11
|
+
label: 'Analysis',
|
|
12
|
+
sections: {
|
|
13
|
+
parameters: {
|
|
14
|
+
label: 'Parameters',
|
|
15
|
+
description: 'Model and threshold controls',
|
|
16
|
+
columns: 2,
|
|
17
|
+
controls: {
|
|
18
|
+
threshold: {
|
|
19
|
+
type: 'number',
|
|
20
|
+
label: 'Threshold',
|
|
21
|
+
default: 0.05,
|
|
22
|
+
min: 0,
|
|
23
|
+
max: 1,
|
|
24
|
+
props: { step: 0.01 },
|
|
25
|
+
},
|
|
26
|
+
method: {
|
|
27
|
+
label: 'Method',
|
|
28
|
+
default: 'logistic',
|
|
29
|
+
options: ['linear', 'logistic', 'spline'],
|
|
30
|
+
},
|
|
31
|
+
normalize: {
|
|
32
|
+
label: 'Normalize intensities',
|
|
33
|
+
default: true,
|
|
34
|
+
type: 'toggle',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
quality: {
|
|
39
|
+
label: 'Quality Control',
|
|
40
|
+
description: 'Result filters',
|
|
41
|
+
columns: 2,
|
|
42
|
+
controls: {
|
|
43
|
+
minPeakArea: {
|
|
44
|
+
type: 'number',
|
|
45
|
+
label: 'Min peak area',
|
|
46
|
+
default: 1000,
|
|
47
|
+
min: 0,
|
|
48
|
+
},
|
|
49
|
+
excludeOutliers: {
|
|
50
|
+
label: 'Exclude outliers',
|
|
51
|
+
default: true,
|
|
52
|
+
type: 'checkbox',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const compactControls = defineControls({
|
|
62
|
+
sampleName: {
|
|
63
|
+
label: 'Sample name',
|
|
64
|
+
default: 'Vehicle',
|
|
65
|
+
required: true,
|
|
66
|
+
section: 'sample',
|
|
67
|
+
sectionLabel: 'Sample',
|
|
68
|
+
},
|
|
69
|
+
concentration: {
|
|
70
|
+
type: 'number',
|
|
71
|
+
label: 'Concentration',
|
|
72
|
+
default: 10,
|
|
73
|
+
min: 0,
|
|
74
|
+
section: 'sample',
|
|
75
|
+
},
|
|
76
|
+
unit: {
|
|
77
|
+
label: 'Unit',
|
|
78
|
+
default: 'uM',
|
|
79
|
+
options: ['nM', 'uM', 'mM'],
|
|
80
|
+
section: 'sample',
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const modelValues = ref<Record<string, unknown>>({})
|
|
85
|
+
const controlValues = ref<Record<string, unknown>>({})
|
|
86
|
+
|
|
87
|
+
function initState() {
|
|
88
|
+
return {
|
|
89
|
+
size: 'md' as FormSize,
|
|
90
|
+
showActions: true,
|
|
91
|
+
readonly: false,
|
|
92
|
+
disabled: false,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<Story title="Forms/FormBuilder" :init-state="initState">
|
|
99
|
+
<Variant title="Model Driven">
|
|
100
|
+
<template #default="{ state }">
|
|
101
|
+
<div class="form-builder-story">
|
|
102
|
+
<div class="form-builder-story__surface">
|
|
103
|
+
<FormBuilder
|
|
104
|
+
v-model="modelValues"
|
|
105
|
+
:model="analysisModel"
|
|
106
|
+
:size="state.size"
|
|
107
|
+
:show-actions="state.showActions"
|
|
108
|
+
:readonly="state.readonly"
|
|
109
|
+
:disabled="state.disabled"
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
<pre class="form-builder-story__values">{{ JSON.stringify(modelValues, null, 2) }}</pre>
|
|
113
|
+
</div>
|
|
114
|
+
</template>
|
|
115
|
+
|
|
116
|
+
<template #controls="{ state }">
|
|
117
|
+
<HstSelect
|
|
118
|
+
v-model="state.size"
|
|
119
|
+
title="Size"
|
|
120
|
+
:options="[
|
|
121
|
+
{ label: 'Small', value: 'sm' },
|
|
122
|
+
{ label: 'Medium', value: 'md' },
|
|
123
|
+
{ label: 'Large', value: 'lg' },
|
|
124
|
+
]"
|
|
125
|
+
/>
|
|
126
|
+
<HstCheckbox v-model="state.showActions" title="Actions" />
|
|
127
|
+
<HstCheckbox v-model="state.readonly" title="Readonly" />
|
|
128
|
+
<HstCheckbox v-model="state.disabled" title="Disabled" />
|
|
129
|
+
</template>
|
|
130
|
+
</Variant>
|
|
131
|
+
|
|
132
|
+
<Variant title="Compact Controls">
|
|
133
|
+
<div class="form-builder-story">
|
|
134
|
+
<div class="form-builder-story__surface form-builder-story__surface--narrow">
|
|
135
|
+
<FormBuilder
|
|
136
|
+
v-model="controlValues"
|
|
137
|
+
:controls="compactControls"
|
|
138
|
+
:show-actions="false"
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
<pre class="form-builder-story__values">{{ JSON.stringify(controlValues, null, 2) }}</pre>
|
|
142
|
+
</div>
|
|
143
|
+
</Variant>
|
|
144
|
+
</Story>
|
|
145
|
+
</template>
|
|
146
|
+
|
|
147
|
+
<style scoped>
|
|
148
|
+
.form-builder-story {
|
|
149
|
+
display: grid;
|
|
150
|
+
grid-template-columns: minmax(0, 1fr) minmax(14rem, 20rem);
|
|
151
|
+
gap: 1rem;
|
|
152
|
+
align-items: start;
|
|
153
|
+
padding: 2rem;
|
|
154
|
+
min-height: 640px;
|
|
155
|
+
background: var(--bg-secondary, #f8fafc);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.form-builder-story__surface {
|
|
159
|
+
min-width: 0;
|
|
160
|
+
padding: 1rem;
|
|
161
|
+
border: 1px solid var(--border-color, #e2e8f0);
|
|
162
|
+
border-radius: var(--radius-md, 0.5rem);
|
|
163
|
+
background: var(--bg-primary, #fff);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.form-builder-story__surface--narrow {
|
|
167
|
+
max-width: 34rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.form-builder-story__values {
|
|
171
|
+
min-width: 0;
|
|
172
|
+
margin: 0;
|
|
173
|
+
padding: 0.875rem;
|
|
174
|
+
border: 1px solid var(--border-color, #e2e8f0);
|
|
175
|
+
border-radius: var(--radius-md, 0.5rem);
|
|
176
|
+
background: var(--bg-tertiary, #f1f5f9);
|
|
177
|
+
color: var(--text-secondary, #475569);
|
|
178
|
+
font-family: var(--font-mono, monospace);
|
|
179
|
+
font-size: 0.75rem;
|
|
180
|
+
line-height: 1.5;
|
|
181
|
+
overflow: auto;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media (max-width: 900px) {
|
|
185
|
+
.form-builder-story {
|
|
186
|
+
grid-template-columns: 1fr;
|
|
187
|
+
padding: 1rem;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
</style>
|