@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
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
/** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */
|
|
3
3
|
import { ref, computed, inject } from 'vue'
|
|
4
4
|
import type {
|
|
5
|
-
|
|
6
|
-
TopBarPageInput,
|
|
7
|
-
TopBarTab,
|
|
8
|
-
TopBarTabInput,
|
|
9
|
-
TopBarTabOption,
|
|
10
|
-
TopBarTabOptionInput,
|
|
5
|
+
PillNavOption,
|
|
11
6
|
TopBarSettingsConfig,
|
|
12
7
|
TopBarVariant,
|
|
13
8
|
PillNavItem,
|
|
@@ -21,20 +16,17 @@ import type {
|
|
|
21
16
|
} from '../types/components'
|
|
22
17
|
import type { PluginNavItem } from '../types/platform'
|
|
23
18
|
import { normalizeItemInput } from '../utils/items'
|
|
24
|
-
import { isPluginIconFormat } from '../utils/pluginIcon'
|
|
25
19
|
import ThemeToggle from './ThemeToggle.vue'
|
|
26
20
|
import SettingsModal from './SettingsModal.vue'
|
|
27
21
|
import ExperimentPopover from './ExperimentPopover.vue'
|
|
28
22
|
import ExperimentSelectorModal from './ExperimentSelectorModal.vue'
|
|
29
|
-
import
|
|
30
|
-
import
|
|
23
|
+
import AppTopBarPageSelectorInternal from './internal/AppTopBarPageSelectorInternal.vue'
|
|
24
|
+
import AppTopBarPillNavInternal from './internal/AppTopBarPillNavInternal.vue'
|
|
31
25
|
import AppAvatarMenu from './AppAvatarMenu.vue'
|
|
32
26
|
import AppPluginSwitcher from './AppPluginSwitcher.vue'
|
|
33
27
|
import PluginIcon from './PluginIcon.vue'
|
|
34
28
|
import { usePlatformContext } from '../composables/usePlatformContext'
|
|
35
29
|
import { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'
|
|
36
|
-
import { useEventListener } from '../composables/useEventListener'
|
|
37
|
-
import { useDropdownState } from '../composables/useDropdownState'
|
|
38
30
|
|
|
39
31
|
interface Props {
|
|
40
32
|
/** App or plugin title shown in the left title group when no page selector is present. */
|
|
@@ -45,10 +37,10 @@ interface Props {
|
|
|
45
37
|
showLogo?: boolean
|
|
46
38
|
/** Top bar visual treatment. */
|
|
47
39
|
variant?: TopBarVariant
|
|
48
|
-
/** Home link used by
|
|
40
|
+
/** Home link used by the brand icon. */
|
|
49
41
|
homePath?: string
|
|
50
42
|
|
|
51
|
-
/** Preferred route-level page switch entries for plugin and platform pages. */
|
|
43
|
+
/** Preferred route-level page switch entries for plugin and platform pages. Integrated plugins read platform plugin.nav_items metadata automatically when pageSelector is omitted. */
|
|
52
44
|
pageSelector?: PageSelectorItemInput[]
|
|
53
45
|
/** Active id for the preferred page selector. */
|
|
54
46
|
currentPageSelectorId?: string
|
|
@@ -67,17 +59,6 @@ interface Props {
|
|
|
67
59
|
/** Draw a notification dot on the notifications icon. */
|
|
68
60
|
hasNotificationDot?: boolean
|
|
69
61
|
|
|
70
|
-
/** Compatibility breadcrumb plugin name. Prefer pageSelector for route-level pages. */
|
|
71
|
-
pluginName?: string
|
|
72
|
-
/** Compatibility page dropdown items. Prefer pageSelector for new route-level navigation. */
|
|
73
|
-
pages?: TopBarPageInput[]
|
|
74
|
-
/** Active id for the compatibility page dropdown. */
|
|
75
|
-
currentPageId?: string
|
|
76
|
-
/** Compatibility center tabs. Prefer pillNav for new in-page modes. */
|
|
77
|
-
tabs?: TopBarTabInput[]
|
|
78
|
-
/** Active id for compatibility center tabs. */
|
|
79
|
-
currentTabId?: string
|
|
80
|
-
|
|
81
62
|
/** Show the theme toggle button. */
|
|
82
63
|
showThemeToggle?: boolean
|
|
83
64
|
/** Show the settings button and modal. */
|
|
@@ -118,14 +99,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
118
99
|
})
|
|
119
100
|
|
|
120
101
|
const emit = defineEmits<{
|
|
121
|
-
'page-select': [page: TopBarPage]
|
|
122
|
-
'tab-select': [tab: TopBarTab]
|
|
123
|
-
'tab-option-select': [option: TopBarTabOption, tab: TopBarTab]
|
|
124
102
|
'profile-click': []
|
|
125
103
|
'admin-click': []
|
|
126
|
-
// New
|
|
127
104
|
'page-selector-select': [page: PageSelectorItem]
|
|
128
105
|
'pill-select': [item: PillNavItem]
|
|
106
|
+
'pill-option-select': [option: PillNavOption, item: PillNavItem]
|
|
129
107
|
'plugin-switcher-select': [plugin: PluginSwitcherPlugin]
|
|
130
108
|
'plugin-switcher-install': []
|
|
131
109
|
'account-menu-select': [item: AccountMenuItem]
|
|
@@ -147,20 +125,17 @@ const profileInitial = computed(() => {
|
|
|
147
125
|
return 'U'
|
|
148
126
|
})
|
|
149
127
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
128
|
+
const hasPlatformPageSelector = computed(() =>
|
|
129
|
+
props.pageSelector === undefined && (plugin.value?.nav_items?.length ?? 0) > 1,
|
|
130
|
+
)
|
|
131
|
+
const hasPageSelector = computed(() => (props.pageSelector?.length ?? 0) > 1 || hasPlatformPageSelector.value)
|
|
153
132
|
const hasPluginSwitcher = computed(() => !!props.pluginSwitcher)
|
|
154
133
|
const hasPillNav = computed(() => !!props.pillNav?.length)
|
|
155
134
|
const hasAccountMenu = computed(() => !!props.accountMenu?.length)
|
|
156
|
-
const hasLegacyBreadcrumb = computed(
|
|
157
|
-
() => !hasPageSelector.value && !hasPluginSwitcher.value && (!!effectivePluginName.value || normalizedPages.value.length > 0),
|
|
158
|
-
)
|
|
159
135
|
const hasTitleGroup = computed(
|
|
160
136
|
() =>
|
|
161
137
|
!hasPageSelector.value &&
|
|
162
138
|
!hasPluginSwitcher.value &&
|
|
163
|
-
!hasLegacyBreadcrumb.value &&
|
|
164
139
|
!!props.title &&
|
|
165
140
|
!!props.subtitle
|
|
166
141
|
)
|
|
@@ -169,58 +144,28 @@ const hasTitleOnly = computed(
|
|
|
169
144
|
!hasPageSelector.value &&
|
|
170
145
|
!hasPluginSwitcher.value &&
|
|
171
146
|
!hasTitleGroup.value &&
|
|
172
|
-
!hasLegacyBreadcrumb.value &&
|
|
173
147
|
!!props.title,
|
|
174
148
|
)
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
?
|
|
178
|
-
:
|
|
149
|
+
const platformPageSelector = computed<PageSelectorItem[]>(() =>
|
|
150
|
+
hasPlatformPageSelector.value
|
|
151
|
+
? plugin.value?.nav_items?.map(pluginNavItemToPageSelectorItem) ?? []
|
|
152
|
+
: [],
|
|
179
153
|
)
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
154
|
+
const normalizedPageSelector = computed<PageSelectorItem[]>(() =>
|
|
155
|
+
props.pageSelector !== undefined
|
|
156
|
+
? props.pageSelector.map(normalizeItemInput)
|
|
157
|
+
: platformPageSelector.value,
|
|
158
|
+
)
|
|
159
|
+
const normalizedPillNav = computed<PillNavItemInput[]>(() => props.pillNav ?? [])
|
|
183
160
|
const normalizedSettingsTabs = computed<SettingsTab[]>(() =>
|
|
184
161
|
props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],
|
|
185
162
|
)
|
|
186
|
-
const
|
|
187
|
-
props.
|
|
188
|
-
?? (
|
|
189
|
-
?? (
|
|
190
|
-
)
|
|
191
|
-
const currentLegacyPage = computed(() =>
|
|
192
|
-
normalizedPages.value.find((page) => page.id === effectiveCurrentPageId.value),
|
|
193
|
-
)
|
|
194
|
-
const effectiveTitle = computed(() =>
|
|
195
|
-
props.title ?? (hasPlatformPages.value ? currentLegacyPage.value?.label : undefined),
|
|
163
|
+
const effectiveCurrentPageSelectorId = computed(() =>
|
|
164
|
+
props.currentPageSelectorId
|
|
165
|
+
?? (hasPlatformPageSelector.value ? currentItemIdFromLocation(normalizedPageSelector.value) : undefined)
|
|
166
|
+
?? (hasPlatformPageSelector.value ? normalizedPageSelector.value[0]?.id : undefined),
|
|
196
167
|
)
|
|
197
168
|
|
|
198
|
-
const {
|
|
199
|
-
isOpen: showPagesDropdown,
|
|
200
|
-
rootRef: dropdownRef,
|
|
201
|
-
close: closePagesDropdown,
|
|
202
|
-
toggle: togglePagesDropdownBase,
|
|
203
|
-
} = useDropdownState()
|
|
204
|
-
const openTabDropdown = ref<string | null>(null)
|
|
205
|
-
const tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())
|
|
206
|
-
|
|
207
|
-
function togglePagesDropdown() {
|
|
208
|
-
togglePagesDropdownBase()
|
|
209
|
-
openTabDropdown.value = null
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function normalizeTopBarTabInput(tab: TopBarTabInput): TopBarTab {
|
|
213
|
-
const normalized = normalizeItemInput<Omit<TopBarTab, 'children'> & { children?: TopBarTabOptionInput[] }>(tab)
|
|
214
|
-
return {
|
|
215
|
-
...normalized,
|
|
216
|
-
children: normalized.children?.map(normalizeItemInput),
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function isSvgTabIcon(icon?: string): icon is string {
|
|
221
|
-
return !!icon && (icon.startsWith('M') || icon.startsWith('m'))
|
|
222
|
-
}
|
|
223
|
-
|
|
224
169
|
function normalizeNavPath(path?: string): string {
|
|
225
170
|
const raw = path?.trim() || '/'
|
|
226
171
|
const prefixed = raw.startsWith('/') ? raw : `/${raw}`
|
|
@@ -233,14 +178,14 @@ function pageIdFromPath(path: string, fallback: string): string {
|
|
|
233
178
|
return normalized.replace(/^\/+/, '').replace(/\/+/g, '-') || fallback
|
|
234
179
|
}
|
|
235
180
|
|
|
236
|
-
function
|
|
181
|
+
function pluginNavItemToPageSelectorItem(item: PluginNavItem, index: number): PageSelectorItem {
|
|
237
182
|
const path = normalizeNavPath(item.path)
|
|
238
183
|
return {
|
|
239
184
|
id: item.id || pageIdFromPath(path, `page-${index + 1}`),
|
|
240
185
|
label: item.label,
|
|
241
186
|
to: path,
|
|
242
187
|
icon: item.icon || plugin.value?.icon,
|
|
243
|
-
|
|
188
|
+
hint: item.description || plugin.value?.name,
|
|
244
189
|
}
|
|
245
190
|
}
|
|
246
191
|
|
|
@@ -256,59 +201,11 @@ function currentPagePath(): string | undefined {
|
|
|
256
201
|
return pathname
|
|
257
202
|
}
|
|
258
203
|
|
|
259
|
-
function
|
|
204
|
+
function currentItemIdFromLocation(pages: Array<Pick<PageSelectorItem, 'id' | 'to' | 'href'>>): string | undefined {
|
|
260
205
|
const path = currentPagePath()
|
|
261
206
|
if (!path) return undefined
|
|
262
207
|
return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id
|
|
263
208
|
}
|
|
264
|
-
|
|
265
|
-
function handlePageClick(page: TopBarPage) {
|
|
266
|
-
if (page.disabled) return
|
|
267
|
-
emit('page-select', page)
|
|
268
|
-
closePagesDropdown()
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function toggleTabDropdown(tabId: string) {
|
|
272
|
-
closePagesDropdown()
|
|
273
|
-
openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function handleTabClick(tab: TopBarTab) {
|
|
277
|
-
if (tab.disabled) return
|
|
278
|
-
if (tab.children?.length) {
|
|
279
|
-
toggleTabDropdown(tab.id)
|
|
280
|
-
} else {
|
|
281
|
-
emit('tab-select', tab)
|
|
282
|
-
openTabDropdown.value = null
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function handleTabOptionClick(option: TopBarTabOption, tab: TopBarTab) {
|
|
287
|
-
if (option.disabled) return
|
|
288
|
-
emit('tab-option-select', option, tab)
|
|
289
|
-
openTabDropdown.value = null
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function setTabDropdownRef(el: HTMLElement | null, tabId: string) {
|
|
293
|
-
if (el) {
|
|
294
|
-
tabDropdownRefs.value.set(tabId, el)
|
|
295
|
-
} else {
|
|
296
|
-
tabDropdownRefs.value.delete(tabId)
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function handleClickOutside(event: MouseEvent) {
|
|
301
|
-
const target = event.target as Node
|
|
302
|
-
|
|
303
|
-
if (openTabDropdown.value !== null) {
|
|
304
|
-
const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))
|
|
305
|
-
if (!clickedInside) {
|
|
306
|
-
openTabDropdown.value = null
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
useEventListener(() => document, 'click', handleClickOutside)
|
|
312
209
|
</script>
|
|
313
210
|
|
|
314
211
|
<template>
|
|
@@ -398,10 +295,10 @@ useEventListener(() => document, 'click', handleClickOutside)
|
|
|
398
295
|
@select="emit('plugin-switcher-select', $event)"
|
|
399
296
|
@install-click="emit('plugin-switcher-install')"
|
|
400
297
|
/>
|
|
401
|
-
<
|
|
298
|
+
<AppTopBarPageSelectorInternal
|
|
402
299
|
v-else-if="hasPageSelector"
|
|
403
300
|
:pages="normalizedPageSelector"
|
|
404
|
-
:current-page-id="
|
|
301
|
+
:current-page-id="effectiveCurrentPageSelectorId"
|
|
405
302
|
@select="emit('page-selector-select', $event)"
|
|
406
303
|
>
|
|
407
304
|
<template v-if="$slots['page-selector-icon']" #icon="slotProps">
|
|
@@ -410,121 +307,14 @@ useEventListener(() => document, 'click', handleClickOutside)
|
|
|
410
307
|
<template v-if="$slots['page-selector-item-icon']" #item-icon="slotProps">
|
|
411
308
|
<slot name="page-selector-item-icon" v-bind="slotProps" />
|
|
412
309
|
</template>
|
|
413
|
-
</
|
|
310
|
+
</AppTopBarPageSelectorInternal>
|
|
414
311
|
|
|
415
|
-
<!-- Left:
|
|
312
|
+
<!-- Left: title -->
|
|
416
313
|
<div v-if="hasTitleGroup" class="mint-topbar-title-group">
|
|
417
314
|
<span class="mint-topbar-title">{{ title }}</span>
|
|
418
315
|
<span class="mint-topbar-subtitle">{{ subtitle }}</span>
|
|
419
316
|
</div>
|
|
420
317
|
|
|
421
|
-
<div v-else-if="hasLegacyBreadcrumb" ref="dropdownRef" class="mint-topbar-breadcrumb">
|
|
422
|
-
<button
|
|
423
|
-
v-if="normalizedPages.length"
|
|
424
|
-
type="button"
|
|
425
|
-
class="mint-topbar-plugin-name"
|
|
426
|
-
@click.stop="togglePagesDropdown"
|
|
427
|
-
>
|
|
428
|
-
{{ effectivePluginName }}
|
|
429
|
-
<svg
|
|
430
|
-
class="mint-topbar-chevron"
|
|
431
|
-
:class="{ 'mint-topbar-chevron--open': showPagesDropdown }"
|
|
432
|
-
width="16"
|
|
433
|
-
height="16"
|
|
434
|
-
viewBox="0 0 24 24"
|
|
435
|
-
fill="none"
|
|
436
|
-
stroke="currentColor"
|
|
437
|
-
stroke-width="2"
|
|
438
|
-
stroke-linecap="round"
|
|
439
|
-
stroke-linejoin="round"
|
|
440
|
-
>
|
|
441
|
-
<path d="m6 9 6 6 6-6" />
|
|
442
|
-
</svg>
|
|
443
|
-
</button>
|
|
444
|
-
<span v-else class="mint-topbar-plugin-name--static">{{ effectivePluginName }}</span>
|
|
445
|
-
|
|
446
|
-
<svg
|
|
447
|
-
v-if="effectiveTitle"
|
|
448
|
-
class="mint-topbar-separator"
|
|
449
|
-
width="16"
|
|
450
|
-
height="16"
|
|
451
|
-
viewBox="0 0 24 24"
|
|
452
|
-
fill="none"
|
|
453
|
-
stroke="currentColor"
|
|
454
|
-
stroke-width="2"
|
|
455
|
-
stroke-linecap="round"
|
|
456
|
-
stroke-linejoin="round"
|
|
457
|
-
>
|
|
458
|
-
<path d="m9 18 6-6-6-6" />
|
|
459
|
-
</svg>
|
|
460
|
-
<span v-if="effectiveTitle" class="mint-topbar-current-page">{{ effectiveTitle }}</span>
|
|
461
|
-
|
|
462
|
-
<div v-show="showPagesDropdown" class="mint-topbar-dropdown">
|
|
463
|
-
<template v-for="page in normalizedPages" :key="page.id">
|
|
464
|
-
<a
|
|
465
|
-
v-if="page.href"
|
|
466
|
-
:href="page.href"
|
|
467
|
-
:class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
|
|
468
|
-
@click="closePagesDropdown"
|
|
469
|
-
>
|
|
470
|
-
<span class="mint-topbar-dropdown-item__page">
|
|
471
|
-
<PluginIcon
|
|
472
|
-
v-if="isPluginIconFormat(page.icon)"
|
|
473
|
-
class="mint-topbar-dropdown-item__icon"
|
|
474
|
-
:icon="page.icon"
|
|
475
|
-
size="sm"
|
|
476
|
-
variant="tinted"
|
|
477
|
-
/>
|
|
478
|
-
<span class="mint-topbar-dropdown-item__copy">
|
|
479
|
-
<span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
|
|
480
|
-
<span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
|
|
481
|
-
</span>
|
|
482
|
-
</span>
|
|
483
|
-
</a>
|
|
484
|
-
<router-link
|
|
485
|
-
v-else-if="page.to"
|
|
486
|
-
:to="page.to"
|
|
487
|
-
:class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
|
|
488
|
-
@click="closePagesDropdown"
|
|
489
|
-
>
|
|
490
|
-
<span class="mint-topbar-dropdown-item__page">
|
|
491
|
-
<PluginIcon
|
|
492
|
-
v-if="isPluginIconFormat(page.icon)"
|
|
493
|
-
class="mint-topbar-dropdown-item__icon"
|
|
494
|
-
:icon="page.icon"
|
|
495
|
-
size="sm"
|
|
496
|
-
variant="tinted"
|
|
497
|
-
/>
|
|
498
|
-
<span class="mint-topbar-dropdown-item__copy">
|
|
499
|
-
<span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
|
|
500
|
-
<span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
|
|
501
|
-
</span>
|
|
502
|
-
</span>
|
|
503
|
-
</router-link>
|
|
504
|
-
<button
|
|
505
|
-
v-else
|
|
506
|
-
type="button"
|
|
507
|
-
:class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
|
|
508
|
-
@click="handlePageClick(page)"
|
|
509
|
-
>
|
|
510
|
-
<span class="mint-topbar-dropdown-item__page">
|
|
511
|
-
<PluginIcon
|
|
512
|
-
v-if="isPluginIconFormat(page.icon)"
|
|
513
|
-
class="mint-topbar-dropdown-item__icon"
|
|
514
|
-
:icon="page.icon"
|
|
515
|
-
size="sm"
|
|
516
|
-
variant="tinted"
|
|
517
|
-
/>
|
|
518
|
-
<span class="mint-topbar-dropdown-item__copy">
|
|
519
|
-
<span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
|
|
520
|
-
<span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
|
|
521
|
-
</span>
|
|
522
|
-
</span>
|
|
523
|
-
</button>
|
|
524
|
-
</template>
|
|
525
|
-
</div>
|
|
526
|
-
</div>
|
|
527
|
-
|
|
528
318
|
<span v-else-if="hasTitleOnly" class="mint-topbar__title-only">{{ title }}</span>
|
|
529
319
|
|
|
530
320
|
<!-- Nav slot (inline, after brand/selector) -->
|
|
@@ -533,187 +323,16 @@ useEventListener(() => document, 'click', handleClickOutside)
|
|
|
533
323
|
<!-- Center: pill nav (new) -->
|
|
534
324
|
<div v-if="hasPillNav || $slots.center" class="mint-topbar__center">
|
|
535
325
|
<slot name="center">
|
|
536
|
-
<
|
|
326
|
+
<AppTopBarPillNavInternal
|
|
537
327
|
v-if="hasPillNav && pillNav"
|
|
538
328
|
:items="normalizedPillNav"
|
|
539
329
|
:current-item-id="currentPillId"
|
|
540
330
|
@select="emit('pill-select', $event)"
|
|
331
|
+
@option-select="(option, item) => emit('pill-option-select', option, item)"
|
|
541
332
|
/>
|
|
542
333
|
</slot>
|
|
543
334
|
</div>
|
|
544
335
|
|
|
545
|
-
<!-- Center: classic tabs (when no pillNav) — wrapped in the same centered
|
|
546
|
-
container as AppPillNav so classic :tabs consumers get centered pill
|
|
547
|
-
layout without migrating to :pill-nav. -->
|
|
548
|
-
<div v-if="!hasPillNav && normalizedTabs.length" class="mint-topbar__center">
|
|
549
|
-
<div class="mint-topbar__tabs">
|
|
550
|
-
<template v-for="tab in normalizedTabs" :key="tab.id">
|
|
551
|
-
<div
|
|
552
|
-
:ref="(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null"
|
|
553
|
-
class="mint-topbar-tab-wrapper"
|
|
554
|
-
>
|
|
555
|
-
<button
|
|
556
|
-
v-if="tab.children?.length"
|
|
557
|
-
type="button"
|
|
558
|
-
:class="[
|
|
559
|
-
'mint-topbar-tab',
|
|
560
|
-
{ 'mint-topbar-tab--active': tab.id === currentTabId || tab.children.some(c => c.id === currentTabId) },
|
|
561
|
-
{ 'mint-topbar-tab--disabled': tab.disabled }
|
|
562
|
-
]"
|
|
563
|
-
@click.stop="handleTabClick(tab)"
|
|
564
|
-
>
|
|
565
|
-
<svg
|
|
566
|
-
v-if="isSvgTabIcon(tab.icon)"
|
|
567
|
-
class="mint-topbar-tab-icon"
|
|
568
|
-
viewBox="0 0 24 24"
|
|
569
|
-
fill="none"
|
|
570
|
-
stroke="currentColor"
|
|
571
|
-
stroke-width="2"
|
|
572
|
-
stroke-linecap="round"
|
|
573
|
-
stroke-linejoin="round"
|
|
574
|
-
aria-hidden="true"
|
|
575
|
-
>
|
|
576
|
-
<path :d="tab.icon" />
|
|
577
|
-
</svg>
|
|
578
|
-
{{ tab.label }}
|
|
579
|
-
<svg
|
|
580
|
-
class="mint-topbar-tab-chevron"
|
|
581
|
-
:class="{ 'mint-topbar-tab-chevron--open': openTabDropdown === tab.id }"
|
|
582
|
-
width="14"
|
|
583
|
-
height="14"
|
|
584
|
-
viewBox="0 0 24 24"
|
|
585
|
-
fill="none"
|
|
586
|
-
stroke="currentColor"
|
|
587
|
-
stroke-width="2"
|
|
588
|
-
stroke-linecap="round"
|
|
589
|
-
stroke-linejoin="round"
|
|
590
|
-
>
|
|
591
|
-
<path d="m6 9 6 6 6-6" />
|
|
592
|
-
</svg>
|
|
593
|
-
</button>
|
|
594
|
-
|
|
595
|
-
<a
|
|
596
|
-
v-else-if="tab.href"
|
|
597
|
-
:href="tab.href"
|
|
598
|
-
:class="[
|
|
599
|
-
'mint-topbar-tab',
|
|
600
|
-
{ 'mint-topbar-tab--active': tab.id === currentTabId },
|
|
601
|
-
{ 'mint-topbar-tab--disabled': tab.disabled }
|
|
602
|
-
]"
|
|
603
|
-
>
|
|
604
|
-
<svg
|
|
605
|
-
v-if="isSvgTabIcon(tab.icon)"
|
|
606
|
-
class="mint-topbar-tab-icon"
|
|
607
|
-
viewBox="0 0 24 24"
|
|
608
|
-
fill="none"
|
|
609
|
-
stroke="currentColor"
|
|
610
|
-
stroke-width="2"
|
|
611
|
-
stroke-linecap="round"
|
|
612
|
-
stroke-linejoin="round"
|
|
613
|
-
aria-hidden="true"
|
|
614
|
-
>
|
|
615
|
-
<path :d="tab.icon" />
|
|
616
|
-
</svg>
|
|
617
|
-
{{ tab.label }}
|
|
618
|
-
</a>
|
|
619
|
-
<router-link
|
|
620
|
-
v-else-if="tab.to"
|
|
621
|
-
:to="tab.to"
|
|
622
|
-
:class="[
|
|
623
|
-
'mint-topbar-tab',
|
|
624
|
-
{ 'mint-topbar-tab--active': tab.id === currentTabId },
|
|
625
|
-
{ 'mint-topbar-tab--disabled': tab.disabled }
|
|
626
|
-
]"
|
|
627
|
-
>
|
|
628
|
-
<svg
|
|
629
|
-
v-if="isSvgTabIcon(tab.icon)"
|
|
630
|
-
class="mint-topbar-tab-icon"
|
|
631
|
-
viewBox="0 0 24 24"
|
|
632
|
-
fill="none"
|
|
633
|
-
stroke="currentColor"
|
|
634
|
-
stroke-width="2"
|
|
635
|
-
stroke-linecap="round"
|
|
636
|
-
stroke-linejoin="round"
|
|
637
|
-
aria-hidden="true"
|
|
638
|
-
>
|
|
639
|
-
<path :d="tab.icon" />
|
|
640
|
-
</svg>
|
|
641
|
-
{{ tab.label }}
|
|
642
|
-
</router-link>
|
|
643
|
-
<button
|
|
644
|
-
v-else
|
|
645
|
-
type="button"
|
|
646
|
-
:class="[
|
|
647
|
-
'mint-topbar-tab',
|
|
648
|
-
{ 'mint-topbar-tab--active': tab.id === currentTabId },
|
|
649
|
-
{ 'mint-topbar-tab--disabled': tab.disabled }
|
|
650
|
-
]"
|
|
651
|
-
@click="handleTabClick(tab)"
|
|
652
|
-
>
|
|
653
|
-
<svg
|
|
654
|
-
v-if="isSvgTabIcon(tab.icon)"
|
|
655
|
-
class="mint-topbar-tab-icon"
|
|
656
|
-
viewBox="0 0 24 24"
|
|
657
|
-
fill="none"
|
|
658
|
-
stroke="currentColor"
|
|
659
|
-
stroke-width="2"
|
|
660
|
-
stroke-linecap="round"
|
|
661
|
-
stroke-linejoin="round"
|
|
662
|
-
aria-hidden="true"
|
|
663
|
-
>
|
|
664
|
-
<path :d="tab.icon" />
|
|
665
|
-
</svg>
|
|
666
|
-
{{ tab.label }}
|
|
667
|
-
</button>
|
|
668
|
-
|
|
669
|
-
<div v-if="tab.children?.length" v-show="openTabDropdown === tab.id" class="mint-topbar-tab-dropdown">
|
|
670
|
-
<template v-for="option in tab.children" :key="option.id">
|
|
671
|
-
<a
|
|
672
|
-
v-if="option.href"
|
|
673
|
-
:href="option.href"
|
|
674
|
-
:class="[
|
|
675
|
-
'mint-topbar-dropdown-item',
|
|
676
|
-
{ 'mint-topbar-dropdown-item--active': option.id === currentTabId },
|
|
677
|
-
{ 'mint-topbar-dropdown-item--disabled': option.disabled }
|
|
678
|
-
]"
|
|
679
|
-
@click="openTabDropdown = null"
|
|
680
|
-
>
|
|
681
|
-
<span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
|
|
682
|
-
<span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
|
|
683
|
-
</a>
|
|
684
|
-
<router-link
|
|
685
|
-
v-else-if="option.to"
|
|
686
|
-
:to="option.to"
|
|
687
|
-
:class="[
|
|
688
|
-
'mint-topbar-dropdown-item',
|
|
689
|
-
{ 'mint-topbar-dropdown-item--active': option.id === currentTabId },
|
|
690
|
-
{ 'mint-topbar-dropdown-item--disabled': option.disabled }
|
|
691
|
-
]"
|
|
692
|
-
@click="openTabDropdown = null"
|
|
693
|
-
>
|
|
694
|
-
<span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
|
|
695
|
-
<span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
|
|
696
|
-
</router-link>
|
|
697
|
-
<button
|
|
698
|
-
v-else
|
|
699
|
-
type="button"
|
|
700
|
-
:class="[
|
|
701
|
-
'mint-topbar-dropdown-item',
|
|
702
|
-
{ 'mint-topbar-dropdown-item--active': option.id === currentTabId },
|
|
703
|
-
{ 'mint-topbar-dropdown-item--disabled': option.disabled }
|
|
704
|
-
]"
|
|
705
|
-
@click="handleTabOptionClick(option, tab)"
|
|
706
|
-
>
|
|
707
|
-
<span class="mint-topbar-dropdown-item__label">{{ option.label }}</span>
|
|
708
|
-
<span v-if="option.description" class="mint-topbar-dropdown-item__description">{{ option.description }}</span>
|
|
709
|
-
</button>
|
|
710
|
-
</template>
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
</template>
|
|
714
|
-
</div>
|
|
715
|
-
</div>
|
|
716
|
-
|
|
717
336
|
<!-- Right section -->
|
|
718
337
|
<div class="mint-topbar__right">
|
|
719
338
|
<span v-if="showStandaloneLabel && isStandalone && !appExperiment" class="mint-topbar__standalone-badge">
|
|
@@ -722,15 +341,7 @@ useEventListener(() => document, 'click', handleClickOutside)
|
|
|
722
341
|
|
|
723
342
|
<ExperimentPopover
|
|
724
343
|
v-if="appExperiment && !isStandalone"
|
|
725
|
-
|
|
726
|
-
:experiment-code="appExperiment.experimentCode.value"
|
|
727
|
-
:experiment-status="appExperiment.experimentStatus.value"
|
|
728
|
-
:show-save="appExperiment.showSave.value"
|
|
729
|
-
:show-detach="appExperiment.showDetach.value"
|
|
730
|
-
:save-disabled="appExperiment.saveDisabled.value"
|
|
731
|
-
:save-disabled-message="appExperiment.saveDisabledMessage.value"
|
|
732
|
-
:save-loading="appExperiment.saveLoading.value"
|
|
733
|
-
:save-success-message="appExperiment.saveSuccessMessage.value"
|
|
344
|
+
v-bind="appExperiment.popover.value"
|
|
734
345
|
@select="appExperiment.openModal()"
|
|
735
346
|
@save="appExperiment.handleSave()"
|
|
736
347
|
@detach="appExperiment.handleDetach()"
|
|
@@ -847,8 +458,7 @@ useEventListener(() => document, 'click', handleClickOutside)
|
|
|
847
458
|
|
|
848
459
|
<ExperimentSelectorModal
|
|
849
460
|
v-if="appExperiment && !isStandalone"
|
|
850
|
-
|
|
851
|
-
:current-experiment-id="appExperiment.experimentId.value"
|
|
461
|
+
v-bind="appExperiment.selectorModal.value"
|
|
852
462
|
@update:model-value="$event ? appExperiment.openModal() : appExperiment.closeModal()"
|
|
853
463
|
@select="appExperiment.handleSelect($event)"
|
|
854
464
|
@deselect="appExperiment.handleDetach()"
|
|
@@ -69,14 +69,14 @@ function initState() {
|
|
|
69
69
|
<div class="experiment-component-layout">
|
|
70
70
|
<div class="experiment-component-panel experiment-component-panel--plate">
|
|
71
71
|
<WellPlate
|
|
72
|
-
v-bind="bindings.
|
|
72
|
+
v-bind="bindings.componentBindingsById['plate-map:WellPlate'].props"
|
|
73
73
|
size="sm"
|
|
74
74
|
readonly
|
|
75
75
|
/>
|
|
76
76
|
</div>
|
|
77
77
|
<div class="experiment-component-panel experiment-component-panel--dose">
|
|
78
78
|
<DoseCalculator
|
|
79
|
-
v-bind="bindings.
|
|
79
|
+
v-bind="bindings.componentBindingsById['dose-response:DoseCalculator'].props"
|
|
80
80
|
/>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
@@ -4,6 +4,7 @@ import { computed } from 'vue'
|
|
|
4
4
|
import {
|
|
5
5
|
extractTemplateCollection,
|
|
6
6
|
getBioTemplateComponentProps,
|
|
7
|
+
toBioTemplateComponentBindingsById,
|
|
7
8
|
toBioTemplateComponentProps,
|
|
8
9
|
toBioTemplateComponentPropsByComponent,
|
|
9
10
|
toBioTemplateComponentPropsById,
|
|
@@ -135,6 +136,7 @@ const templateSummaries = computed(() =>
|
|
|
135
136
|
}))
|
|
136
137
|
)
|
|
137
138
|
const componentProps = computed(() => toBioTemplateComponentProps(props.target))
|
|
139
|
+
const componentBindingsById = computed(() => toBioTemplateComponentBindingsById(props.target))
|
|
138
140
|
const componentPropsById = computed(() => toBioTemplateComponentPropsById(props.target))
|
|
139
141
|
const componentPropsByComponent = computed(() => toBioTemplateComponentPropsByComponent(props.target))
|
|
140
142
|
const renderer = computed(() => ({ target: props.target }))
|
|
@@ -146,6 +148,7 @@ function getComponentProps(component: string, options?: BioTemplateComponentProp
|
|
|
146
148
|
interface BioTemplateExperimentWorkspaceBindings {
|
|
147
149
|
renderer: { target: BioTemplateWorkspaceTarget }
|
|
148
150
|
componentProps: ReturnType<typeof toBioTemplateComponentProps>
|
|
151
|
+
componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>
|
|
149
152
|
componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>
|
|
150
153
|
componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>
|
|
151
154
|
getComponentProps: typeof getComponentProps
|
|
@@ -154,6 +157,7 @@ interface BioTemplateExperimentWorkspaceBindings {
|
|
|
154
157
|
const bindings = computed<BioTemplateExperimentWorkspaceBindings>(() => ({
|
|
155
158
|
renderer: renderer.value,
|
|
156
159
|
componentProps: componentProps.value,
|
|
160
|
+
componentBindingsById: componentBindingsById.value,
|
|
157
161
|
componentPropsById: componentPropsById.value,
|
|
158
162
|
componentPropsByComponent: componentPropsByComponent.value,
|
|
159
163
|
getComponentProps,
|
|
@@ -163,6 +167,7 @@ interface BioTemplateExperimentWorkspaceSlotProps {
|
|
|
163
167
|
target: BioTemplateWorkspaceTarget
|
|
164
168
|
bindings: BioTemplateExperimentWorkspaceBindings
|
|
165
169
|
componentProps: ReturnType<typeof toBioTemplateComponentProps>
|
|
170
|
+
componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>
|
|
166
171
|
componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>
|
|
167
172
|
componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>
|
|
168
173
|
getComponentProps: typeof getComponentProps
|
|
@@ -255,6 +260,7 @@ function handleSave() {
|
|
|
255
260
|
:target="target"
|
|
256
261
|
:bindings="bindings"
|
|
257
262
|
:component-props="componentProps"
|
|
263
|
+
:component-bindings-by-id="componentBindingsById"
|
|
258
264
|
:component-props-by-id="componentPropsById"
|
|
259
265
|
:component-props-by-component="componentPropsByComponent"
|
|
260
266
|
:get-component-props="getComponentProps"
|