APopper
APopper is the low-level positioning primitive (built on Popper.js) that powers ATooltip, ADropdownMenu, and APopupMenu. Use it directly when you need a custom popup anchored to a trigger with full control over placement and trigger behavior.
Import
import { Popper } from '@any-design/anyui/vue';
// React: import { Popper } from '@any-design/anyui/react';
// Svelte: import { Popper } from '@any-design/anyui/svelte';
Basic usage
Use AButton as the trigger and put related poppers in a .demo-row when you want the controls to scan as one group.
<template>
<div class="demo-row popper-preview-row">
<APopper placement="top">
<AButton>Hover</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Hover panel</strong>
<p>A lightweight floating note above the trigger.</p>
</div>
</template>
</APopper>
<APopper placement="bottom" trigger-type="click">
<AButton type="primary">Click</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Click panel</strong>
<p>Click outside to dismiss, then click again to reopen.</p>
</div>
</template>
</APopper>
<APopper placement="bottom-start" trigger-type="contextmenu">
<AButton type="secondary">Right click</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Context panel</strong>
<p>Right-click elsewhere to close, then trigger it again.</p>
</div>
</template>
</APopper>
<APopper ref="manualPopper" placement="right" trigger-type="manual">
<AButton @click="openManual">Open</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Manual panel</strong>
<p>Imperative control is useful for guided flows.</p>
<AButton size="small" type="secondary" @click="manualPopper?.hide()">Close</AButton>
</div>
</template>
</APopper>
</div>
</template>
<script setup>
import { nextTick, ref } from 'vue';
const manualPopper = ref();
const openManual = async () => {
manualPopper.value?.hide();
await nextTick();
manualPopper.value?.show();
};
</script>
<style scoped>
.popper-preview-row {
gap: 12px 14px;
}
.popper-preview-card {
width: max-content;
max-width: 240px;
padding: 12px 14px;
border: 1px solid var(--a-surface-border-color, var(--line));
border-radius: var(--a-radius, 14px);
background: var(--a-surface, var(--bg-bright));
box-shadow: var(--a-shadow-md, 0 4px 10px var(--shadow-5), 0 12px 28px -8px var(--shadow-10));
color: var(--text);
font-size: 13px;
line-height: 1.5;
}
.popper-preview-card strong {
display: block;
margin-bottom: 4px;
font-size: 13px;
font-weight: 700;
}
.popper-preview-card p {
margin: 0 0 10px;
color: var(--text-secondary, var(--text));
}
</style>
Examples
Manual trigger
Set triggerType="manual" and call the exposed show() / hide() methods via a template ref for full programmatic control.
<template>
<APopper ref="popper" trigger-type="manual">
<AButton @click="openPopper">Open</AButton>
<template #popup>
<div class="custom-pop">
Controlled popup
<AButton size="small" @click="popper.hide()">Close</AButton>
</div>
</template>
</APopper>
</template>
<script setup>
import { nextTick, ref } from 'vue';
const popper = ref();
const openPopper = async () => {
popper.value?.hide();
await nextTick();
popper.value?.show();
};
</script>
Right-click
Use triggerType="contextmenu" to anchor a popup to a right-click.
<template>
<APopper trigger-type="contextmenu" placement="bottom-start">
<AButton type="secondary">Right-click me</AButton>
<template #popup>
<div class="custom-pop">Context actions here</div>
</template>
</APopper>
</template>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement |
Placement | ‘bottom’ | Placement. |
triggerType |
‘hover’ | ‘click’ | ‘contextmenu’ | ‘manual’ | ‘hover’ | Trigger type. |
offset |
Number | 18 | Offset (px). |
hideDelay |
Number | 100 | Hide delay (ms). |
closeWhenClickOutside |
Boolean | true | Close on outside click. |
zIndex |
Number | 3000 | z-index. |
appendToBody |
Boolean | true | Append to body. |
transition |
String | undefined | Transition name. |
popupClass |
String | undefined | Class hook. |
group |
String | ‘’ | Shared popper group. |
Events
| Event | Payload | Description |
|---|---|---|
popupStatusChanged |
Boolean | Visibility change. |
Slots
| Slot | Props | Description |
|---|---|---|
default |
— | Trigger element. |
popup |
— | Popup content. |
Methods
| Method | Signature | Description |
|---|---|---|
show / hide |
() => void | Programmatic control (useful with triggerType="manual"). |
getTriggerEl / getPopupEl |
() => HTMLElement | DOM accessors. |
APopper
APopper 是底层定位原语(基于 Popper.js),支撑 ATooltip、ADropdownMenu 与 APopupMenu。当你需要自定义弹出层并锚定到触发元素、且要完全控制位置和触发行为时,可直接使用它。
引入
import { Popper } from '@any-design/anyui/vue';
// React: import { Popper } from '@any-design/anyui/react';
// Svelte: import { Popper } from '@any-design/anyui/svelte';
基础用法
触发器建议使用 AButton,多个触发方式可以放进 .demo-row,让预览区保持一行扫描。
<template>
<div class="demo-row popper-preview-row">
<APopper placement="top">
<AButton>悬停</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>悬停面板</strong>
<p>在触发按钮上方展示一段轻量提示。</p>
</div>
</template>
</APopper>
<APopper placement="bottom" trigger-type="click">
<AButton type="primary">点击</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>点击面板</strong>
<p>点击外部关闭,然后可以再次点击打开。</p>
</div>
</template>
</APopper>
<APopper placement="bottom-start" trigger-type="contextmenu">
<AButton type="secondary">右键</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>右键面板</strong>
<p>在外部右键可关闭,随后可以再次触发。</p>
</div>
</template>
</APopper>
<APopper ref="manualPopper" placement="right" trigger-type="manual">
<AButton @click="openManual">打开</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>手动面板</strong>
<p>命令式控制适合引导流程或复杂状态。</p>
<AButton size="small" type="secondary" @click="manualPopper?.hide()">关闭</AButton>
</div>
</template>
</APopper>
</div>
</template>
<script setup>
import { nextTick, ref } from 'vue';
const manualPopper = ref();
const openManual = async () => {
manualPopper.value?.hide();
await nextTick();
manualPopper.value?.show();
};
</script>
<style scoped>
.popper-preview-row {
gap: 12px 14px;
}
.popper-preview-card {
width: max-content;
max-width: 240px;
padding: 12px 14px;
border: 1px solid var(--a-surface-border-color, var(--line));
border-radius: var(--a-radius, 14px);
background: var(--a-surface, var(--bg-bright));
box-shadow: var(--a-shadow-md, 0 4px 10px var(--shadow-5), 0 12px 28px -8px var(--shadow-10));
color: var(--text);
font-size: 13px;
line-height: 1.5;
}
.popper-preview-card strong {
display: block;
margin-bottom: 4px;
font-size: 13px;
font-weight: 700;
}
.popper-preview-card p {
margin: 0 0 10px;
color: var(--text-secondary, var(--text));
}
</style>
示例
手动触发
设置 triggerType="manual",通过模板 ref 调用暴露的 show() / hide() 方法实现完全的命令式控制。
<template>
<APopper ref="popper" trigger-type="manual">
<AButton @click="openPopper">打开</AButton>
<template #popup>
<div class="custom-pop">
受控弹出层
<AButton size="small" @click="popper.hide()">关闭</AButton>
</div>
</template>
</APopper>
</template>
<script setup>
import { nextTick, ref } from 'vue';
const popper = ref();
const openPopper = async () => {
popper.value?.hide();
await nextTick();
popper.value?.show();
};
</script>
右键
使用 triggerType="contextmenu" 将弹出层锚定到右键。
<template>
<APopper trigger-type="contextmenu" placement="bottom-start">
<AButton type="secondary">右键点击我</AButton>
<template #popup>
<div class="custom-pop">此处放置上下文操作</div>
</template>
</APopper>
</template>
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
placement |
Placement | ‘bottom’ | 位置。 |
triggerType |
‘hover’ | ‘click’ | ‘contextmenu’ | ‘manual’ | ‘hover’ | 触发方式。 |
offset |
Number | 18 | 偏移(px)。 |
hideDelay |
Number | 100 | 隐藏延迟(ms)。 |
closeWhenClickOutside |
Boolean | true | 点击外部关闭。 |
zIndex |
Number | 3000 | z-index。 |
appendToBody |
Boolean | true | 挂载到 body。 |
transition |
String | undefined | 过渡名。 |
popupClass |
String | undefined | 类名钩子。 |
group |
String | ‘’ | 共享 popper 分组。 |
事件
| 事件 | 载荷 | 说明 |
|---|---|---|
popupStatusChanged |
Boolean | 可见性变化。 |
插槽
| 插槽 | 作用域参数 | 说明 |
|---|---|---|
default |
— | 触发元素。 |
popup |
— | 弹出内容。 |
方法
| 方法 | 签名 | 说明 |
|---|---|---|
show / hide |
() => void | 命令式控制(配合 triggerType="manual" 使用)。 |
getTriggerEl / getPopupEl |
() => HTMLElement | DOM 访问器。 |
<!-- Demo 1 -->
<template>
<div class="demo-row popper-preview-row">
<APopper placement="top">
<AButton>Hover</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Hover panel</strong>
<p>A lightweight floating note above the trigger.</p>
</div>
</template>
</APopper>
<APopper placement="bottom" trigger-type="click">
<AButton type="primary">Click</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Click panel</strong>
<p>Click outside to dismiss, then click again to reopen.</p>
</div>
</template>
</APopper>
<APopper placement="bottom-start" trigger-type="contextmenu">
<AButton type="secondary">Right click</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Context panel</strong>
<p>Right-click elsewhere to close, then trigger it again.</p>
</div>
</template>
</APopper>
<APopper ref="manualPopper" placement="right" trigger-type="manual">
<AButton @click="openManual">Open</AButton>
<template #popup>
<div class="popper-preview-card">
<strong>Manual panel</strong>
<p>Imperative control is useful for guided flows.</p>
<AButton size="small" type="secondary" @click="manualPopper?.hide()">Close</AButton>
</div>
</template>
</APopper>
</div>
</template>
<script setup>
import { nextTick, ref } from 'vue';
const manualPopper = ref();
const openManual = async () => {
manualPopper.value?.hide();
await nextTick();
manualPopper.value?.show();
};
</script>
<style scoped>
.popper-preview-row {
gap: 12px 14px;
}
.popper-preview-card {
width: max-content;
max-width: 240px;
padding: 12px 14px;
border: 1px solid var(--a-surface-border-color, var(--line));
border-radius: var(--a-radius, 14px);
background: var(--a-surface, var(--bg-bright));
box-shadow: var(--a-shadow-md, 0 4px 10px var(--shadow-5), 0 12px 28px -8px var(--shadow-10));
color: var(--text);
font-size: 13px;
line-height: 1.5;
}
.popper-preview-card strong {
display: block;
margin-bottom: 4px;
font-size: 13px;
font-weight: 700;
}
.popper-preview-card p {
margin: 0 0 10px;
color: var(--text-secondary, var(--text));
}
</style>