@morscherlab/mint-sdk 1.0.0-beta.3 → 1.0.0-beta.4
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 +56 -4
- 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/AppPageSelectorInternal.vue.d.ts} +1 -1
- package/dist/components/{AppPillNav.vue.d.ts → internal/AppPillNavInternal.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-BkGF4B4y.js} +4484 -3967
- package/dist/components-BkGF4B4y.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-CHsME9H1.js} +40 -28
- package/dist/composables-CHsME9H1.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 +3625 -3651
- package/dist/templates/componentBindings.d.ts +13 -0
- package/dist/templates/index.d.ts +3 -3
- package/dist/templates/index.js +2 -2
- package/dist/{templates-50NPjaxL.js → templates-B5jmTWuk.js} +111 -56
- package/dist/templates-B5jmTWuk.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 → useScheduleDrag-BgzpQT53.js} +160 -117
- package/dist/useScheduleDrag-BgzpQT53.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/AppPageSelector.test.ts +8 -8
- package/src/__tests__/components/AppPillNav.test.ts +53 -6
- package/src/__tests__/components/AppSidebar.test.ts +126 -0
- package/src/__tests__/components/AppToastContainer.test.ts +0 -11
- package/src/__tests__/components/AppTopBar.test.ts +182 -119
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +7 -1
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +15 -1
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +26 -1
- package/src/__tests__/components/CalendarGridPanel.test.ts +3 -3
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +161 -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 +6 -3
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +6 -6
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +6 -1
- package/src/__tests__/composables/useControlSchema.test.ts +150 -36
- 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 +12 -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 +144 -24
- 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 +11 -2
- package/src/components/BioTemplateRenderer.vue +15 -227
- package/src/components/ComponentBindingRenderer.story.vue +57 -0
- package/src/components/ComponentBindingRenderer.vue +308 -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.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/AppPageSelectorInternal.vue} +9 -9
- package/src/components/internal/AppPillNavInternal.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-pill-nav.css +70 -0
- package/src/styles/components/app-sidebar.css +119 -0
- package/src/styles/components/app-top-bar.css +0 -235
- package/src/styles/index.css +0 -1
- package/src/templates/componentBindings.ts +38 -0
- package/src/templates/index.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/{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,308 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Render generated SDK component bindings such as WellPlate, DoseCalculator, DataFrame, and PlateMapEditor. */
|
|
3
|
+
import { computed, type Component } from 'vue'
|
|
4
|
+
import DataFrame from './DataFrame.vue'
|
|
5
|
+
import DoseCalculator from './DoseCalculator.vue'
|
|
6
|
+
import ExperimentTimeline from './ExperimentTimeline.vue'
|
|
7
|
+
import PlateMapEditor from './PlateMapEditor.vue'
|
|
8
|
+
import ReagentList from './ReagentList.vue'
|
|
9
|
+
import SampleSelector from './SampleSelector.vue'
|
|
10
|
+
import WellPlate from './WellPlate.vue'
|
|
11
|
+
|
|
12
|
+
export interface ComponentBindingRendererBinding {
|
|
13
|
+
id?: string
|
|
14
|
+
component: string
|
|
15
|
+
props?: Record<string, unknown> | readonly string[]
|
|
16
|
+
propsObject?: Record<string, unknown>
|
|
17
|
+
description?: string
|
|
18
|
+
template_id?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type ComponentBindingRendererLayout = 'grid' | 'stack'
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
/** Single generated SDK component binding to render. */
|
|
25
|
+
binding?: ComponentBindingRendererBinding
|
|
26
|
+
/** Generated SDK component bindings to render. */
|
|
27
|
+
bindings?: ComponentBindingRendererBinding[]
|
|
28
|
+
/** Optional allow-list of component names, for example ['WellPlate', 'DataFrame']. */
|
|
29
|
+
include?: string[]
|
|
30
|
+
/** Optional deny-list of component names. */
|
|
31
|
+
exclude?: string[]
|
|
32
|
+
/** Compact child component sizing. */
|
|
33
|
+
dense?: boolean
|
|
34
|
+
/** Prefer preview-safe props for editable components. */
|
|
35
|
+
readonly?: boolean
|
|
36
|
+
/** Show component/template labels above each rendered component. */
|
|
37
|
+
showHeaders?: boolean
|
|
38
|
+
/** Show binding descriptions in each header. */
|
|
39
|
+
showDescriptions?: boolean
|
|
40
|
+
/** Grid or vertical stack layout. */
|
|
41
|
+
layout?: ComponentBindingRendererLayout
|
|
42
|
+
/** Message shown when no binding can be rendered. */
|
|
43
|
+
emptyText?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type RenderableBinding = ComponentBindingRendererBinding & {
|
|
47
|
+
id: string
|
|
48
|
+
componentImpl: Component
|
|
49
|
+
normalizedProps: Record<string, unknown>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
53
|
+
binding: undefined,
|
|
54
|
+
bindings: () => [],
|
|
55
|
+
include: () => [],
|
|
56
|
+
exclude: () => [],
|
|
57
|
+
dense: false,
|
|
58
|
+
readonly: true,
|
|
59
|
+
showHeaders: true,
|
|
60
|
+
showDescriptions: true,
|
|
61
|
+
layout: 'grid',
|
|
62
|
+
emptyText: 'No renderable SDK component bindings.',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const componentRegistry: Record<string, Component> = {
|
|
66
|
+
DataFrame,
|
|
67
|
+
DoseCalculator,
|
|
68
|
+
ExperimentTimeline,
|
|
69
|
+
PlateMapEditor,
|
|
70
|
+
ReagentList,
|
|
71
|
+
SampleSelector,
|
|
72
|
+
WellPlate,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const inputBindings = computed<ComponentBindingRendererBinding[]>(() => [
|
|
76
|
+
...(props.binding ? [props.binding] : []),
|
|
77
|
+
...props.bindings,
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
const renderableBindings = computed<RenderableBinding[]>(() =>
|
|
81
|
+
inputBindings.value
|
|
82
|
+
.filter(binding => isIncluded(binding.component))
|
|
83
|
+
.map((binding, index) => {
|
|
84
|
+
const componentImpl = componentRegistry[binding.component]
|
|
85
|
+
if (!componentImpl) return null
|
|
86
|
+
return {
|
|
87
|
+
...binding,
|
|
88
|
+
id: binding.id ?? `${binding.component}-${index + 1}`,
|
|
89
|
+
componentImpl,
|
|
90
|
+
normalizedProps: normalizeProps(binding),
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
.filter((binding): binding is RenderableBinding => binding !== null)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const rendererClasses = computed(() => [
|
|
97
|
+
'mint-component-binding-renderer',
|
|
98
|
+
`mint-component-binding-renderer--${props.layout}`,
|
|
99
|
+
props.dense ? 'mint-component-binding-renderer--dense' : '',
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
function isIncluded(componentName: string): boolean {
|
|
103
|
+
if (props.include.length > 0 && !props.include.includes(componentName)) return false
|
|
104
|
+
return !props.exclude.includes(componentName)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function baseProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {
|
|
108
|
+
if (binding.propsObject) return { ...binding.propsObject }
|
|
109
|
+
if (isRecord(binding.props)) return { ...binding.props }
|
|
110
|
+
return {}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizeProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {
|
|
114
|
+
const base = baseProps(binding)
|
|
115
|
+
|
|
116
|
+
switch (binding.component) {
|
|
117
|
+
case 'WellPlate':
|
|
118
|
+
return {
|
|
119
|
+
...base,
|
|
120
|
+
readonly: props.readonly || Boolean(base.readonly),
|
|
121
|
+
size: props.dense ? 'md' : (base.size ?? 'fill'),
|
|
122
|
+
}
|
|
123
|
+
case 'PlateMapEditor':
|
|
124
|
+
return {
|
|
125
|
+
...base,
|
|
126
|
+
size: props.dense ? 'md' : (base.size ?? 'fill'),
|
|
127
|
+
showToolbar: props.readonly ? false : (base.showToolbar ?? true),
|
|
128
|
+
showSidebar: props.readonly ? false : (base.showSidebar ?? true),
|
|
129
|
+
allowAddPlates: props.readonly ? false : (base.allowAddPlates ?? true),
|
|
130
|
+
allowAddSamples: props.readonly ? false : (base.allowAddSamples ?? true),
|
|
131
|
+
}
|
|
132
|
+
case 'DataFrame':
|
|
133
|
+
return {
|
|
134
|
+
...base,
|
|
135
|
+
size: props.dense ? 'sm' : (base.size ?? 'md'),
|
|
136
|
+
maxHeight: base.maxHeight ?? (props.dense ? '280px' : undefined),
|
|
137
|
+
}
|
|
138
|
+
case 'ExperimentTimeline':
|
|
139
|
+
return {
|
|
140
|
+
...base,
|
|
141
|
+
editable: props.readonly ? false : base.editable,
|
|
142
|
+
size: props.dense ? 'sm' : (base.size ?? 'md'),
|
|
143
|
+
}
|
|
144
|
+
case 'ReagentList':
|
|
145
|
+
return {
|
|
146
|
+
...base,
|
|
147
|
+
readonly: props.readonly || Boolean(base.readonly),
|
|
148
|
+
}
|
|
149
|
+
case 'SampleSelector':
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
enableGrouping: props.readonly ? false : (base.enableGrouping ?? true),
|
|
153
|
+
enableSmartGroup: props.readonly ? false : (base.enableSmartGroup ?? true),
|
|
154
|
+
}
|
|
155
|
+
default:
|
|
156
|
+
return base
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
161
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
162
|
+
}
|
|
163
|
+
</script>
|
|
164
|
+
|
|
165
|
+
<template>
|
|
166
|
+
<div :class="rendererClasses">
|
|
167
|
+
<section
|
|
168
|
+
v-for="renderableBinding in renderableBindings"
|
|
169
|
+
:key="renderableBinding.id"
|
|
170
|
+
class="mint-component-binding-renderer__item"
|
|
171
|
+
:data-component-binding-id="renderableBinding.id"
|
|
172
|
+
:data-template-id="renderableBinding.template_id"
|
|
173
|
+
:data-template-component="renderableBinding.component"
|
|
174
|
+
>
|
|
175
|
+
<header
|
|
176
|
+
v-if="showHeaders"
|
|
177
|
+
class="mint-component-binding-renderer__header"
|
|
178
|
+
>
|
|
179
|
+
<div class="mint-component-binding-renderer__title-group">
|
|
180
|
+
<p class="mint-component-binding-renderer__component">
|
|
181
|
+
{{ renderableBinding.component }}
|
|
182
|
+
</p>
|
|
183
|
+
<p
|
|
184
|
+
v-if="renderableBinding.template_id || renderableBinding.id"
|
|
185
|
+
class="mint-component-binding-renderer__source"
|
|
186
|
+
>
|
|
187
|
+
{{ renderableBinding.template_id ?? renderableBinding.id }}
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
<p
|
|
191
|
+
v-if="showDescriptions && renderableBinding.description"
|
|
192
|
+
class="mint-component-binding-renderer__description"
|
|
193
|
+
>
|
|
194
|
+
{{ renderableBinding.description }}
|
|
195
|
+
</p>
|
|
196
|
+
</header>
|
|
197
|
+
|
|
198
|
+
<div class="mint-component-binding-renderer__body">
|
|
199
|
+
<component
|
|
200
|
+
:is="renderableBinding.componentImpl"
|
|
201
|
+
v-bind="renderableBinding.normalizedProps"
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
</section>
|
|
205
|
+
|
|
206
|
+
<p
|
|
207
|
+
v-if="renderableBindings.length === 0"
|
|
208
|
+
class="mint-component-binding-renderer__empty"
|
|
209
|
+
>
|
|
210
|
+
{{ emptyText }}
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
</template>
|
|
214
|
+
|
|
215
|
+
<style scoped>
|
|
216
|
+
.mint-component-binding-renderer {
|
|
217
|
+
display: grid;
|
|
218
|
+
gap: 1rem;
|
|
219
|
+
min-width: 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.mint-component-binding-renderer--grid {
|
|
223
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 28rem), 1fr));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.mint-component-binding-renderer--stack {
|
|
227
|
+
grid-template-columns: minmax(0, 1fr);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.mint-component-binding-renderer--dense {
|
|
231
|
+
gap: 0.75rem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.mint-component-binding-renderer__item {
|
|
235
|
+
min-width: 0;
|
|
236
|
+
overflow: hidden;
|
|
237
|
+
border: 1px solid var(--border-color, #e5e7eb);
|
|
238
|
+
border-radius: 0.5rem;
|
|
239
|
+
background: var(--bg-card, #ffffff);
|
|
240
|
+
box-shadow: var(--shadow-sm, 0 1px 2px rgb(15 23 42 / 0.06));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.mint-component-binding-renderer__header {
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: flex-start;
|
|
246
|
+
justify-content: space-between;
|
|
247
|
+
gap: 1rem;
|
|
248
|
+
padding: 0.75rem 1rem;
|
|
249
|
+
border-bottom: 1px solid var(--border-color, #e5e7eb);
|
|
250
|
+
background: var(--bg-secondary, #f8fafc);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.mint-component-binding-renderer__title-group {
|
|
254
|
+
min-width: 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.mint-component-binding-renderer__component {
|
|
258
|
+
margin: 0;
|
|
259
|
+
color: var(--text-primary, #0f172a);
|
|
260
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
|
|
261
|
+
font-size: 0.8125rem;
|
|
262
|
+
font-weight: 600;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.mint-component-binding-renderer__source,
|
|
266
|
+
.mint-component-binding-renderer__description,
|
|
267
|
+
.mint-component-binding-renderer__empty {
|
|
268
|
+
margin: 0;
|
|
269
|
+
color: var(--text-secondary, #475569);
|
|
270
|
+
font-size: 0.75rem;
|
|
271
|
+
line-height: 1.4;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.mint-component-binding-renderer__description {
|
|
275
|
+
max-width: 24rem;
|
|
276
|
+
text-align: right;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.mint-component-binding-renderer__body {
|
|
280
|
+
min-width: 0;
|
|
281
|
+
padding: 1rem;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.mint-component-binding-renderer--dense .mint-component-binding-renderer__header,
|
|
285
|
+
.mint-component-binding-renderer--dense .mint-component-binding-renderer__body {
|
|
286
|
+
padding: 0.75rem;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.mint-component-binding-renderer__empty {
|
|
290
|
+
padding: 1rem;
|
|
291
|
+
border: 1px dashed var(--border-color, #e5e7eb);
|
|
292
|
+
border-radius: 0.5rem;
|
|
293
|
+
background: var(--bg-secondary, #f8fafc);
|
|
294
|
+
text-align: center;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@media (max-width: 720px) {
|
|
298
|
+
.mint-component-binding-renderer__header {
|
|
299
|
+
display: block;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.mint-component-binding-renderer__description {
|
|
303
|
+
max-width: none;
|
|
304
|
+
margin-top: 0.25rem;
|
|
305
|
+
text-align: left;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
</style>
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
type ControlComponentPropsMap,
|
|
12
12
|
} from '../composables/useControlSchema'
|
|
13
13
|
|
|
14
|
+
type SidebarVariant = 'analysis' | 'default'
|
|
15
|
+
|
|
14
16
|
const controls = defineControls({
|
|
15
17
|
threshold: {
|
|
16
18
|
type: 'number',
|
|
@@ -137,8 +139,11 @@ const MockResultChart = defineComponent({
|
|
|
137
139
|
function initState() {
|
|
138
140
|
return {
|
|
139
141
|
title: 'Analysis Workspace',
|
|
142
|
+
sidebarTitle: 'Analysis Controls',
|
|
143
|
+
sidebarSubtitle: 'Current run',
|
|
140
144
|
sidebarPosition: 'left',
|
|
141
|
-
|
|
145
|
+
sidebarVariant: 'analysis' as SidebarVariant,
|
|
146
|
+
responsiveSidebar: true,
|
|
142
147
|
floating: false,
|
|
143
148
|
dense: true,
|
|
144
149
|
showSettings: true,
|
|
@@ -157,8 +162,11 @@ function initState() {
|
|
|
157
162
|
<ControlWorkspaceView
|
|
158
163
|
:workspace="workspace"
|
|
159
164
|
:title="state.title"
|
|
165
|
+
:sidebar-title="state.sidebarTitle"
|
|
166
|
+
:sidebar-subtitle="state.sidebarSubtitle"
|
|
160
167
|
:sidebar-position="state.sidebarPosition"
|
|
161
|
-
:
|
|
168
|
+
:sidebar-variant="state.sidebarVariant"
|
|
169
|
+
:responsive-sidebar="state.responsiveSidebar"
|
|
162
170
|
:floating="state.floating"
|
|
163
171
|
:dense="state.dense"
|
|
164
172
|
:show-settings="state.showSettings"
|
|
@@ -171,6 +179,8 @@ function initState() {
|
|
|
171
179
|
|
|
172
180
|
<template #controls="{ state }">
|
|
173
181
|
<HstText v-model="state.title" title="Title" />
|
|
182
|
+
<HstText v-model="state.sidebarTitle" title="Sidebar title" />
|
|
183
|
+
<HstText v-model="state.sidebarSubtitle" title="Sidebar subtitle" />
|
|
174
184
|
<HstSelect
|
|
175
185
|
v-model="state.sidebarPosition"
|
|
176
186
|
title="Sidebar"
|
|
@@ -180,13 +190,14 @@ function initState() {
|
|
|
180
190
|
]"
|
|
181
191
|
/>
|
|
182
192
|
<HstSelect
|
|
183
|
-
v-model="state.
|
|
184
|
-
title="
|
|
193
|
+
v-model="state.sidebarVariant"
|
|
194
|
+
title="Sidebar variant"
|
|
185
195
|
:options="[
|
|
186
|
-
{ label: '
|
|
187
|
-
{ label: '
|
|
196
|
+
{ label: 'Analysis', value: 'analysis' },
|
|
197
|
+
{ label: 'Default', value: 'default' },
|
|
188
198
|
]"
|
|
189
199
|
/>
|
|
200
|
+
<HstCheckbox v-model="state.responsiveSidebar" title="Responsive sidebar" />
|
|
190
201
|
<HstCheckbox v-model="state.floating" title="Floating" />
|
|
191
202
|
<HstCheckbox v-model="state.dense" title="Dense" />
|
|
192
203
|
<HstCheckbox v-model="state.showSettings" title="Settings" />
|
|
@@ -228,16 +239,16 @@ function initState() {
|
|
|
228
239
|
v-model="doseDesignValues"
|
|
229
240
|
:model="doseDesignModel"
|
|
230
241
|
title="Dose Design Workspace"
|
|
231
|
-
v-slot="{
|
|
242
|
+
v-slot="{ componentBindingsById }"
|
|
232
243
|
>
|
|
233
244
|
<div class="dose-design-demo">
|
|
234
245
|
<WellPlate
|
|
235
|
-
v-bind="
|
|
246
|
+
v-bind="componentBindingsById.plate.props"
|
|
236
247
|
size="fill"
|
|
237
248
|
selection-mode="multiple"
|
|
238
249
|
:heatmap="{ enabled: true, min: 0, max: 100, colorScale: 'viridis' }"
|
|
239
250
|
/>
|
|
240
|
-
<DoseCalculator v-bind="
|
|
251
|
+
<DoseCalculator v-bind="componentBindingsById.dose.props" />
|
|
241
252
|
</div>
|
|
242
253
|
</ControlWorkspaceView>
|
|
243
254
|
</div>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
2
|
+
/** Complete control workspace component that turns a simple controls data model into AppTopBar, AppSidebar, and FormBuilder forms. */
|
|
3
3
|
import { computed, effectScope, onScopeDispose, shallowRef, toRaw, watch, type EffectScope } from 'vue'
|
|
4
4
|
import type { TopBarVariant } from '../types'
|
|
5
5
|
import type { FormEnhancements } from '../types/form-builder'
|
|
6
6
|
import type {
|
|
7
|
+
ControlComponentBindingsConfig,
|
|
7
8
|
ControlComponentPropsByIdMap,
|
|
8
9
|
ControlComponentPropsMap,
|
|
9
10
|
ControlModel,
|
|
@@ -18,13 +19,15 @@ import AppSidebar from './AppSidebar.vue'
|
|
|
18
19
|
import AppTopBar from './AppTopBar.vue'
|
|
19
20
|
import FormBuilder from './FormBuilder.vue'
|
|
20
21
|
|
|
21
|
-
type
|
|
22
|
+
type ControlWorkspaceSidebarVariant = 'default' | 'analysis'
|
|
22
23
|
type ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>
|
|
23
24
|
|
|
24
25
|
interface ControlWorkspaceDefaultSlotProps {
|
|
25
26
|
workspace: ResolvedControlWorkspace
|
|
26
27
|
bindings: ResolvedControlWorkspace['bindings']
|
|
27
28
|
values: ResolvedControlWorkspace['values']
|
|
29
|
+
componentBindings: ReturnType<ResolvedControlWorkspace['getComponentBindings']>
|
|
30
|
+
componentBindingsById: ReturnType<ResolvedControlWorkspace['getComponentBindingsById']>
|
|
28
31
|
componentProps: ReturnType<ResolvedControlWorkspace['getComponentProps']>
|
|
29
32
|
componentPropsById: ReturnType<ResolvedControlWorkspace['getComponentPropsById']>
|
|
30
33
|
}
|
|
@@ -32,7 +35,7 @@ interface ControlWorkspaceDefaultSlotProps {
|
|
|
32
35
|
interface ControlWorkspaceTopbarSlotProps {
|
|
33
36
|
workspace: ResolvedControlWorkspace
|
|
34
37
|
bindings: ResolvedControlWorkspace['bindings']
|
|
35
|
-
topBar: ResolvedControlWorkspace['topBar']
|
|
38
|
+
topBar: ResolvedControlWorkspace['bindings']['topBar']['value']
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
interface ControlWorkspaceSidebarSlotProps {
|
|
@@ -62,12 +65,20 @@ interface Props {
|
|
|
62
65
|
subtitle?: string
|
|
63
66
|
/** AppTopBar visual variant. */
|
|
64
67
|
topBarVariant?: TopBarVariant
|
|
65
|
-
/** AppTopBar generated navigation style. "pill" uses the preferred AppPillNav surface; "tabs" keeps the legacy prop surface. */
|
|
66
|
-
navigation?: ControlWorkspaceNavigation
|
|
67
68
|
/** AppSidebar/AppLayout sidebar width. */
|
|
68
69
|
sidebarWidth?: string
|
|
70
|
+
/** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */
|
|
71
|
+
sidebarVariant?: ControlWorkspaceSidebarVariant
|
|
72
|
+
/** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */
|
|
73
|
+
responsiveSidebar?: boolean
|
|
69
74
|
/** Sidebar position in AppLayout. */
|
|
70
75
|
sidebarPosition?: 'left' | 'right'
|
|
76
|
+
/** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */
|
|
77
|
+
sidebarTitle?: string
|
|
78
|
+
/** Optional AppSidebar chrome subtitle for active experiment/run context. */
|
|
79
|
+
sidebarSubtitle?: string
|
|
80
|
+
/** Optional compact badge/count rendered in the AppSidebar chrome header. */
|
|
81
|
+
sidebarBadge?: string | number
|
|
71
82
|
/** Floating AppLayout style. */
|
|
72
83
|
floating?: boolean
|
|
73
84
|
/** Compact AppSidebar density. */
|
|
@@ -78,6 +89,8 @@ interface Props {
|
|
|
78
89
|
showFormActions?: boolean
|
|
79
90
|
/** Runtime FormBuilder enhancements passed to generated forms. */
|
|
80
91
|
formEnhancements?: FormEnhancements<Record<string, unknown>>
|
|
92
|
+
/** Optional SDK component bindings exposed to the default slot with resolved props. */
|
|
93
|
+
componentBindings?: ControlComponentBindingsConfig
|
|
81
94
|
/** Optional mapping from workspace values to component props exposed to the default slot. */
|
|
82
95
|
componentProps?: ControlComponentPropsMap
|
|
83
96
|
/** Optional named mappings from workspace values to component props exposed to the default slot. */
|
|
@@ -120,14 +133,19 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
120
133
|
title: 'Workspace',
|
|
121
134
|
subtitle: undefined,
|
|
122
135
|
topBarVariant: 'card',
|
|
123
|
-
navigation: 'pill',
|
|
124
136
|
sidebarWidth: '320px',
|
|
137
|
+
sidebarVariant: 'analysis',
|
|
138
|
+
responsiveSidebar: true,
|
|
125
139
|
sidebarPosition: 'left',
|
|
140
|
+
sidebarTitle: undefined,
|
|
141
|
+
sidebarSubtitle: undefined,
|
|
142
|
+
sidebarBadge: undefined,
|
|
126
143
|
floating: false,
|
|
127
144
|
dense: true,
|
|
128
145
|
showSettings: true,
|
|
129
146
|
showFormActions: false,
|
|
130
147
|
formEnhancements: undefined,
|
|
148
|
+
componentBindings: undefined,
|
|
131
149
|
componentProps: undefined,
|
|
132
150
|
componentPropsById: undefined,
|
|
133
151
|
formLoading: false,
|
|
@@ -177,6 +195,12 @@ function createGeneratedWorkspace(seedValues: Record<string, unknown> = {}) {
|
|
|
177
195
|
|
|
178
196
|
const generatedWorkspace = shallowRef<UseControlWorkspaceReturn<ControlSchema>>(createGeneratedWorkspace())
|
|
179
197
|
const resolvedWorkspace = computed<UseControlWorkspaceReturn<ControlSchema>>(() => props.workspace ?? generatedWorkspace.value)
|
|
198
|
+
const resolvedComponentBindings = computed(() =>
|
|
199
|
+
resolvedWorkspace.value.getComponentBindings(props.componentBindings ?? resolvedModel.value?.componentBindings)
|
|
200
|
+
)
|
|
201
|
+
const resolvedComponentBindingsById = computed(() =>
|
|
202
|
+
resolvedWorkspace.value.getComponentBindingsById(props.componentBindings ?? resolvedModel.value?.componentBindings)
|
|
203
|
+
)
|
|
180
204
|
const resolvedComponentProps = computed(() =>
|
|
181
205
|
resolvedWorkspace.value.getComponentProps(props.componentProps ?? resolvedModel.value?.componentProps)
|
|
182
206
|
)
|
|
@@ -185,9 +209,12 @@ const resolvedComponentPropsById = computed(() =>
|
|
|
185
209
|
)
|
|
186
210
|
const resolvedBindings = computed<ResolvedControlWorkspace['bindings']>(() => ({
|
|
187
211
|
...resolvedWorkspace.value.bindings,
|
|
212
|
+
componentBindings: resolvedComponentBindings,
|
|
213
|
+
componentBindingsById: resolvedComponentBindingsById,
|
|
188
214
|
componentProps: resolvedComponentProps,
|
|
189
215
|
componentPropsById: resolvedComponentPropsById,
|
|
190
216
|
}))
|
|
217
|
+
const resolvedTopBarSlot = computed(() => resolvedWorkspace.value.bindings.topBar.value)
|
|
191
218
|
|
|
192
219
|
watch(
|
|
193
220
|
[() => props.model, () => props.controls, () => props.controlOptions, () => props.initialValues],
|
|
@@ -265,6 +292,7 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
265
292
|
class="mint-control-workspace-view"
|
|
266
293
|
:sidebar-position="sidebarPosition"
|
|
267
294
|
:sidebar-width="sidebarWidth"
|
|
295
|
+
:responsive-sidebar="responsiveSidebar"
|
|
268
296
|
:floating="floating"
|
|
269
297
|
>
|
|
270
298
|
<template #topbar>
|
|
@@ -272,19 +300,16 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
272
300
|
name="topbar"
|
|
273
301
|
:workspace="resolvedWorkspace"
|
|
274
302
|
:bindings="resolvedBindings"
|
|
275
|
-
:top-bar="
|
|
303
|
+
:top-bar="resolvedTopBarSlot"
|
|
276
304
|
>
|
|
277
305
|
<AppTopBar
|
|
278
306
|
:title="title"
|
|
279
307
|
:subtitle="subtitle"
|
|
280
308
|
:variant="topBarVariant"
|
|
281
|
-
:
|
|
282
|
-
:current-
|
|
283
|
-
:pill-nav="navigation === 'pill' ? resolvedWorkspace.pillNav.items : undefined"
|
|
284
|
-
:current-pill-id="navigation === 'pill' ? resolvedWorkspace.pillNav.currentItemId : undefined"
|
|
309
|
+
:pill-nav="resolvedWorkspace.pillNav.items"
|
|
310
|
+
:current-pill-id="resolvedWorkspace.pillNav.currentItemId"
|
|
285
311
|
:show-settings="showSettings"
|
|
286
312
|
:settings-config="resolvedWorkspace.topBarSettings.settingsConfig"
|
|
287
|
-
@tab-select="resolvedWorkspace.topBar.onTabSelect"
|
|
288
313
|
@pill-select="resolvedWorkspace.pillNav.onSelect"
|
|
289
314
|
@settings-values-change="resolvedWorkspace.topBarSettings.onSettingsValuesChange"
|
|
290
315
|
/>
|
|
@@ -300,6 +325,10 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
300
325
|
>
|
|
301
326
|
<AppSidebar
|
|
302
327
|
v-bind="resolvedWorkspace.sidebar"
|
|
328
|
+
:title="sidebarTitle"
|
|
329
|
+
:subtitle="sidebarSubtitle"
|
|
330
|
+
:badge="sidebarBadge"
|
|
331
|
+
:variant="sidebarVariant"
|
|
303
332
|
:floating="false"
|
|
304
333
|
:dense="dense"
|
|
305
334
|
:width="sidebarWidth"
|
|
@@ -316,6 +345,8 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
316
345
|
:workspace="resolvedWorkspace"
|
|
317
346
|
:bindings="resolvedBindings"
|
|
318
347
|
:values="resolvedWorkspace.values"
|
|
348
|
+
:component-bindings="resolvedComponentBindings"
|
|
349
|
+
:component-bindings-by-id="resolvedComponentBindingsById"
|
|
319
350
|
:component-props="resolvedComponentProps"
|
|
320
351
|
:component-props-by-id="resolvedComponentPropsById"
|
|
321
352
|
>
|
|
@@ -4,7 +4,7 @@ import { ref, computed, watch } from 'vue'
|
|
|
4
4
|
import { useCalendarGrid, type CalendarGridDay } from '../composables/useCalendarGrid'
|
|
5
5
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
6
6
|
import { useEventListener } from '../composables/useEventListener'
|
|
7
|
-
import
|
|
7
|
+
import CalendarGridPanelInternal from './internal/CalendarGridPanelInternal.vue'
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
modelValue?: string
|
|
@@ -159,7 +159,7 @@ useEventListener(() => window, 'resize', handleScrollOrResize)
|
|
|
159
159
|
aria-modal="true"
|
|
160
160
|
aria-label="Date picker"
|
|
161
161
|
>
|
|
162
|
-
<
|
|
162
|
+
<CalendarGridPanelInternal
|
|
163
163
|
:week-days="weekDays"
|
|
164
164
|
:month-year="monthYear"
|
|
165
165
|
:days="calendarDays"
|
|
@@ -4,7 +4,7 @@ import { computed, watch, nextTick } from 'vue'
|
|
|
4
4
|
import { useCalendarGrid, type CalendarGridDay } from '../composables/useCalendarGrid'
|
|
5
5
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
6
6
|
import { formatTime, formatTimeSlot, generateTimeSlots, toMinutes } from '../composables/useTimeUtils'
|
|
7
|
-
import
|
|
7
|
+
import CalendarGridPanelInternal from './internal/CalendarGridPanelInternal.vue'
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
modelValue?: string
|
|
@@ -210,7 +210,7 @@ watch(isOpen, (open) => {
|
|
|
210
210
|
>
|
|
211
211
|
<!-- Calendar section -->
|
|
212
212
|
<div class="mint-datetime-picker__calendar-section">
|
|
213
|
-
<
|
|
213
|
+
<CalendarGridPanelInternal
|
|
214
214
|
:week-days="weekDays"
|
|
215
215
|
:month-year="monthYear"
|
|
216
216
|
:days="calendarDays"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import DoseDesignWorkspaceView from './DoseDesignWorkspaceView.vue'
|
|
4
|
+
import type { DoseDesignControlModelOptions } from '../composables/useControlSchema'
|
|
5
|
+
|
|
6
|
+
type SidebarVariant = 'analysis' | 'default'
|
|
7
|
+
|
|
8
|
+
const initState = () => ({
|
|
9
|
+
title: 'Dose Design Workspace',
|
|
10
|
+
includeMolecularWeight: true,
|
|
11
|
+
plateFormat: 96,
|
|
12
|
+
selectedWells: ['A1', 'A2', 'B1', 'B2'],
|
|
13
|
+
sidebarTitle: 'Dose Controls',
|
|
14
|
+
sidebarSubtitle: 'Plate setup',
|
|
15
|
+
sidebarVariant: 'analysis' as SidebarVariant,
|
|
16
|
+
responsiveSidebar: true,
|
|
17
|
+
showSettings: true,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const state = ref(initState())
|
|
21
|
+
const values = ref<Record<string, unknown>>({})
|
|
22
|
+
|
|
23
|
+
function doseOptions(): DoseDesignControlModelOptions {
|
|
24
|
+
return {
|
|
25
|
+
selectedWells: state.value.selectedWells,
|
|
26
|
+
plateFormat: state.value.plateFormat,
|
|
27
|
+
includeMolecularWeight: state.value.includeMolecularWeight,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<Story title="Workflow/DoseDesignWorkspaceView" :init-state="initState">
|
|
34
|
+
<Variant title="Playground">
|
|
35
|
+
<div style="min-height: 720px; background: var(--bg-secondary, #f8fafc);">
|
|
36
|
+
<DoseDesignWorkspaceView
|
|
37
|
+
v-model="values"
|
|
38
|
+
:title="state.title"
|
|
39
|
+
:dose-design-options="doseOptions()"
|
|
40
|
+
:sidebar-title="state.sidebarTitle"
|
|
41
|
+
:sidebar-subtitle="state.sidebarSubtitle"
|
|
42
|
+
:sidebar-variant="state.sidebarVariant"
|
|
43
|
+
:responsive-sidebar="state.responsiveSidebar"
|
|
44
|
+
:show-settings="state.showSettings"
|
|
45
|
+
:well-plate-props="{
|
|
46
|
+
heatmap: { enabled: true, min: 0, max: 100, colorScale: 'viridis' },
|
|
47
|
+
}"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</Variant>
|
|
51
|
+
|
|
52
|
+
<template #controls>
|
|
53
|
+
<HstText v-model="state.title" title="Title" />
|
|
54
|
+
<HstText v-model="state.sidebarTitle" title="Sidebar title" />
|
|
55
|
+
<HstText v-model="state.sidebarSubtitle" title="Sidebar subtitle" />
|
|
56
|
+
<HstCheckbox v-model="state.includeMolecularWeight" title="Molecular weight" />
|
|
57
|
+
<HstCheckbox v-model="state.responsiveSidebar" title="Responsive sidebar" />
|
|
58
|
+
<HstCheckbox v-model="state.showSettings" title="Settings" />
|
|
59
|
+
<HstSelect
|
|
60
|
+
v-model="state.sidebarVariant"
|
|
61
|
+
title="Sidebar variant"
|
|
62
|
+
:options="[
|
|
63
|
+
{ label: 'Analysis', value: 'analysis' },
|
|
64
|
+
{ label: 'Default', value: 'default' },
|
|
65
|
+
]"
|
|
66
|
+
/>
|
|
67
|
+
<HstSelect
|
|
68
|
+
v-model="state.plateFormat"
|
|
69
|
+
title="Plate format"
|
|
70
|
+
:options="[
|
|
71
|
+
{ label: '96 wells', value: 96 },
|
|
72
|
+
{ label: '384 wells', value: 384 },
|
|
73
|
+
]"
|
|
74
|
+
/>
|
|
75
|
+
</template>
|
|
76
|
+
</Story>
|
|
77
|
+
</template>
|