@morscherlab/mint-sdk 1.2.0-beta.4 → 1.2.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/dist/{EmptyState-p90quq8Z.js → EmptyState-NBqf2_8z.js} +2 -2
- package/dist/{EmptyState-p90quq8Z.js.map → EmptyState-NBqf2_8z.js.map} +1 -1
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-CfOdh5Rm.js} +3 -3
- package/dist/{ExperimentPopover-tLwJ7XF4.js.map → ExperimentPopover-CfOdh5Rm.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-DvIagrxY.js} +4 -4
- package/dist/{ExperimentSelectorModal-B6P2msT4.js.map → ExperimentSelectorModal-DvIagrxY.js.map} +1 -1
- package/dist/{SettingsModal-CkNM8qAI.js → SettingsModal-W3TYfnP9.js} +4 -7
- package/dist/SettingsModal-W3TYfnP9.js.map +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
- package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
- package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
- package/dist/components/DropdownButton.vue.d.ts +2 -0
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
- package/dist/components/SecretInput.vue.d.ts +28 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
- package/dist/{components-MGNL6tRn.js → components-B80gV0P6.js} +575 -561
- package/dist/components-B80gV0P6.js.map +1 -0
- package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
- package/dist/composables/index.d.ts +5 -0
- package/dist/composables/index.js +9 -9
- package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
- package/dist/composables/useAsync.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
- package/dist/composables/useCommandHistory.d.ts +5 -1
- package/dist/composables/useFocusTrap.d.ts +21 -0
- package/dist/composables/useMenuKeyboard.d.ts +29 -0
- package/dist/composables/useOptimisticMutation.d.ts +5 -1
- package/dist/composables/usePluginWorkspace.d.ts +51 -0
- package/dist/composables/useResourceCrud.d.ts +5 -1
- package/dist/composables/useRovingFocus.d.ts +24 -0
- package/dist/composables/useWellPainting.d.ts +5 -1
- package/dist/composables/useWellPlateAdapter.d.ts +5 -1
- package/dist/composables/useWellPlateValidation.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +188 -0
- package/dist/{composables-CKxOcvgh.js → composables-C8mmD5ta.js} +65 -17
- package/dist/composables-C8mmD5ta.js.map +1 -0
- package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
- package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +13 -13
- package/dist/install.js +7 -7
- package/dist/styles.css +100 -49
- package/dist/templates/builderReadUtils.d.ts +1 -1
- package/dist/templates/index.js +4 -4
- package/dist/{templates-BTJ9C9Kb.js → templates-CK72aKO1.js} +14 -23
- package/dist/templates-CK72aKO1.js.map +1 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/{useControlSchema-CXhxTul7.js → useControlSchema-_OVhavKq.js} +280 -63
- package/dist/useControlSchema-_OVhavKq.js.map +1 -0
- package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-D5G4UvWd.js} +2 -2
- package/dist/{useDropdownState-DSb3BC3N.js.map → useDropdownState-D5G4UvWd.js.map} +1 -1
- package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-DSEckf_g.js} +3 -3
- package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-DSEckf_g.js.map} +1 -1
- package/dist/{useFormBuilder-CuOFB3aP.js → useFormBuilder-Bd714C7y.js} +2 -2
- package/dist/{useFormBuilder-CuOFB3aP.js.map → useFormBuilder-Bd714C7y.js.map} +1 -1
- package/dist/{useProtocolTemplates-BWhQIWV5.js → useProtocolTemplates-Dq5Pzwyd.js} +481 -46
- package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +1 -0
- package/dist/utils/pluginSecrets.d.ts +53 -0
- package/dist/utils/records.d.ts +4 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
- package/src/__tests__/components/BaseTabs.test.ts +67 -0
- package/src/__tests__/components/DropdownButton.test.ts +134 -0
- package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
- package/src/__tests__/components/SecretInput.test.ts +87 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
- package/src/__tests__/composables/usePluginClient.test.ts +140 -0
- package/src/components/AppAvatarMenu.vue +34 -3
- package/src/components/BaseModal.vue +8 -56
- package/src/components/BaseTabs.vue +26 -1
- package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
- package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
- package/src/components/ComponentBindingRenderer.vue +1 -3
- package/src/components/ControlWorkspaceView.vue +11 -22
- package/src/components/DoseDesignWorkspaceView.vue +6 -25
- package/src/components/DropdownButton.vue +32 -2
- package/src/components/PluginWorkspaceView.props.ts +38 -0
- package/src/components/PluginWorkspaceView.vue +53 -224
- package/src/components/SecretInput.story.vue +79 -0
- package/src/components/SecretInput.vue +150 -0
- package/src/components/index.ts +9 -0
- package/src/components/internal/WellEditPopupInternal.vue +20 -1
- package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
- package/src/components/internal/WorkspaceShellInternal.vue +125 -0
- package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
- package/src/composables/experimentDesignData.ts +1 -3
- package/src/composables/flattenWorkspaceBindings.ts +104 -0
- package/src/composables/formBuilderRegistry.ts +5 -0
- package/src/composables/index.ts +26 -0
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
- package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
- package/src/composables/useBioTemplateWorkspace.ts +18 -43
- package/src/composables/useCommandHistory.ts +5 -1
- package/src/composables/useFocusTrap.ts +91 -0
- package/src/composables/useMenuKeyboard.ts +90 -0
- package/src/composables/useOptimisticMutation.ts +5 -1
- package/src/composables/usePluginClient.ts +42 -4
- package/src/composables/usePluginWorkspace.ts +208 -0
- package/src/composables/useResourceCrud.ts +5 -1
- package/src/composables/useRovingFocus.ts +66 -0
- package/src/composables/useWellPainting.ts +5 -1
- package/src/composables/useWellPlateAdapter.ts +5 -1
- package/src/composables/useWellPlateValidation.ts +5 -1
- package/src/composables/workspaceRecipes.ts +267 -0
- package/src/index.ts +16 -0
- package/src/styles/components/secret-input.css +60 -0
- package/src/styles/index.css +1 -0
- package/src/templates/builderReadUtils.ts +1 -3
- package/src/templates/componentTargetResolvers.ts +1 -3
- package/src/templates/controlSchemaTargets.ts +1 -3
- package/src/types/form-builder.ts +1 -0
- package/src/utils/formModelSync.ts +1 -3
- package/src/utils/pluginIcon.ts +1 -3
- package/src/utils/pluginSecrets.ts +102 -0
- package/src/utils/records.ts +9 -0
- package/dist/SettingsModal-CkNM8qAI.js.map +0 -1
- package/dist/components-MGNL6tRn.js.map +0 -1
- package/dist/composables-CKxOcvgh.js.map +0 -1
- package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
- package/dist/templates-BTJ9C9Kb.js.map +0 -1
- package/dist/useControlSchema-CXhxTul7.js.map +0 -1
- package/dist/useProtocolTemplates-BWhQIWV5.js.map +0 -1
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
UseBioTemplatePresetWorkspaceReturn,
|
|
15
15
|
} from '../composables/useBioTemplatePresetWorkspace'
|
|
16
16
|
import { useBioTemplatePresetWorkspace } from '../composables/useBioTemplatePresetWorkspace'
|
|
17
|
+
import { bioTemplatePresetWorkspaceProps } from '../composables/workspaceRecipes'
|
|
17
18
|
import AlertBox from './AlertBox.vue'
|
|
18
19
|
import AppSidebar from './AppSidebar.vue'
|
|
19
20
|
import BaseButton from './BaseButton.vue'
|
|
@@ -96,19 +97,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
96
97
|
resetLabel: 'Reset defaults',
|
|
97
98
|
})
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
...(props.workspaceOptions.initialValues ?? {}),
|
|
107
|
-
...(props.initialValues ?? {}),
|
|
108
|
-
...(externalValues.value ?? {}),
|
|
109
|
-
},
|
|
110
|
-
}
|
|
111
|
-
})
|
|
100
|
+
// Thin recipe over bioTemplatePresetWorkspaceProps(): merges workspaceOptions/initialValues/
|
|
101
|
+
// v-model into the options useBioTemplatePresetWorkspace() consumes (see workspaceRecipes.ts).
|
|
102
|
+
const resolvedPresetProps = computed(() => bioTemplatePresetWorkspaceProps(props))
|
|
103
|
+
const externalValues = computed(() => resolvedPresetProps.value.externalValues)
|
|
104
|
+
const resolvedWorkspaceOptions = computed<UseBioTemplatePresetWorkspaceOptions>(() =>
|
|
105
|
+
resolvedPresetProps.value.resolvedWorkspaceOptions
|
|
106
|
+
)
|
|
112
107
|
|
|
113
108
|
let generatedWorkspaceScope: EffectScope | undefined
|
|
114
109
|
|
|
@@ -16,6 +16,7 @@ import ReagentList from './ReagentList.vue'
|
|
|
16
16
|
import SampleSelector from './SampleSelector.vue'
|
|
17
17
|
import ScheduleCalendar from './ScheduleCalendar.vue'
|
|
18
18
|
import WellPlate from './WellPlate.vue'
|
|
19
|
+
import { isRecord } from '../utils/records'
|
|
19
20
|
|
|
20
21
|
export interface ComponentBindingRendererBinding {
|
|
21
22
|
id?: string
|
|
@@ -173,9 +174,6 @@ function normalizeProps(binding: ComponentBindingRendererBinding): Record<string
|
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
177
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
178
|
-
}
|
|
179
177
|
</script>
|
|
180
178
|
|
|
181
179
|
<template>
|
|
@@ -20,7 +20,8 @@ import type {
|
|
|
20
20
|
ControlWorkspaceOptions,
|
|
21
21
|
UseControlWorkspaceReturn,
|
|
22
22
|
} from '../composables/useControlSchema'
|
|
23
|
-
import {
|
|
23
|
+
import { useControlWorkspace } from '../composables/useControlSchema'
|
|
24
|
+
import { controlWorkspaceProps } from '../composables/workspaceRecipes'
|
|
24
25
|
import AppLayout from './AppLayout.vue'
|
|
25
26
|
import AppSidebar from './AppSidebar.vue'
|
|
26
27
|
import AppTopBar from './AppTopBar.vue'
|
|
@@ -161,27 +162,15 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
161
162
|
formSize: 'md',
|
|
162
163
|
})
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
const resolvedControlOptions = computed<ControlWorkspaceOptions>(() => {
|
|
174
|
-
if (props.initialValues === undefined && externalValues.value === undefined) return baseControlOptions.value
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
...baseControlOptions.value,
|
|
178
|
-
initialValues: {
|
|
179
|
-
...(baseControlOptions.value.initialValues ?? {}),
|
|
180
|
-
...(props.initialValues ?? {}),
|
|
181
|
-
...(externalValues.value ?? {}),
|
|
182
|
-
},
|
|
183
|
-
}
|
|
184
|
-
})
|
|
165
|
+
// Thin recipe over controlWorkspaceProps(): resolves model/controls/controlOptions/v-model
|
|
166
|
+
// into the schema + options pair useControlWorkspace() consumes, shared with
|
|
167
|
+
// DoseDesignWorkspaceView (which forwards its own props here) and
|
|
168
|
+
// BioTemplatePresetWorkspaceView's equivalent options merge.
|
|
169
|
+
const resolvedWorkspaceModel = computed(() => controlWorkspaceProps(props))
|
|
170
|
+
const externalValues = computed(() => resolvedWorkspaceModel.value.externalValues)
|
|
171
|
+
const resolvedModel = computed(() => resolvedWorkspaceModel.value.resolvedModel)
|
|
172
|
+
const resolvedControls = computed(() => resolvedWorkspaceModel.value.resolvedControls)
|
|
173
|
+
const resolvedControlOptions = computed(() => resolvedWorkspaceModel.value.resolvedControlOptions)
|
|
185
174
|
|
|
186
175
|
let generatedWorkspaceScope: EffectScope | undefined
|
|
187
176
|
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
UseControlWorkspaceReturn,
|
|
20
20
|
} from '../composables/useControlSchema'
|
|
21
21
|
import { defineDoseDesignControlModel } from '../composables/useControlSchema'
|
|
22
|
+
import { doseDesignWorkspaceProps } from '../composables/workspaceRecipes'
|
|
22
23
|
import ControlWorkspaceView from './ControlWorkspaceView.vue'
|
|
23
24
|
import DoseCalculator from './DoseCalculator.vue'
|
|
24
25
|
import WellPlate from './WellPlate.vue'
|
|
@@ -158,6 +159,10 @@ const resolvedModel = computed<ControlModel | ControlModelBinding>(() =>
|
|
|
158
159
|
props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)
|
|
159
160
|
)
|
|
160
161
|
|
|
162
|
+
// Thin recipe: forwards the ~25 props this view shares verbatim with ControlWorkspaceView
|
|
163
|
+
// (see workspaceRecipes.ts) instead of spelling each one out in the template.
|
|
164
|
+
const controlWorkspaceRecipe = computed(() => doseDesignWorkspaceProps(props, resolvedModel.value))
|
|
165
|
+
|
|
161
166
|
function resolvedPlateId(): string {
|
|
162
167
|
return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'
|
|
163
168
|
}
|
|
@@ -200,31 +205,7 @@ function slotProps(slotProps: {
|
|
|
200
205
|
|
|
201
206
|
<template>
|
|
202
207
|
<ControlWorkspaceView
|
|
203
|
-
|
|
204
|
-
:workspace="workspace"
|
|
205
|
-
:control-options="controlOptions"
|
|
206
|
-
:initial-values="initialValues"
|
|
207
|
-
:model-value="modelValue"
|
|
208
|
-
:values="values"
|
|
209
|
-
:title="title"
|
|
210
|
-
:subtitle="subtitle"
|
|
211
|
-
:top-bar-variant="topBarVariant"
|
|
212
|
-
:sidebar-width="sidebarWidth"
|
|
213
|
-
:sidebar-variant="sidebarVariant"
|
|
214
|
-
:responsive-sidebar="responsiveSidebar"
|
|
215
|
-
:sidebar-position="sidebarPosition"
|
|
216
|
-
:sidebar-title="sidebarTitle"
|
|
217
|
-
:sidebar-subtitle="sidebarSubtitle"
|
|
218
|
-
:sidebar-badge="sidebarBadge"
|
|
219
|
-
:floating="floating"
|
|
220
|
-
:dense="dense"
|
|
221
|
-
:show-settings="showSettings"
|
|
222
|
-
:show-form-actions="showFormActions"
|
|
223
|
-
:form-enhancements="formEnhancements"
|
|
224
|
-
:form-loading="formLoading"
|
|
225
|
-
:form-disabled="formDisabled"
|
|
226
|
-
:form-readonly="formReadonly"
|
|
227
|
-
:form-size="formSize"
|
|
208
|
+
v-bind="controlWorkspaceRecipe"
|
|
228
209
|
@update:model-value="emit('update:modelValue', $event)"
|
|
229
210
|
@update:values="emit('update:values', $event)"
|
|
230
211
|
@submit="emit('submit', $event)"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/** Button that opens a dropdown menu for selecting one option from a list. */
|
|
3
|
-
import { computed } from 'vue'
|
|
3
|
+
import { computed, nextTick, ref } from 'vue'
|
|
4
4
|
import { useDropdownState } from '../composables/useDropdownState'
|
|
5
|
+
import { useMenuKeyboard } from '../composables/useMenuKeyboard'
|
|
5
6
|
import type { SelectOption, SelectOptionInput, ButtonVariant, ButtonSize } from '../types'
|
|
6
7
|
import { normalizeOptionInput } from '../utils/options'
|
|
7
8
|
|
|
@@ -53,11 +54,33 @@ const emit = defineEmits<{
|
|
|
53
54
|
select: [option: SelectOption<string | number>]
|
|
54
55
|
}>()
|
|
55
56
|
|
|
56
|
-
const { isOpen, rootRef, close, toggle: toggleDropdown } = useDropdownState()
|
|
57
|
+
const { isOpen, rootRef, open, close: closeDropdown, toggle: toggleDropdown } = useDropdownState()
|
|
57
58
|
const normalizedOptions = computed<SelectOption<string | number>[]>(() =>
|
|
58
59
|
props.options.map(normalizeOptionInput)
|
|
59
60
|
)
|
|
60
61
|
|
|
62
|
+
const triggerRef = ref<HTMLElement | null>(null)
|
|
63
|
+
const panelRef = ref<HTMLElement | null>(null)
|
|
64
|
+
|
|
65
|
+
function optionItems(): HTMLElement[] {
|
|
66
|
+
if (!panelRef.value) return []
|
|
67
|
+
return Array.from(panelRef.value.querySelectorAll<HTMLElement>('[role="option"]:not([aria-disabled="true"])'))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function close(returnFocus = false) {
|
|
71
|
+
closeDropdown()
|
|
72
|
+
if (returnFocus) {
|
|
73
|
+
void nextTick(() => triggerRef.value?.focus())
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({
|
|
78
|
+
items: optionItems,
|
|
79
|
+
isOpen: () => isOpen.value,
|
|
80
|
+
open,
|
|
81
|
+
close,
|
|
82
|
+
})
|
|
83
|
+
|
|
61
84
|
const selectedLabel = computed(() => {
|
|
62
85
|
if (props.modelValue === undefined) return props.placeholder ?? 'Select...'
|
|
63
86
|
const found = normalizedOptions.value.find(o => o.value === props.modelValue)
|
|
@@ -80,6 +103,7 @@ function selectOption(option: SelectOption<string | number>) {
|
|
|
80
103
|
<template>
|
|
81
104
|
<div ref="rootRef" class="mint-dropdown-button">
|
|
82
105
|
<button
|
|
106
|
+
ref="triggerRef"
|
|
83
107
|
type="button"
|
|
84
108
|
:disabled="disabled || loading"
|
|
85
109
|
:class="[
|
|
@@ -94,6 +118,7 @@ function selectOption(option: SelectOption<string | number>) {
|
|
|
94
118
|
:aria-expanded="isOpen"
|
|
95
119
|
aria-haspopup="listbox"
|
|
96
120
|
@click="toggle"
|
|
121
|
+
@keydown="handleTriggerKeydown"
|
|
97
122
|
>
|
|
98
123
|
<svg v-if="loading" class="mint-dropdown-button__spinner" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
99
124
|
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" opacity="0.25" />
|
|
@@ -116,15 +141,18 @@ function selectOption(option: SelectOption<string | number>) {
|
|
|
116
141
|
<Transition name="mint-dropdown-button-menu">
|
|
117
142
|
<ul
|
|
118
143
|
v-if="isOpen"
|
|
144
|
+
ref="panelRef"
|
|
119
145
|
class="mint-dropdown-button__menu"
|
|
120
146
|
role="listbox"
|
|
121
147
|
:aria-activedescendant="modelValue !== undefined ? `mint-dropdown-option-${modelValue}` : undefined"
|
|
148
|
+
@keydown="handlePanelKeydown"
|
|
122
149
|
>
|
|
123
150
|
<li
|
|
124
151
|
v-for="option in normalizedOptions"
|
|
125
152
|
:id="`mint-dropdown-option-${option.value}`"
|
|
126
153
|
:key="String(option.value)"
|
|
127
154
|
role="option"
|
|
155
|
+
:tabindex="-1"
|
|
128
156
|
:aria-selected="option.value === modelValue"
|
|
129
157
|
:aria-disabled="option.disabled"
|
|
130
158
|
:class="[
|
|
@@ -135,6 +163,8 @@ function selectOption(option: SelectOption<string | number>) {
|
|
|
135
163
|
},
|
|
136
164
|
]"
|
|
137
165
|
@click="selectOption(option)"
|
|
166
|
+
@keydown.enter.prevent="selectOption(option)"
|
|
167
|
+
@keydown.space.prevent="selectOption(option)"
|
|
138
168
|
>
|
|
139
169
|
<span class="mint-dropdown-button__option-label">{{ option.label }}</span>
|
|
140
170
|
<span v-if="option.description" class="mint-dropdown-button__option-description">
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AccountMenuItem,
|
|
3
|
+
ExperimentSummary,
|
|
4
|
+
PageSelectorItem,
|
|
3
5
|
PageSelectorItemInput,
|
|
6
|
+
PillNavItem,
|
|
4
7
|
PillNavItemInput,
|
|
8
|
+
PillNavOption,
|
|
5
9
|
PluginSwitcherInfo,
|
|
10
|
+
PluginSwitcherPlugin,
|
|
6
11
|
SidebarToolSection,
|
|
7
12
|
TopBarSettingsConfig,
|
|
8
13
|
TopBarVariant,
|
|
@@ -156,6 +161,39 @@ export interface PluginWorkspaceViewProps {
|
|
|
156
161
|
sidebarCloseLabel?: string
|
|
157
162
|
}
|
|
158
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Event map for PluginWorkspaceView, shared with the {@link usePluginWorkspace} composable
|
|
166
|
+
* so its internal pieces can emit through the exact same typed contract as the public component.
|
|
167
|
+
*/
|
|
168
|
+
export interface PluginWorkspaceViewEmits {
|
|
169
|
+
'update:activeView': [value: string]
|
|
170
|
+
'page-selector-select': [page: PageSelectorItem]
|
|
171
|
+
'pill-select': [item: PillNavItem]
|
|
172
|
+
'pill-option-select': [option: PillNavOption, item: PillNavItem]
|
|
173
|
+
'plugin-switcher-select': [plugin: PluginSwitcherPlugin]
|
|
174
|
+
'plugin-switcher-install': []
|
|
175
|
+
'account-menu-select': [item: AccountMenuItem]
|
|
176
|
+
'sign-out': []
|
|
177
|
+
'notifications-click': []
|
|
178
|
+
'settings-values-change': [data: Record<string, unknown>]
|
|
179
|
+
'admin-click': []
|
|
180
|
+
'experiment-select': [experiment: ExperimentSummary]
|
|
181
|
+
'experiment-detach': []
|
|
182
|
+
'update:modelValue': [values: Record<string, unknown>]
|
|
183
|
+
'update:values': [values: Record<string, unknown>]
|
|
184
|
+
'update:sidebarCollapsed': [value: boolean]
|
|
185
|
+
'update:toggle': [sectionId: string, value: boolean]
|
|
186
|
+
'section-action': [sectionId: string, actionId: string]
|
|
187
|
+
'form-submit': [sectionId: string, values: Record<string, unknown>]
|
|
188
|
+
'form-cancel': [sectionId: string]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Typed `emit` function shape produced by `defineEmits<PluginWorkspaceViewEmits>()`. */
|
|
192
|
+
export type PluginWorkspaceViewEmit = <K extends keyof PluginWorkspaceViewEmits>(
|
|
193
|
+
event: K,
|
|
194
|
+
...args: PluginWorkspaceViewEmits[K]
|
|
195
|
+
) => void
|
|
196
|
+
|
|
159
197
|
export interface PluginWorkspaceSlotProps {
|
|
160
198
|
activeView: string
|
|
161
199
|
setActiveView: (viewId: string) => void
|
|
@@ -12,26 +12,19 @@
|
|
|
12
12
|
import { computed, useSlots } from 'vue'
|
|
13
13
|
import type {
|
|
14
14
|
AccountMenuItem,
|
|
15
|
-
ExperimentSummary,
|
|
16
|
-
PageSelectorItem,
|
|
17
15
|
PillNavItem,
|
|
18
16
|
PluginSwitcherPlugin,
|
|
19
17
|
PillNavOption,
|
|
20
18
|
} from '../types'
|
|
21
19
|
import { useAppExperiment } from '../composables/useAppExperiment'
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import { usePluginWorkspaceNavigation } from './PluginWorkspaceView.navigation'
|
|
27
|
-
import {
|
|
28
|
-
getPluginWorkspaceSidebarSlotNames,
|
|
29
|
-
getPluginWorkspaceTopBarSlotNames,
|
|
30
|
-
hasPluginWorkspaceSidebarSurface,
|
|
31
|
-
} from './PluginWorkspaceView.shell'
|
|
20
|
+
import { usePluginWorkspace } from '../composables/usePluginWorkspace'
|
|
21
|
+
import WorkspaceControlsInternal from './internal/WorkspaceControlsInternal.vue'
|
|
22
|
+
import WorkspaceShellInternal from './internal/WorkspaceShellInternal.vue'
|
|
23
|
+
import WorkspaceSidebarInternal from './internal/WorkspaceSidebarInternal.vue'
|
|
32
24
|
import type {
|
|
33
25
|
PluginWorkspaceForwardedSlotProps,
|
|
34
26
|
PluginWorkspaceSlotProps,
|
|
27
|
+
PluginWorkspaceViewEmits,
|
|
35
28
|
PluginWorkspaceViewProps,
|
|
36
29
|
} from './PluginWorkspaceView.props'
|
|
37
30
|
|
|
@@ -103,28 +96,7 @@ const props = withDefaults(defineProps<PluginWorkspaceViewProps>(), {
|
|
|
103
96
|
sidebarCloseLabel: 'Close workspace sidebar',
|
|
104
97
|
})
|
|
105
98
|
|
|
106
|
-
const emit = defineEmits<
|
|
107
|
-
'update:activeView': [value: string]
|
|
108
|
-
'page-selector-select': [page: PageSelectorItem]
|
|
109
|
-
'pill-select': [item: PillNavItem]
|
|
110
|
-
'pill-option-select': [option: PillNavOption, item: PillNavItem]
|
|
111
|
-
'plugin-switcher-select': [plugin: PluginSwitcherPlugin]
|
|
112
|
-
'plugin-switcher-install': []
|
|
113
|
-
'account-menu-select': [item: AccountMenuItem]
|
|
114
|
-
'sign-out': []
|
|
115
|
-
'notifications-click': []
|
|
116
|
-
'settings-values-change': [data: Record<string, unknown>]
|
|
117
|
-
'admin-click': []
|
|
118
|
-
'experiment-select': [experiment: ExperimentSummary]
|
|
119
|
-
'experiment-detach': []
|
|
120
|
-
'update:modelValue': [values: Record<string, unknown>]
|
|
121
|
-
'update:values': [values: Record<string, unknown>]
|
|
122
|
-
'update:sidebarCollapsed': [value: boolean]
|
|
123
|
-
'update:toggle': [sectionId: string, value: boolean]
|
|
124
|
-
'section-action': [sectionId: string, actionId: string]
|
|
125
|
-
'form-submit': [sectionId: string, values: Record<string, unknown>]
|
|
126
|
-
'form-cancel': [sectionId: string]
|
|
127
|
-
}>()
|
|
99
|
+
const emit = defineEmits<PluginWorkspaceViewEmits>()
|
|
128
100
|
|
|
129
101
|
defineSlots<{
|
|
130
102
|
default?: (props: PluginWorkspaceSlotProps) => unknown
|
|
@@ -158,82 +130,19 @@ if (props.experimentShell) {
|
|
|
158
130
|
})
|
|
159
131
|
}
|
|
160
132
|
|
|
161
|
-
const topBarSlotNames = computed<string[]>(() =>
|
|
162
|
-
getPluginWorkspaceTopBarSlotNames(slots),
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
const sidebarSlotNames = computed<string[]>(() =>
|
|
166
|
-
getPluginWorkspaceSidebarSlotNames(slots),
|
|
167
|
-
)
|
|
168
|
-
|
|
169
133
|
const {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
resolvedComponentBindings,
|
|
174
|
-
resolvedComponentBindingsById,
|
|
175
|
-
resolvedComponentProps,
|
|
176
|
-
resolvedComponentPropsById,
|
|
134
|
+
shell,
|
|
135
|
+
sidebar,
|
|
136
|
+
controls,
|
|
177
137
|
handleControlValuesUpdate,
|
|
178
|
-
} = usePluginWorkspaceControls({
|
|
179
|
-
model: () => props.model,
|
|
180
|
-
controls: () => props.controls,
|
|
181
|
-
controlOptions: () => props.controlOptions,
|
|
182
|
-
modelValue: () => props.modelValue,
|
|
183
|
-
values: () => props.values,
|
|
184
|
-
componentBindings: () => props.componentBindings,
|
|
185
|
-
componentProps: () => props.componentProps,
|
|
186
|
-
componentPropsById: () => props.componentPropsById,
|
|
187
|
-
emitValues: (values) => {
|
|
188
|
-
emit('update:modelValue', values)
|
|
189
|
-
emit('update:values', values)
|
|
190
|
-
},
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
const {
|
|
194
|
-
resolvedPillNav,
|
|
195
|
-
resolvedActiveView,
|
|
196
|
-
resolvedCurrentPillId,
|
|
197
|
-
resolvedCurrentPageSelectorId,
|
|
198
|
-
setActiveView,
|
|
199
138
|
handlePageSelectorSelect,
|
|
200
139
|
handlePillSelect,
|
|
201
|
-
} =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
currentPageSelectorId: () => props.currentPageSelectorId,
|
|
206
|
-
pillNav: () => props.pillNav,
|
|
207
|
-
pageSelector: () => props.pageSelector,
|
|
208
|
-
panelIds: () => Object.keys(props.panels),
|
|
209
|
-
generatedPillNav,
|
|
210
|
-
generatedDefaultView,
|
|
211
|
-
emitActiveView: (viewId) => emit('update:activeView', viewId),
|
|
212
|
-
emitPageSelectorSelect: (page) => emit('page-selector-select', page),
|
|
213
|
-
emitPillSelect: (item) => emit('pill-select', item),
|
|
140
|
+
} = usePluginWorkspace({
|
|
141
|
+
props,
|
|
142
|
+
slots,
|
|
143
|
+
emit,
|
|
214
144
|
})
|
|
215
145
|
|
|
216
|
-
const hasSidebarSurface = computed(() => {
|
|
217
|
-
return hasPluginWorkspaceSidebarSurface({
|
|
218
|
-
showSidebar: props.showSidebar,
|
|
219
|
-
panels: props.panels,
|
|
220
|
-
forms: props.forms,
|
|
221
|
-
model: props.model,
|
|
222
|
-
controls: props.controls,
|
|
223
|
-
showSidebarWhenEmpty: props.showSidebarWhenEmpty,
|
|
224
|
-
sidebarContentId: props.sidebarContentId,
|
|
225
|
-
sidebarTitle: props.sidebarTitle,
|
|
226
|
-
sidebarSubtitle: props.sidebarSubtitle,
|
|
227
|
-
sidebarBadge: props.sidebarBadge,
|
|
228
|
-
hasSidebarSlot: Boolean(slots.sidebar),
|
|
229
|
-
sidebarSlotCount: sidebarSlotNames.value.length,
|
|
230
|
-
})
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
function handlePillOptionSelect(option: PillNavOption, item: PillNavItem) {
|
|
234
|
-
emit('pill-option-select', option, item)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
146
|
function handleToggle(sectionId: string, value: boolean) {
|
|
238
147
|
emit('update:toggle', sectionId, value)
|
|
239
148
|
}
|
|
@@ -249,126 +158,48 @@ function handleFormCancel(sectionId: string) {
|
|
|
249
158
|
</script>
|
|
250
159
|
|
|
251
160
|
<template>
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
161
|
+
<WorkspaceShellInternal
|
|
162
|
+
:shell="shell"
|
|
163
|
+
@page-selector-select="handlePageSelectorSelect"
|
|
164
|
+
@pill-select="handlePillSelect"
|
|
165
|
+
@pill-option-select="(option: PillNavOption, item: PillNavItem) => emit('pill-option-select', option, item)"
|
|
166
|
+
@plugin-switcher-select="(plugin: PluginSwitcherPlugin) => emit('plugin-switcher-select', plugin)"
|
|
167
|
+
@plugin-switcher-install="emit('plugin-switcher-install')"
|
|
168
|
+
@account-menu-select="(item: AccountMenuItem) => emit('account-menu-select', item)"
|
|
169
|
+
@sign-out="emit('sign-out')"
|
|
170
|
+
@notifications-click="emit('notifications-click')"
|
|
171
|
+
@settings-values-change="emit('settings-values-change', $event)"
|
|
172
|
+
@admin-click="emit('admin-click')"
|
|
260
173
|
>
|
|
261
|
-
<template #topbar>
|
|
262
|
-
<slot
|
|
263
|
-
name="topbar"
|
|
264
|
-
:active-view="resolvedActiveView"
|
|
265
|
-
:set-active-view="setActiveView"
|
|
266
|
-
:values="internalControlValues"
|
|
267
|
-
:component-bindings="resolvedComponentBindings"
|
|
268
|
-
:component-bindings-by-id="resolvedComponentBindingsById"
|
|
269
|
-
:component-props="resolvedComponentProps"
|
|
270
|
-
:component-props-by-id="resolvedComponentPropsById"
|
|
271
|
-
>
|
|
272
|
-
<AppTopBar
|
|
273
|
-
:title="title"
|
|
274
|
-
:subtitle="subtitle"
|
|
275
|
-
:variant="topBarVariant"
|
|
276
|
-
:home-path="homePath"
|
|
277
|
-
:show-logo="showLogo"
|
|
278
|
-
:page-selector="pageSelector"
|
|
279
|
-
:current-page-selector-id="resolvedCurrentPageSelectorId"
|
|
280
|
-
:plugin-switcher="pluginSwitcher"
|
|
281
|
-
:pill-nav="resolvedPillNav"
|
|
282
|
-
:current-pill-id="resolvedCurrentPillId"
|
|
283
|
-
:show-theme-toggle="showThemeToggle"
|
|
284
|
-
:show-settings="showSettings"
|
|
285
|
-
:settings-config="settingsConfig"
|
|
286
|
-
:show-standalone-label="showStandaloneLabel"
|
|
287
|
-
:standalone-label="standaloneLabel"
|
|
288
|
-
:account-menu="accountMenu"
|
|
289
|
-
:show-notifications="showNotifications"
|
|
290
|
-
:has-notification-dot="hasNotificationDot"
|
|
291
|
-
:show-admin="showAdmin"
|
|
292
|
-
:admin-path="adminPath"
|
|
293
|
-
:user-name="userName"
|
|
294
|
-
:user-initial="userInitial"
|
|
295
|
-
:user-email="userEmail"
|
|
296
|
-
@page-selector-select="handlePageSelectorSelect"
|
|
297
|
-
@pill-select="handlePillSelect"
|
|
298
|
-
@pill-option-select="handlePillOptionSelect"
|
|
299
|
-
@plugin-switcher-select="emit('plugin-switcher-select', $event)"
|
|
300
|
-
@plugin-switcher-install="emit('plugin-switcher-install')"
|
|
301
|
-
@account-menu-select="emit('account-menu-select', $event)"
|
|
302
|
-
@sign-out="emit('sign-out')"
|
|
303
|
-
@notifications-click="emit('notifications-click')"
|
|
304
|
-
@settings-values-change="emit('settings-values-change', $event)"
|
|
305
|
-
@admin-click="emit('admin-click')"
|
|
306
|
-
>
|
|
307
|
-
<template
|
|
308
|
-
v-for="slotName in topBarSlotNames"
|
|
309
|
-
:key="slotName"
|
|
310
|
-
#[slotName]="slotProps"
|
|
311
|
-
>
|
|
312
|
-
<!-- @vue-ignore dynamic SDK slot forwarding for AppTopBar passthrough slots -->
|
|
313
|
-
<slot
|
|
314
|
-
:name="slotName"
|
|
315
|
-
v-bind="slotProps ?? {}"
|
|
316
|
-
/>
|
|
317
|
-
</template>
|
|
318
|
-
</AppTopBar>
|
|
319
|
-
</slot>
|
|
174
|
+
<template v-if="slots.topbar" #topbar="scope">
|
|
175
|
+
<slot name="topbar" v-bind="scope" />
|
|
320
176
|
</template>
|
|
321
177
|
|
|
322
|
-
<template
|
|
178
|
+
<template
|
|
179
|
+
v-for="slotName in shell.topBarSlotNames"
|
|
180
|
+
:key="slotName"
|
|
181
|
+
#[slotName]="slotProps"
|
|
182
|
+
>
|
|
183
|
+
<!-- @vue-ignore dynamic SDK slot forwarding for AppTopBar passthrough slots -->
|
|
323
184
|
<slot
|
|
324
|
-
name="
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
<AppSidebar
|
|
334
|
-
:title="sidebarTitle"
|
|
335
|
-
:subtitle="sidebarSubtitle"
|
|
336
|
-
:badge="sidebarBadge"
|
|
337
|
-
:variant="sidebarVariant"
|
|
338
|
-
:panels="panels"
|
|
339
|
-
:active-view="resolvedActiveView"
|
|
340
|
-
:floating="false"
|
|
341
|
-
:dense="dense"
|
|
342
|
-
:width="sidebarWidth"
|
|
343
|
-
:side="sidebarPosition"
|
|
344
|
-
:forms="forms"
|
|
345
|
-
:content-id="sidebarContentId"
|
|
346
|
-
:show-when-empty="showSidebarWhenEmpty || sidebarContentId !== undefined"
|
|
347
|
-
:model="model"
|
|
348
|
-
:controls="controls"
|
|
349
|
-
:control-options="controlOptions"
|
|
350
|
-
:values="internalControlValues"
|
|
351
|
-
:form-enhancements="formEnhancements"
|
|
352
|
-
:show-form-actions="showFormActions"
|
|
353
|
-
:form-loading="formLoading"
|
|
354
|
-
:form-disabled="formDisabled"
|
|
355
|
-
:form-readonly="formReadonly"
|
|
356
|
-
:form-size="formSize"
|
|
357
|
-
:collapsible="sidebarCollapsible"
|
|
358
|
-
:collapsed="sidebarCollapsed"
|
|
359
|
-
:default-collapsed="defaultSidebarCollapsed"
|
|
360
|
-
:collapsed-width="sidebarCollapsedWidth"
|
|
361
|
-
:collapse-button-label="sidebarCollapseButtonLabel"
|
|
362
|
-
:expand-button-label="sidebarExpandButtonLabel"
|
|
185
|
+
:name="slotName"
|
|
186
|
+
v-bind="slotProps ?? {}"
|
|
187
|
+
/>
|
|
188
|
+
</template>
|
|
189
|
+
|
|
190
|
+
<template v-if="shell.hasSidebarSurface" #sidebar="scope">
|
|
191
|
+
<slot name="sidebar" v-bind="scope">
|
|
192
|
+
<WorkspaceSidebarInternal
|
|
193
|
+
:sidebar="sidebar"
|
|
363
194
|
@update:model-value="handleControlValuesUpdate"
|
|
364
195
|
@update:collapsed="emit('update:sidebarCollapsed', $event)"
|
|
365
196
|
@update:toggle="handleToggle"
|
|
366
|
-
@section-action="(sectionId, actionId) => emit('section-action', sectionId, actionId)"
|
|
197
|
+
@section-action="(sectionId: string, actionId: string) => emit('section-action', sectionId, actionId)"
|
|
367
198
|
@form-submit="handleFormSubmit"
|
|
368
199
|
@form-cancel="handleFormCancel"
|
|
369
200
|
>
|
|
370
201
|
<template
|
|
371
|
-
v-for="slotName in
|
|
202
|
+
v-for="slotName in sidebar.slotNames"
|
|
372
203
|
:key="slotName"
|
|
373
204
|
#[slotName]="slotProps"
|
|
374
205
|
>
|
|
@@ -378,18 +209,16 @@ function handleFormCancel(sectionId: string) {
|
|
|
378
209
|
v-bind="slotProps ?? {}"
|
|
379
210
|
/>
|
|
380
211
|
</template>
|
|
381
|
-
</
|
|
212
|
+
</WorkspaceSidebarInternal>
|
|
382
213
|
</slot>
|
|
383
214
|
</template>
|
|
384
215
|
|
|
385
|
-
<
|
|
386
|
-
:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
/>
|
|
394
|
-
</AppLayout>
|
|
216
|
+
<template #default>
|
|
217
|
+
<WorkspaceControlsInternal :controls="controls">
|
|
218
|
+
<template #default="controlsScope">
|
|
219
|
+
<slot v-bind="controlsScope" />
|
|
220
|
+
</template>
|
|
221
|
+
</WorkspaceControlsInternal>
|
|
222
|
+
</template>
|
|
223
|
+
</WorkspaceShellInternal>
|
|
395
224
|
</template>
|