@morscherlab/mint-sdk 1.2.0-beta.4 → 1.2.0-beta.7
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/__tests__/docs/frontendDocsCatalog.test.ts +22 -1
- 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
|
UseBioTemplatePackWorkspaceReturn,
|
|
15
15
|
} from '../composables/useBioTemplatePackWorkspace'
|
|
16
16
|
import { useBioTemplatePackWorkspace } from '../composables/useBioTemplatePackWorkspace'
|
|
17
|
+
import { bioTemplatePackWorkspaceProps } from '../composables/workspaceRecipes'
|
|
17
18
|
import BioTemplateExperimentWorkspaceView from './BioTemplateExperimentWorkspaceView.vue'
|
|
18
19
|
|
|
19
20
|
interface BioTemplatePackWorkspaceSlotProps {
|
|
@@ -140,6 +141,15 @@ const resolvedActions = computed<PackWorkspaceActions>(() => ({
|
|
|
140
141
|
save: props.actions?.save ?? (() => resolvedWorkspace.value.saveCurrent()),
|
|
141
142
|
}))
|
|
142
143
|
|
|
144
|
+
// Thin recipe over bioTemplatePackWorkspaceProps(): forwards this view's own display props
|
|
145
|
+
// plus the pack-derived target/label/status/actions to BioTemplateExperimentWorkspaceView.
|
|
146
|
+
const experimentWorkspaceRecipe = computed(() => bioTemplatePackWorkspaceProps(props, {
|
|
147
|
+
target: target.value,
|
|
148
|
+
label: label.value,
|
|
149
|
+
status: resolvedStatus.value,
|
|
150
|
+
actions: resolvedActions.value,
|
|
151
|
+
}))
|
|
152
|
+
|
|
143
153
|
watch(
|
|
144
154
|
[() => props.pack, () => props.workspaceOptions],
|
|
145
155
|
() => {
|
|
@@ -152,21 +162,7 @@ onScopeDispose(() => generatedWorkspaceScope?.stop())
|
|
|
152
162
|
</script>
|
|
153
163
|
|
|
154
164
|
<template>
|
|
155
|
-
<BioTemplateExperimentWorkspaceView
|
|
156
|
-
:target="target"
|
|
157
|
-
kind="collection"
|
|
158
|
-
:label="label"
|
|
159
|
-
:message="message"
|
|
160
|
-
:status="resolvedStatus"
|
|
161
|
-
:actions="resolvedActions"
|
|
162
|
-
:dense="dense"
|
|
163
|
-
:readonly="readonly"
|
|
164
|
-
:show-status="showStatus"
|
|
165
|
-
:show-template-summary="showTemplateSummary"
|
|
166
|
-
:load-label="loadLabel"
|
|
167
|
-
:reset-label="resetLabel"
|
|
168
|
-
:save-label="saveLabel"
|
|
169
|
-
>
|
|
165
|
+
<BioTemplateExperimentWorkspaceView v-bind="experimentWorkspaceRecipe">
|
|
170
166
|
<template v-if="hasDefaultSlot" #default="slotProps">
|
|
171
167
|
<slot
|
|
172
168
|
v-bind="slotProps"
|
|
@@ -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
|