FlatDropdown
Slot-driven floating dropdown panel (`TxFlatDropdown`). Hover / click / manual triggers, flip-aware placement, and a scale + blur exit animation.
FlatDropdown
Basic Usage
Hover the trigger to open. The panel stays alive for closeDelay ms after the pointer leaves, so you can travel into it. close-on-content-click dismisses the panel after any click inside.
FlatDropdown (basic)
Trigger Modes
trigger decides how the panel is summoned:
hover(default) — opens on pointer enter / focus, closes aftercloseDelay.click— toggles on click;closeDelayis not applied.manual— the component never opens itself. Drive it withv-model.
Use manual when the panel must follow application state rather than pointer intent — for example a dropdown that opens as the result of a keyboard shortcut.
Controlled vs Uncontrolled
Omit v-model and the component tracks its own open state. Bind v-model and you own it — the component still emits open / close, but will not change the value itself unless the interaction is allowed.
disabled blocks every opening path, including programmatic ones through the trigger slot's show().
Sizing
By default the panel sizes to its content. Two escape hatches:
match-trigger-width— sets the panel'smin-widthto the measured trigger width. Good for select-like menus.width— a fixed px number or any CSS length. Overridesmatch-trigger-width.
Dismissal Contract
Three independent dismissal paths, each separately switchable:
| Prop | Default | Dismisses when |
|---|---|---|
closeOnClickOutside | true | A click lands outside both trigger and panel |
closeOnEsc | true | Escape is pressed |
closeOnContentClick | false | Any click inside the panel |
closeOnClickOutside only applies to the click and hover triggers — under manual the host owns dismissal entirely.
API
TxFlatDropdown
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | boolean | undefined | Controlled open state. Omit for uncontrolled behaviour. |
trigger | 'hover' | 'click' | 'manual' | 'hover' | How the panel is summoned. |
placement | Placement | 'bottom-start' | Floating placement relative to the trigger. |
offset | number | 10 | Gap in px between trigger and panel. |
openDelay | number | 0 | Delay before opening on hover/focus (ms). |
closeDelay | number | 600 | Delay before closing after pointer leave (ms). |
exitDuration | number | 280 | Duration of the scale + blur exit animation (ms). |
disabled | boolean | false | Disable every interaction. |
teleport | boolean | string | 'body' | Teleport target; pass false to render inline. |
matchTriggerWidth | boolean | false | Match the panel's min-width to the trigger width. |
width | number | string | undefined | Fixed panel width. Overrides matchTriggerWidth. |
closeOnClickOutside | boolean | true | Close when clicking outside. |
closeOnEsc | boolean | true | Close when pressing Escape. |
closeOnContentClick | boolean | false | Close after any click inside the panel. |
panelClass | TxFlatDropdownClass | undefined | Extra class(es) merged onto the panel element. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | boolean | Open state changed. |
open | — | The panel opened. |
close | — | The panel closed. |
Slots
| Slot | Props | Description |
|---|---|---|
trigger | { open, toggle, show, hide } | The anchor element. |
default | { open, close, side } | Panel body. side is the resolved side after flip. |
Best Practices
- Prefer
trigger="click"for destructive or state-changing menus — hover-opened panels are easy to trip over on trackpads. - Keep
closeDelaygenerous (the600default) forhover; a short delay makes diagonal pointer travel into the panel frustrating. - Set
teleport="false"when the dropdown lives inside a container with its own stacking or clipping context and you need it to inherit that context. Note that inline rendering re-exposes the panel to ancestoroverflow: hidden. - Use the
sideslot prop to flip your own decorations (arrows, shadows) when the panel flips above the trigger. - Do not rely on the panel being positioned synchronously after opening — placement is written back reactively, so measure in a
requestAnimationFrameif you need absolute geometry. - The reference wrapper advertises the panel with
aria-haspopup,aria-expanded, andaria-controls(pointing at the panel's generated id), so a slotted<button>trigger inherits disclosure semantics automatically.
Source
- Component:
packages/tuffex/packages/components/src/flat-dropdown/src/TxFlatDropdown.vue - Types:
packages/tuffex/packages/components/src/flat-dropdown/src/types.ts