@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
|
@@ -10,11 +10,13 @@ import NumberInput from './NumberInput.vue'
|
|
|
10
10
|
import FileUploader from './FileUploader.vue'
|
|
11
11
|
import FormField from './FormField.vue'
|
|
12
12
|
import WellPlate from './WellPlate.vue'
|
|
13
|
-
import { defineControls, getControlDefaults } from '../composables/useControlSchema'
|
|
13
|
+
import { defineControlModel, defineControls, getControlDefaults } from '../composables/useControlSchema'
|
|
14
14
|
import { useBioTemplateWorkspace } from '../composables/useBioTemplateWorkspace'
|
|
15
15
|
import { createWellPlateScreenCollection } from '../templates'
|
|
16
16
|
import type { SidebarToolSection } from '../types'
|
|
17
17
|
|
|
18
|
+
type SidebarVariant = 'analysis' | 'default'
|
|
19
|
+
|
|
18
20
|
/* --- Icon paths (Lucide-style) --- */
|
|
19
21
|
const icons = {
|
|
20
22
|
upload: ['M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4', 'M17 8l-5-5-5 5', 'M12 3v12'],
|
|
@@ -130,6 +132,13 @@ const polarity = ref('negative')
|
|
|
130
132
|
const container = ref('vial')
|
|
131
133
|
const expNumber = ref(1)
|
|
132
134
|
const initials = ref('XP')
|
|
135
|
+
const sequencePrefix = ref('exp001_260210_XP')
|
|
136
|
+
const runMode = ref('balanced')
|
|
137
|
+
const runModeOptions = [
|
|
138
|
+
{ value: 'fast', label: 'Fast' },
|
|
139
|
+
{ value: 'balanced', label: 'Balanced' },
|
|
140
|
+
{ value: 'qc', label: 'QC focused' },
|
|
141
|
+
]
|
|
133
142
|
const toggleState = reactive<Record<string, boolean>>({ naming: true, randomize: false })
|
|
134
143
|
const schemaControls = defineControls({
|
|
135
144
|
threshold: {
|
|
@@ -172,13 +181,46 @@ const schemaControls = defineControls({
|
|
|
172
181
|
},
|
|
173
182
|
},
|
|
174
183
|
})
|
|
175
|
-
const controlValues =
|
|
184
|
+
const controlValues = ref<Record<string, unknown>>({ ...getControlDefaults(schemaControls) })
|
|
185
|
+
const modelDrivenSidebarModel = defineControlModel({
|
|
186
|
+
views: {
|
|
187
|
+
analysis: {
|
|
188
|
+
label: 'Analysis',
|
|
189
|
+
sections: {
|
|
190
|
+
parameters: {
|
|
191
|
+
label: 'Parameters',
|
|
192
|
+
description: 'Thresholds and scoring',
|
|
193
|
+
icon: icons.settings,
|
|
194
|
+
controls: {
|
|
195
|
+
threshold: { type: 'number', default: 0.05, min: 0, max: 1 },
|
|
196
|
+
method: ['linear', 'logistic', 'spline'],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
filters: {
|
|
200
|
+
label: 'Filters',
|
|
201
|
+
description: 'Result cleanup',
|
|
202
|
+
sidebar: {
|
|
203
|
+
icon: icons.zap,
|
|
204
|
+
iconColor: '#0ea5e9',
|
|
205
|
+
iconBg: '#e0f2fe',
|
|
206
|
+
defaultOpen: false,
|
|
207
|
+
},
|
|
208
|
+
controls: {
|
|
209
|
+
showOutliers: true,
|
|
210
|
+
minPeakArea: { type: 'number', default: 1000, min: 0 },
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
const modelDrivenValues = ref<Record<string, unknown>>({})
|
|
176
218
|
const templateCollection = createWellPlateScreenCollection({
|
|
177
219
|
samples: ['Control', 'Treatment'],
|
|
178
220
|
compounds: { 'Drug A': [10, 1, 0.1] },
|
|
179
221
|
})
|
|
180
222
|
const templateWorkspace = useBioTemplateWorkspace(templateCollection)
|
|
181
|
-
const templateValues =
|
|
223
|
+
const templateValues = ref<Record<string, unknown>>({ ...templateWorkspace.controls.initialValues })
|
|
182
224
|
const templateWellPlateProps = templateWorkspace.componentProps
|
|
183
225
|
.find(binding => binding.component === 'WellPlate')
|
|
184
226
|
?.propsObject ?? {}
|
|
@@ -190,8 +232,9 @@ function handleToggle(id: string, value: boolean) {
|
|
|
190
232
|
function initSimpleToolkit() {
|
|
191
233
|
return {
|
|
192
234
|
activeView: 'analysis',
|
|
235
|
+
variant: 'analysis' as SidebarVariant,
|
|
193
236
|
floating: false,
|
|
194
|
-
width: '
|
|
237
|
+
width: '20rem',
|
|
195
238
|
side: 'left' as const,
|
|
196
239
|
}
|
|
197
240
|
}
|
|
@@ -276,14 +319,70 @@ function initSimpleToolkit() {
|
|
|
276
319
|
<Variant title="Schema Driven Controls">
|
|
277
320
|
<div style="padding: 2rem; height: 520px; position: relative; background: var(--bg-primary, #f1f5f9);">
|
|
278
321
|
<AppSidebar
|
|
322
|
+
variant="analysis"
|
|
323
|
+
title="Peak Picking"
|
|
324
|
+
subtitle="Current experiment"
|
|
279
325
|
:controls="schemaControls"
|
|
280
326
|
active-view="analysis"
|
|
281
327
|
v-model="controlValues"
|
|
282
|
-
width="300px"
|
|
283
328
|
/>
|
|
284
329
|
</div>
|
|
285
330
|
</Variant>
|
|
286
331
|
|
|
332
|
+
<Variant title="Model Driven Analysis">
|
|
333
|
+
<div style="padding: 2rem; height: 560px; position: relative; background: var(--bg-primary, #f1f5f9);">
|
|
334
|
+
<AppSidebar
|
|
335
|
+
v-model="modelDrivenValues"
|
|
336
|
+
variant="analysis"
|
|
337
|
+
title="Peak Picking"
|
|
338
|
+
subtitle="Current experiment"
|
|
339
|
+
:model="modelDrivenSidebarModel"
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
</Variant>
|
|
343
|
+
|
|
344
|
+
<Variant title="Route Owned Shell">
|
|
345
|
+
<div style="padding: 2rem; height: 560px; display: flex; gap: 1rem; background: var(--bg-primary, #f1f5f9);">
|
|
346
|
+
<AppSidebar
|
|
347
|
+
variant="analysis"
|
|
348
|
+
title="Sequence"
|
|
349
|
+
subtitle="Acquisition run"
|
|
350
|
+
content-id="seqgen-sidebar"
|
|
351
|
+
show-when-empty
|
|
352
|
+
>
|
|
353
|
+
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
|
|
354
|
+
<FormField label="File Prefix">
|
|
355
|
+
<BaseInput v-model="sequencePrefix" size="sm" />
|
|
356
|
+
</FormField>
|
|
357
|
+
<FormField label="Run Mode">
|
|
358
|
+
<BaseSelect v-model="runMode" :options="runModeOptions" size="sm" />
|
|
359
|
+
</FormField>
|
|
360
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem;">
|
|
361
|
+
<BaseButton size="sm" variant="secondary">NEG</BaseButton>
|
|
362
|
+
<BaseButton size="sm" variant="secondary">VIAL</BaseButton>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<template #footer>
|
|
367
|
+
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
368
|
+
<BaseButton variant="cta" style="width: 100%;">Generate Sequence</BaseButton>
|
|
369
|
+
<BaseButton variant="ghost" size="sm" style="width: 100%;">Clear Draft</BaseButton>
|
|
370
|
+
</div>
|
|
371
|
+
</template>
|
|
372
|
+
</AppSidebar>
|
|
373
|
+
|
|
374
|
+
<div style="flex: 1; min-width: 0; border: 1px solid var(--border-color, #e5e7eb); border-radius: 0.5rem; background: var(--bg-card, #fff); padding: 1rem;">
|
|
375
|
+
<div style="display: flex; justify-content: space-between; gap: 1rem; align-items: center;">
|
|
376
|
+
<div>
|
|
377
|
+
<div style="font-size: 0.875rem; font-weight: 650; color: var(--text-primary, #111827);">MS queue</div>
|
|
378
|
+
<div style="font-size: 0.75rem; color: var(--text-muted, #6b7280);">{{ sequencePrefix }} · {{ runMode }}</div>
|
|
379
|
+
</div>
|
|
380
|
+
<BaseButton size="sm" variant="secondary">Preview CSV</BaseButton>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
</Variant>
|
|
385
|
+
|
|
287
386
|
<Variant title="Template Driven Controls">
|
|
288
387
|
<div style="padding: 2rem; min-height: 560px; display: flex; gap: 1rem; background: var(--bg-primary, #f1f5f9);">
|
|
289
388
|
<AppSidebar
|
|
@@ -335,6 +434,7 @@ function initSimpleToolkit() {
|
|
|
335
434
|
</div>
|
|
336
435
|
<AppSidebar
|
|
337
436
|
:panels="simplePanels"
|
|
437
|
+
:variant="state.variant"
|
|
338
438
|
:active-view="state.activeView"
|
|
339
439
|
:floating="state.floating"
|
|
340
440
|
:width="state.width"
|
|
@@ -365,6 +465,14 @@ function initSimpleToolkit() {
|
|
|
365
465
|
title="Active View"
|
|
366
466
|
:options="['analysis', 'results', 'settings'].map(v => ({ label: v, value: v }))"
|
|
367
467
|
/>
|
|
468
|
+
<HstSelect
|
|
469
|
+
v-model="state.variant"
|
|
470
|
+
title="Variant"
|
|
471
|
+
:options="[
|
|
472
|
+
{ label: 'Analysis', value: 'analysis' },
|
|
473
|
+
{ label: 'Default', value: 'default' },
|
|
474
|
+
]"
|
|
475
|
+
/>
|
|
368
476
|
<HstCheckbox v-model="state.floating" title="Floating" />
|
|
369
477
|
<HstText v-model="state.width" title="Width" />
|
|
370
478
|
<HstSelect
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* <AppSidebar :controls="controls" :active-view="activeTab" v-model="values" />
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
|
-
import { computed } from 'vue'
|
|
27
|
+
import { computed, ref, useSlots, type Slots } from 'vue'
|
|
28
28
|
import type { PillNavItem, SidebarToolSection } from '../types'
|
|
29
29
|
import type { FormEnhancements, FormSchema } from '../types/form-builder'
|
|
30
30
|
import {
|
|
@@ -42,15 +42,23 @@ import CollapsibleCard from './CollapsibleCard.vue'
|
|
|
42
42
|
import FormBuilder from './FormBuilder.vue'
|
|
43
43
|
|
|
44
44
|
interface Props {
|
|
45
|
+
/** Optional chrome title rendered above generated sections. */
|
|
46
|
+
title?: string
|
|
47
|
+
/** Optional secondary chrome copy rendered below title. */
|
|
48
|
+
subtitle?: string
|
|
49
|
+
/** Optional compact badge/count rendered in the chrome header. */
|
|
50
|
+
badge?: string | number
|
|
51
|
+
/** Visual preset for common plugin sidebars. `analysis` preserves the LEAF-style MINT analysis sidebar design language. */
|
|
52
|
+
variant?: 'default' | 'analysis'
|
|
45
53
|
/** Map of view IDs to their tool sections */
|
|
46
54
|
panels?: Record<string, SidebarToolSection[]>
|
|
47
55
|
/** Which view's panels to display */
|
|
48
56
|
activeView?: string
|
|
49
|
-
/** Floating variant with absolute positioning */
|
|
57
|
+
/** Floating variant with absolute positioning. Defaults to false for analysis variant. */
|
|
50
58
|
floating?: boolean
|
|
51
59
|
/** Compact layout: smaller headers, tighter spacing, no icon backgrounds */
|
|
52
60
|
dense?: boolean
|
|
53
|
-
/** Width when visible */
|
|
61
|
+
/** Width when visible. Defaults to 20rem for analysis variant, otherwise 280px. */
|
|
54
62
|
width?: string
|
|
55
63
|
/** Position sidebar on left or right side */
|
|
56
64
|
side?: 'left' | 'right'
|
|
@@ -60,7 +68,7 @@ interface Props {
|
|
|
60
68
|
forms?: Record<string, FormSchema>
|
|
61
69
|
/** Generated view IDs from useControlSchema(). Consumed for clean v-bind ergonomics. */
|
|
62
70
|
viewIds?: string[]
|
|
63
|
-
/** Generated
|
|
71
|
+
/** Generated AppTopBar pillNav-compatible view items from useControlSchema(). Consumed for clean v-bind ergonomics. */
|
|
64
72
|
viewItems?: PillNavItem[]
|
|
65
73
|
/** Default view ID used when activeView is omitted. */
|
|
66
74
|
defaultView?: string
|
|
@@ -70,6 +78,10 @@ interface Props {
|
|
|
70
78
|
controls?: ControlSchema
|
|
71
79
|
/** Options passed to compact control schema generation, including shared initialValues. */
|
|
72
80
|
controlOptions?: ControlWorkspaceOptions
|
|
81
|
+
/** DOM id for the scrollable content area. Use with Teleport when route/tab children own sidebar controls. */
|
|
82
|
+
contentId?: string
|
|
83
|
+
/** Render the sidebar shell even when no panel matches the active view. Useful for default-slot or Teleport-driven sidebars. */
|
|
84
|
+
showWhenEmpty?: boolean
|
|
73
85
|
/** Shared values for auto-rendered section forms. Supports default v-model. */
|
|
74
86
|
modelValue?: Record<string, unknown>
|
|
75
87
|
/** Shared values for auto-rendered section forms */
|
|
@@ -86,14 +98,30 @@ interface Props {
|
|
|
86
98
|
formReadonly?: boolean
|
|
87
99
|
/** Size passed to auto-rendered section forms */
|
|
88
100
|
formSize?: 'sm' | 'md' | 'lg'
|
|
101
|
+
/** Show a built-in collapse/expand button in the sidebar chrome. Defaults to true for analysis variant. */
|
|
102
|
+
collapsible?: boolean
|
|
103
|
+
/** Controlled collapsed state. */
|
|
104
|
+
collapsed?: boolean
|
|
105
|
+
/** Initial collapsed state when collapsed is uncontrolled. */
|
|
106
|
+
defaultCollapsed?: boolean
|
|
107
|
+
/** Width when collapsed. */
|
|
108
|
+
collapsedWidth?: string
|
|
109
|
+
/** Accessible label for the collapse action. */
|
|
110
|
+
collapseButtonLabel?: string
|
|
111
|
+
/** Accessible label for the expand action. */
|
|
112
|
+
expandButtonLabel?: string
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
const props = withDefaults(defineProps<Props>(), {
|
|
116
|
+
title: undefined,
|
|
117
|
+
subtitle: undefined,
|
|
118
|
+
badge: undefined,
|
|
119
|
+
variant: 'default',
|
|
92
120
|
panels: () => ({}),
|
|
93
121
|
activeView: '',
|
|
94
|
-
floating:
|
|
122
|
+
floating: undefined,
|
|
95
123
|
dense: false,
|
|
96
|
-
width:
|
|
124
|
+
width: undefined,
|
|
97
125
|
side: 'left',
|
|
98
126
|
toggleState: () => ({}),
|
|
99
127
|
forms: () => ({}),
|
|
@@ -102,6 +130,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
102
130
|
defaultView: '',
|
|
103
131
|
model: undefined,
|
|
104
132
|
controlOptions: () => ({}),
|
|
133
|
+
contentId: undefined,
|
|
134
|
+
showWhenEmpty: false,
|
|
105
135
|
modelValue: undefined,
|
|
106
136
|
values: () => ({}),
|
|
107
137
|
showFormActions: false,
|
|
@@ -109,8 +139,38 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
109
139
|
formDisabled: false,
|
|
110
140
|
formReadonly: false,
|
|
111
141
|
formSize: 'sm',
|
|
142
|
+
collapsible: undefined,
|
|
143
|
+
collapsed: undefined,
|
|
144
|
+
defaultCollapsed: false,
|
|
145
|
+
collapsedWidth: '3rem',
|
|
146
|
+
collapseButtonLabel: 'Collapse sidebar',
|
|
147
|
+
expandButtonLabel: 'Expand sidebar',
|
|
112
148
|
})
|
|
113
149
|
|
|
150
|
+
const emit = defineEmits<{
|
|
151
|
+
'update:toggle': [sectionId: string, value: boolean]
|
|
152
|
+
'update:modelValue': [values: Record<string, unknown>]
|
|
153
|
+
'update:values': [values: Record<string, unknown>]
|
|
154
|
+
'update:collapsed': [value: boolean]
|
|
155
|
+
'form-submit': [sectionId: string, values: Record<string, unknown>]
|
|
156
|
+
'form-cancel': [sectionId: string]
|
|
157
|
+
}>()
|
|
158
|
+
|
|
159
|
+
const slots: Slots = useSlots()
|
|
160
|
+
const internalCollapsed = ref(props.defaultCollapsed)
|
|
161
|
+
const collapsedModel = computed({
|
|
162
|
+
get: () => props.collapsed ?? internalCollapsed.value,
|
|
163
|
+
set: (value: boolean) => {
|
|
164
|
+
internalCollapsed.value = value
|
|
165
|
+
emit('update:collapsed', value)
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const isAnalysisVariant = computed(() => props.variant === 'analysis')
|
|
170
|
+
const resolvedFloating = computed(() => props.floating ?? !isAnalysisVariant.value)
|
|
171
|
+
const resolvedWidth = computed(() => props.width ?? (isAnalysisVariant.value ? '20rem' : '280px'))
|
|
172
|
+
const resolvedCollapsible = computed(() => props.collapsible ?? isAnalysisVariant.value)
|
|
173
|
+
|
|
114
174
|
const resolvedModel = computed<ControlModelBinding | undefined>(() => {
|
|
115
175
|
if (props.model === undefined) return undefined
|
|
116
176
|
return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)
|
|
@@ -168,28 +228,25 @@ const activeSections = computed<SidebarToolSection[]>(() => {
|
|
|
168
228
|
return resolvedPanels.value[resolvedActiveView.value]
|
|
169
229
|
})
|
|
170
230
|
|
|
171
|
-
const isVisible = computed(() =>
|
|
231
|
+
const isVisible = computed<boolean>(() =>
|
|
232
|
+
activeSections.value.length > 0 || props.showWhenEmpty || Boolean(slots.default),
|
|
233
|
+
)
|
|
172
234
|
|
|
173
|
-
const sidebarClasses = computed(() => [
|
|
235
|
+
const sidebarClasses = computed<string[]>(() => [
|
|
174
236
|
'mint-sidebar',
|
|
175
237
|
`mint-sidebar--${props.side}`,
|
|
176
|
-
|
|
238
|
+
`mint-sidebar--${props.variant}`,
|
|
239
|
+
resolvedFloating.value ? 'mint-sidebar--floating' : 'mint-sidebar--static',
|
|
177
240
|
props.dense ? 'mint-sidebar--dense' : '',
|
|
241
|
+
resolvedCollapsible.value ? 'mint-sidebar--collapsible' : '',
|
|
242
|
+
collapsedModel.value ? 'mint-sidebar--collapsed' : '',
|
|
178
243
|
!isVisible.value ? 'mint-sidebar--hidden' : '',
|
|
179
244
|
])
|
|
180
245
|
|
|
181
|
-
const sidebarStyle = computed(() => ({
|
|
182
|
-
width: props.
|
|
246
|
+
const sidebarStyle = computed<Record<string, string>>(() => ({
|
|
247
|
+
width: collapsedModel.value ? props.collapsedWidth : resolvedWidth.value,
|
|
183
248
|
}))
|
|
184
249
|
|
|
185
|
-
const emit = defineEmits<{
|
|
186
|
-
'update:toggle': [sectionId: string, value: boolean]
|
|
187
|
-
'update:modelValue': [values: Record<string, unknown>]
|
|
188
|
-
'update:values': [values: Record<string, unknown>]
|
|
189
|
-
'form-submit': [sectionId: string, values: Record<string, unknown>]
|
|
190
|
-
'form-cancel': [sectionId: string]
|
|
191
|
-
}>()
|
|
192
|
-
|
|
193
250
|
function handleFormUpdate(values: Record<string, unknown>) {
|
|
194
251
|
const nextValues = { ...resolvedValues.value, ...values }
|
|
195
252
|
emit('update:modelValue', nextValues)
|
|
@@ -204,6 +261,14 @@ function handleFormCancel(sectionId: string) {
|
|
|
204
261
|
emit('form-cancel', sectionId)
|
|
205
262
|
}
|
|
206
263
|
|
|
264
|
+
function toggleCollapsed() {
|
|
265
|
+
collapsedModel.value = !collapsedModel.value
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function expandCollapsed() {
|
|
269
|
+
collapsedModel.value = false
|
|
270
|
+
}
|
|
271
|
+
|
|
207
272
|
function mergeSidebarPanels(
|
|
208
273
|
generated: Record<string, SidebarToolSection[]>,
|
|
209
274
|
explicit: Record<string, SidebarToolSection[]>,
|
|
@@ -242,13 +307,54 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
242
307
|
:class="sidebarClasses"
|
|
243
308
|
:style="sidebarStyle"
|
|
244
309
|
>
|
|
245
|
-
<!-- Header slot -->
|
|
246
|
-
<div
|
|
247
|
-
|
|
310
|
+
<!-- Header slot / built-in chrome -->
|
|
311
|
+
<div
|
|
312
|
+
v-if="$slots.header || title || subtitle || badge !== undefined || resolvedCollapsible"
|
|
313
|
+
class="mint-sidebar__header"
|
|
314
|
+
>
|
|
315
|
+
<slot
|
|
316
|
+
name="header"
|
|
317
|
+
:collapsed="collapsedModel"
|
|
318
|
+
:toggle-collapsed="toggleCollapsed"
|
|
319
|
+
>
|
|
320
|
+
<div v-if="!collapsedModel" class="mint-sidebar__heading">
|
|
321
|
+
<div class="mint-sidebar__heading-copy">
|
|
322
|
+
<h2 v-if="title" class="mint-sidebar__title">{{ title }}</h2>
|
|
323
|
+
<p v-if="subtitle" class="mint-sidebar__subtitle">{{ subtitle }}</p>
|
|
324
|
+
</div>
|
|
325
|
+
<span v-if="badge !== undefined" class="mint-sidebar__badge">{{ badge }}</span>
|
|
326
|
+
</div>
|
|
327
|
+
<button
|
|
328
|
+
v-if="resolvedCollapsible"
|
|
329
|
+
type="button"
|
|
330
|
+
class="mint-sidebar__collapse-button"
|
|
331
|
+
:aria-label="collapsedModel ? expandButtonLabel : collapseButtonLabel"
|
|
332
|
+
:aria-expanded="!collapsedModel"
|
|
333
|
+
@click="toggleCollapsed"
|
|
334
|
+
>
|
|
335
|
+
<svg
|
|
336
|
+
class="mint-sidebar__collapse-icon"
|
|
337
|
+
:class="{ 'mint-sidebar__collapse-icon--collapsed': collapsedModel }"
|
|
338
|
+
viewBox="0 0 24 24"
|
|
339
|
+
fill="none"
|
|
340
|
+
stroke="currentColor"
|
|
341
|
+
stroke-width="2"
|
|
342
|
+
stroke-linecap="round"
|
|
343
|
+
stroke-linejoin="round"
|
|
344
|
+
aria-hidden="true"
|
|
345
|
+
>
|
|
346
|
+
<path d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
|
|
347
|
+
</svg>
|
|
348
|
+
</button>
|
|
349
|
+
</slot>
|
|
248
350
|
</div>
|
|
249
351
|
|
|
250
352
|
<!-- Tool sections -->
|
|
251
|
-
<div
|
|
353
|
+
<div
|
|
354
|
+
v-if="!collapsedModel"
|
|
355
|
+
:id="contentId"
|
|
356
|
+
class="mint-sidebar__sections"
|
|
357
|
+
>
|
|
252
358
|
<CollapsibleCard
|
|
253
359
|
v-for="section in activeSections"
|
|
254
360
|
:key="section.id"
|
|
@@ -280,10 +386,24 @@ function isControlModelBinding(model: ControlModel | ControlModelBinding): model
|
|
|
280
386
|
/>
|
|
281
387
|
</slot>
|
|
282
388
|
</CollapsibleCard>
|
|
389
|
+
|
|
390
|
+
<slot
|
|
391
|
+
:sections="activeSections"
|
|
392
|
+
:active-view="resolvedActiveView"
|
|
393
|
+
:values="resolvedValues"
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
<div v-else-if="$slots.collapsed" class="mint-sidebar__collapsed">
|
|
398
|
+
<slot
|
|
399
|
+
name="collapsed"
|
|
400
|
+
:sections="activeSections"
|
|
401
|
+
:expand="expandCollapsed"
|
|
402
|
+
/>
|
|
283
403
|
</div>
|
|
284
404
|
|
|
285
405
|
<!-- Footer slot -->
|
|
286
|
-
<div v-if="$slots.footer" class="mint-sidebar__footer">
|
|
406
|
+
<div v-if="!collapsedModel && $slots.footer" class="mint-sidebar__footer">
|
|
287
407
|
<slot name="footer" />
|
|
288
408
|
</div>
|
|
289
409
|
</aside>
|
|
@@ -231,10 +231,9 @@ const sampleAccountMenu: AccountMenuItem[] = [
|
|
|
231
231
|
</div>
|
|
232
232
|
</Variant>
|
|
233
233
|
|
|
234
|
-
<Variant title="
|
|
234
|
+
<Variant title="Navigation · Page selector">
|
|
235
235
|
<div style="padding: 2rem;">
|
|
236
236
|
<AppTopBar
|
|
237
|
-
plugin-name="IC50 Calculator"
|
|
238
237
|
title="Results"
|
|
239
238
|
:page-selector="samplePageSelector"
|
|
240
239
|
current-page-selector-id="plugins"
|
|
@@ -245,10 +244,9 @@ const sampleAccountMenu: AccountMenuItem[] = [
|
|
|
245
244
|
</div>
|
|
246
245
|
</Variant>
|
|
247
246
|
|
|
248
|
-
<Variant title="
|
|
247
|
+
<Variant title="Navigation · Pill nav">
|
|
249
248
|
<div style="padding: 2rem;">
|
|
250
249
|
<AppTopBar
|
|
251
|
-
plugin-name="Plate Analyzer"
|
|
252
250
|
title="Experiment View"
|
|
253
251
|
:pill-nav="samplePillNav.slice(0, 3)"
|
|
254
252
|
current-pill-id="workspace"
|
|
@@ -268,7 +266,6 @@ const sampleAccountMenu: AccountMenuItem[] = [
|
|
|
268
266
|
<div style="padding: 2rem;">
|
|
269
267
|
<ExperimentProvider>
|
|
270
268
|
<AppTopBar
|
|
271
|
-
plugin-name="IC50 Calculator"
|
|
272
269
|
title="Analysis"
|
|
273
270
|
variant="card"
|
|
274
271
|
:show-theme-toggle="true"
|