Skip to content

BbDropdown

Displays contextual action menus and menu content (includes the co-located BbDropdownGroup export, which is how a selectable group is bound).

import { BbDropdown } from 'bitboss-ui';

On this page

Props

NameTypeDefaultDescription
activatorRecord<string, any> | HTMLElement | null | undefined-

External activator element or component ref. When provided, the activator slot is not rendered and event listeners are attached programmatically to the referenced element. ARIA (aria-haspopup/aria-expanded/aria-controls) is attached programmatically too — don't add your own, or you get a double-set conflict.

The no-wrapper alternative to the activator slot, for when you already hold an element or component ref. It gets the same aria-haspopup / aria-expanded / aria-controls attached programmatically — so do not hand-write those on the referenced element either.

See example
adaptiveboolean | undefined`config.adaptive` (`true`)

On mobile viewports (below config.mobileMaxWidth), render the dropdown as a bottom-anchored, auto-sized off-canvas sheet that **navigates** through nested submenus (drilling one level at a time with a back control) instead of showing hover flyouts. Slots and selection behave exactly as on desktop. When unset, falls back to the global config.adaptive.

Unset resolves to the global adaptive config, which is true: on a small viewport the menu is a bottom sheet, not a flyout. Pin it false for a desktop-only dense tool, or for a test that asserts against one surface.

See example
anchorRecord<string, any> | HTMLElement | null | undefinedthe activator element

Element the panel positions itself against, when it should differ from the activator — e.g. a wrapper that the activator is only a part of, so the menu aligns to the whole control while a single button opens it. Accepts an HTMLElement or a Vue component ref. Also drives a percentage width. Ignored when placement is 'cursor'.

Positions the panel against something other than the trigger — the case being a control whose trigger is only part of it, like the toggle half of a split button. A percentage width then resolves against the anchor too. Ignored when placement is cursor.

See example
boundarystring | Record<string, any> | HTMLElement | null | undefined-

Constrains the dropdown within a bounding element. Accepts an HTMLElement, a Vue component ref, or a CSS selector string.

compactboolean | undefinedfalse

Apply the compact density variant — the same ladder BbSelect uses, on both surfaces: flyout rows go 28px → 24px, sheet rows 44px → 36px. The label size does not change; density here is height, as it is for a select. A menu row is a touch target in a way a select option is not quite — a mis-tap fires the neighbouring COMMAND rather than picking the wrong value — so the compact sheet stops at 36px and never follows the flyout down to 24px. That is the same floor the select's sheet holds.

disabledboolean | undefinedfalse

Disables the component

Freezes the whole component, trigger included. It is not the same as disabling every item: a disabled item is skipped by keyboard navigation while the menu still opens.

eagerboolean | undefinedfalse

Prevents rendering content before it's made visible.

enforceCoherenceboolean | undefinedfalse

Default for pipeline groups: drop selections that are no longer present in a group's current items (e.g. after a dependencies re-fetch) from the selection and v-model. A group's own enforceCoherence overrides this.

The dropdown-level default for pipeline groups, overridable per group. It stays lazy: a group nobody opened never fetches and never prunes, and pruning never runs against an empty or still-loading item set.

See example
idstring | undefined-

The identifier of the component.

itemsrequiredBbDropdownItem[][]

Array used to render each dropdown button. They can act as button, as a, or as router-link as they are based on the functionality provided by BbBaseButton. Pass a flat array for a single group, nest arrays for divider-separated groups, or pass {@link InternalGroup} objects for titled / selectable sections. An item may carry its own items to open a submenu. The shapes can be mixed.

Three shapes, mixable: an item, a nested array (a divider-separated section), or a group object. text is the label field and label is the group heading — a group carrying text is the v2 shape.

See example
loadingTextstring | undefined-

Default placeholder text shown while a pipeline group's async items load. A group's own loadingText overrides this; both fall back to the localized common.loadingText ('Loading...').

Pipeline groups only, and the default for all of them; a group can override it with its own loadingText. Unset, it is the plugin locale’s "Loading…" string.

See example
noDataTextstring | undefined-

Default placeholder text shown when a pipeline group resolves to no items. A group's own noDataText overrides this; both fall back to the localized common.noDataText ('No data to display').

The empty-result counterpart of loadingText, with the same per-group override and the same locale fallback.

See example
offCanvasPropsPartial<BbOffCanvasProps> | undefined-

Props for the phone sheet. A pt inside it, object or colon keys, styles only that sheet. Overrides forwarded to the underlying BbOffCanvas when adaptive renders the sheet on mobile. Merged over the adaptive defaults (direction: 'bottom', size: 'auto', draggable: true). Ignored when adaptive is false. A pt inside it styles ONLY the sheet, in BbOffCanvas's part words (root, header, title, description, content, footer, close; state open), object ({ pt: { header: '…' } }) or colon keys ({ 'pt:header': '…' }) alike. It merges after what this component forwards to its sheet, so it wins a conflict and drops nothing — see the passthrough guide § Inside the sheet.

Merged over the sheet’s own direction: "bottom", size: "auto", draggable: true. Ignored entirely when adaptive resolves to false, so it is not a way to configure the desktop panel.

See example
offsetnumber | undefined3

Space the floating container will maintain from the activator.

paddingnumber | undefined10

Minimum empty space to keep from the edge of the page

placementPlacement | "cursor" | undefined"bottom"

Position of the dropdown relative to the activator. Use 'cursor' to anchor at the mouse/touch coordinates. (No arrow is drawn either way — arrows are tooltip-only in v3.)

Every floating placement, plus cursor — which anchors the panel at the pointer and is what you pair with trigger: "contextMenu" for an OS-style menu. The panel flips on its own when there is no room.

See example
ptPtItemMap<BbDropdownPtPart, BbDropdownItemPtPart, BbDropdownPtState, InternalItem> | undefined-

Passthrough, object form: the same keys as the pt:* attributes without the prefix. See the pt:<part> row.

pt:footerPtValue | undefined-
pt:footer:disabledPtValue | undefined-
pt:footer:openPtValue | undefined-
pt:footer:selectedPtValue | undefined-
pt:headerPtValue | undefined-
pt:header:disabledPtValue | undefined-
pt:header:openPtValue | undefined-
pt:header:selectedPtValue | undefined-
pt:itemPtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownPtState>) => PtItemResult) | undefined-
pt:item-descriptionPtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownPtState>) => PtItemResult) | undefined-
pt:item-description:disabledPtValue | undefined-
pt:item-description:openPtValue | undefined-
pt:item-description:selectedPtValue | undefined-
pt:item-iconPtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownPtState>) => PtItemResult) | undefined-
pt:item-icon:disabledPtValue | undefined-
pt:item-icon:openPtValue | undefined-
pt:item-icon:selectedPtValue | undefined-
pt:item-textPtValue | ((ctx: PtItemCtx<InternalItem, BbDropdownPtState>) => PtItemResult) | undefined-
pt:item-text:disabledPtValue | undefined-
pt:item-text:openPtValue | undefined-
pt:item-text:selectedPtValue | undefined-
pt:item:disabledPtValue | undefined-
pt:item:openPtValue | undefined-
pt:item:selectedPtValue | undefined-
pt:listPtValue | undefined-
pt:list:disabledPtValue | undefined-
pt:list:openPtValue | undefined-
pt:list:selectedPtValue | undefined-
pt:panelPtValue | undefined-
pt:panel:disabledPtValue | undefined-
pt:panel:openPtValue | undefined-
pt:panel:selectedPtValue | undefined-
pt:rootPtValue | undefined-
pt:root:disabledPtValue | undefined-
pt:root:openPtValue | undefined-
pt:root:selectedPtValue | undefined-
pt:sheetPtValue | undefined-
pt:sheet:disabledPtValue | undefined-
pt:sheet:openPtValue | undefined-
pt:sheet:selectedPtValue | undefined-
queryDebounceTimenumber | undefined300

Debounce (ms) applied to the async re-fetch a searchable group fires as the user types. Applies to every searchable group in the menu.

Applies to a searchable group’s own search field, debouncing the query before it reaches that group’s async getter. Nothing else on the component reads it.

transitionDurationnumber | undefined150

How long the transition has to last in milliseconds. Menus use the fast transition rather than the medium one the other popovers default to.

triggerDropdownTrigger | DropdownTrigger[] | undefined"click"

Interaction(s) that open the dropdown. Accepts a single trigger or an array to combine them. - 'click' — left-click / tap (default) - 'contextMenu' — right-click on desktop, long-press on mobile

Takes an array to combine them (["click", "contextMenu"]). On touch devices the contextMenu gesture is a long press, and right-clicking again while open moves the menu rather than closing it.

See example
widthstring | number | undefined"auto"

Width of the dropdown in pixels, percentage, or 'auto' to fit content.

Content-sized by default with a 128px floor — not a fixed width. Pass a number when menus have to line up across the rows of a table, and a percentage when the panel should match its trigger.

See example

Events

NameTypeDescription
hidden(event: "hidden"): void
hide(event: "hide"): void
item:selected(event: "item:selected", value: any): void

Fires for any selectable group with the changed item’s value — its item-value in a pipeline group, otherwise its key. Bind the companion’s v-model when you want the state; use this when you want the moment.

See example
item:unselected(event: "item:unselected", value: any): voidSee example
show(event: "show"): void
shown(event: "shown"): void

Slots

The listed properties are the ones exposed to the slot scope.

NameTypeDescription
<groupKey>BbDropdownItemSlotProps

That group's title line (the group is passed as item, groupTitle is true).

<groupKey>:appendBbDropdownItemSlotProps

The trailing region of that group's title line.

<groupKey>:itemBbDropdownItemSlotProps

The content of every row of that group, and no other; a row's own #<key> still wins.

See example
<groupKey>:item:appendBbDropdownItemSlotProps

The trailing region of every row of that group; replaces their append:icon. The check takes its place on the selected row.

See example
<groupKey>:item:prependBbDropdownItemSlotProps

The leading region of every row of that group — e.g. an avatar per record, drawn from item.meta, while other rows keep their prepend:icon.

The switcher slot: draw each record (an avatar, a logo) from item.meta on this group’s rows while every other row keeps its prepend:icon. Set meta on each item, or through itemProps in a fetched group.

See example
<groupKey>:prependBbDropdownItemSlotProps

The leading region of that group's title line.

<key>BbDropdownItemSlotProps

One row's content (text and description), by its key; in a fetched group, by its resolved itemValue.

See example
<key>:appendBbDropdownItemSlotProps

One row's trailing region; replaces its append:icon and its submenu chevron. The check takes its place on the selected row.

The only slot that can replace a row’s submenu chevron. On the selected row of a selectable group the check still wins.

See example
<key>:prependBbDropdownItemSlotProps

One row's leading region; replaces its prepend:icon.

See example
activatorBbDropdownActivatorSlotProps

The element that triggers the dropdown to open. Spread props onto your trigger element.

Spread its props onto a real focusable element or the menu has nothing to attach ARIA and keyboard handling to. The scope also carries loading, true while any item’s async onClick is still running — this replaces the exposed loading a v2 template ref read.

See example
defaultany

Hosts <BbDropdownGroup> companions. They register their group with the dropdown and render nothing themselves.

footerBbDropdownEdgeSlotProps

Content pinned to the bottom of the dropdown panel, outside the scroll area. Stays visible while the items scroll and is separated from them by a divider — for action buttons.

The pinned counterpart below the items, for one persistent action that must stay reachable while the list scrolls.

headerBbDropdownEdgeSlotProps

Content pinned to the top of the dropdown panel, outside the scroll area. Stays visible while the items scroll and is separated from them by a divider — for a title or a filter field.

Pinned above the scrolling items and separated by a divider — a title, or a field that filters the list you build. On the adaptive sheet it becomes the sheet’s pinned header.

item:appendBbDropdownItemSlotProps

The trailing region of every row (and group title line), replacing its append:icon. A named slot wins over it; the submenu chevron and the check on a selected row take its place.

Replaces the rows’ append:icon (since beta.31; it used to lose to it). It never replaces a submenu chevron, the check takes its place on the selected row, and a group’s or a row’s own named slot beats it.

See example
item:prependBbDropdownItemSlotProps

The leading region of every row (and group title line), replacing its prepend:icon. A row's own or its group's named slot wins over it.

Rendered before every row’s content, group titles included — there groupTitle is true and item is the group. It replaces the rows’ prepend:icon, and a group’s or a row’s own named slot beats it. The payload is read-only: keep slot content presentational and leave clicks to the dropdown.

See example
loadingBbDropdownPlaceholderSlotProps

Replaces the default loading placeholder row shown in a pipeline group while its async items load. Rendered once per loading group.

no-dataBbDropdownPlaceholderSlotProps

Replaces the default "no data" placeholder row shown in a pipeline group that resolved to no items. Rendered once per empty group.

Changes from v2

Props removed or renamed in 3.0. If you are coming from v2, the answer is here.

  • arrowPaddingremovedfails silently
  • offcanvasPropsoffCanvasPropsfails silently