Popover
A floating card triggered by hover、click、focus or contextmenu. Built on top of Tooltip.
Basic Usage
Use placement to set position of Popover, and use content to set content of the Popover.
<template>
<div class="max-w-600px mx-auto">
<div class="flex justify-center">
<re-popover placement="top-start" content="Top Start Text Prompt" title="Title">
<re-button>top-start</re-button>
</re-popover>
<re-popover placement="top" content="Top Center Text Prompt" title="Title">
<re-button>top</re-button>
</re-popover>
<re-popover placement="top-end" content="Top End Text Prompt" title="Title">
<re-button>top-end</re-button>
</re-popover>
</div>
<div class="flex justify-between">
<div class="flex flex-col items-start">
<re-popover
placement="left-start"
width="120"
content="Left Start Text Prompt"
title="Title"
>
<re-button>left-start</re-button>
</re-popover>
<re-popover placement="left" width="120px" content="Left Middle Text Prompt" title="Title">
<re-button class="mt-1em ml-0!">left</re-button>
</re-popover>
<re-popover placement="left-end" width="120px" content="Left End Text Prompt" title="Title">
<re-button class="mt-1em ml-0!">left-end</re-button>
</re-popover>
</div>
<div class="flex flex-col items-end">
<re-popover
placement="right-start"
width="120px"
content="Right Start Text Prompt"
title="Title"
>
<re-button>right-start</re-button>
</re-popover>
<re-popover
placement="right"
width="120px"
content="Right Center Text Prompt"
title="Title"
>
<re-button class="mt-1em ml-0!">right</re-button>
</re-popover>
<re-popover
placement="right-end"
width="120px"
content="Right End Text Prompt"
title="Title"
>
<re-button class="mt-1em ml-0!">right-end</re-button>
</re-popover>
</div>
</div>
<div class="flex justify-center">
<re-popover placement="bottom-start" content="Bottom Start Text Prompt" title="Title">
<re-button>bottom-start</re-button>
</re-popover>
<re-popover placement="bottom" content="Bottom Center Text Prompt" title="Title">
<re-button>bottom</re-button>
</re-popover>
<re-popover placement="bottom-end" content="Bottom End Text Prompt" title="Title">
<re-button>bottom-end</re-button>
</re-popover>
</div>
</div>
</template>Title
Use title or slot#title to provide the title.
<template>
<re-popover
content="Content text Content text Content text Content"
title="This is a Title"
max-width="200"
trigger="hover"
>
<re-button>Hover me</re-button>
</re-popover>
<re-popover
content="Content text Content text Content text Content"
max-width="200"
trigger="hover"
>
<template #title>
<div style="color: var(--re-color-primary-base); padding: 0.5em 1em; font-size: 16px">
This is a Slot Title
</div>
</template>
<re-button>Slot Title</re-button>
</re-popover>
</template>Trigger
Use hover 、 click 、 focus and contextmenu to display Popover by trigger attribute.
<template>
<re-popover
content="Popover text Popover text Popover text"
width="200"
title="Title"
trigger="hover"
>
<re-button>Hover</re-button>
</re-popover>
<re-popover
content="Popover text Popover text Popover text"
width="200"
title="Title"
trigger="click"
>
<re-button>Click</re-button>
</re-popover>
<re-popover
content="Popover text Popover text Popover text"
width="200"
title="Title"
trigger="focus"
>
<re-button>Focus</re-button>
</re-popover>
<re-popover
content="Popover text Popover text Popover text"
width="200"
title="Title"
trigger="contextmenu"
placement="right"
>
<re-button>Contextmenu</re-button>
</re-popover>
</template>Content
Use content or slot#content to set Popover content, is-html for render HTML content.
<template>
<re-popover content="<i>Text Content</i>" title="Title" trigger="hover">
<re-button>Text Content</re-button>
</re-popover>
<re-popover content="<i>HTML Content</i>" is-html title="Title" trigger="hover">
<re-button>HTML Content</re-button>
</re-popover>
<re-popover title="Title" trigger="hover">
<re-button>Slot Content</re-button>
<template #content>
<div style="color: var(--re-color-primary-base); padding: 0 1em 1em">The Slot Content</div>
</template>
</re-popover>
</template>Disabled
Use disabled to disable Popover.
<template>
<div>
<re-popover
:disabled="disabled"
content="Disabled Popover Disabled Popover Disabled Popover"
width="200"
title="Title"
trigger="hover"
>
<re-button type="success" :disabled="disabled">
{{ disabled ? 'Disabled' : 'Enabled' }}
</re-button>
</re-popover>
<re-button class="ml-5em!" @click="toggle">Click to toggle</re-button>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const disabled = ref<boolean>(false)
const toggle = () => {
disabled.value = !disabled.value
}
</script>Customize Boundary
Use boundary and rootBoundary to set Popover boundary.
<template>
<div>
<div class="flex flex-wrap">
<div
ref="container1"
class="h-300px grow basis-300px of-x-hidden of-y-auto text-center demo-border"
>
<div class="h-300px"></div>
<re-popover
v-if="container1"
:boundary="container1"
content="Scroll me to top boundary"
title="Title"
width="200"
:visible="visible"
>
<re-button>boundary: container</re-button>
</re-popover>
<div class="h-300px"></div>
</div>
<div
ref="container2"
class="h-300px grow flex basis-300px items-center of-x-auto demo-border"
>
<div class="w-500px shrink-0"></div>
<re-popover
v-if="container2"
content="Scroll me to left boundary"
title="Title"
width="200"
:boundary="container2"
:visible="visible"
placement="left"
>
<re-button>boundary: container</re-button>
</re-popover>
<div class="w-500px shrink-0"></div>
</div>
</div>
<div
ref="container3"
class="h-300px flex-1 of-hidden of-y-auto shrink-0 text-center demo-border"
>
<div class="h-300px"></div>
<re-popover
content="Scroll me to body top boundary"
title="Title"
width="200"
:visible="visible"
>
<re-button>rootBoundary: 'viewport'</re-button>
</re-popover>
<div class="h-300px"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const visible = ref<boolean>(true)
const container1 = ref()
const container2 = ref()
const container3 = ref()
onMounted(() => {
container1.value.scrollTo(0, 160)
container2.value.scrollTo(220, 0)
container3.value.scrollTo(0, 160)
})
</script>Flip
By default, the Popover layer will auto flip based on the viewport, but when one of the below:
1、boundary: 'clippingAncestors' and altBoundary: true.
2、elementContext: 'reference'.
it will flip based on the parent container of the reference which is overflow: hidden/auto/scroll.
Otherwise, use flipOptions attribute for more behaviors.
<template>
<div class="flex flex-wrap">
<div
ref="container1"
class="h-300px grow basis-300px of-hidden of-y-auto shrink-0 text-center demo-border"
>
<div class="h-300px"></div>
<re-popover
v-model:visible="visible"
alt-boundary
content="Scroll me to top boundary"
title="Title"
width="200"
>
<re-button>altBoundary: true</re-button>
</re-popover>
<div class="h-300px"></div>
</div>
<div
ref="container2"
class="h-300px grow basis-300px flex items-center min-w-0 of-x-auto demo-border"
>
<div class="w-500px shrink-0"></div>
<re-popover
content="Scroll me to left boundary"
alt-boundary
:visible="visible"
placement="left"
title="Title"
width="200"
>
<re-button>altBoundary: true</re-button>
</re-popover>
<div class="w-500px shrink-0"></div>
</div>
</div>
<div class="flex flex-wrap">
<div
ref="container3"
class="h-300px grow basis-300px of-hidden of-y-auto shrink-0 text-center demo-border"
>
<div class="h-300px"></div>
<re-popover
element-context="reference"
content="elementContext: 'reference'"
title="Title"
width="200"
:visible="visible"
>
<re-button>Scroll me to top boundary</re-button>
</re-popover>
<div class="h-300px"></div>
</div>
<div
ref="container4"
class="h-300px grow basis-300px flex items-center min-w-0 of-x-auto demo-border"
>
<div class="w-500px shrink-0"></div>
<re-popover
content="elementContext: 'reference'"
element-context="reference"
:visible="visible"
placement="left"
title="Title"
width="200"
>
<re-button>Scroll me to left boundary</re-button>
</re-popover>
<div class="w-500px shrink-0"></div>
</div>
</div>
<div class="flex flex-wrap">
<div
ref="container5"
class="h-300px grow basis-300px of-hidden of-y-auto shrink-0 text-center demo-border"
>
<div class="h-300px"></div>
<re-popover
alt-boundary
:flip-options="{ fallbackPlacements: ['top', 'right', 'bottom'] }"
content="Scroll me to top boundary"
title="Title"
width="200"
:visible="visible"
>
<re-button class="mr-15em">altBoundary: true</re-button>
</re-popover>
<div class="h-300px"></div>
</div>
<div
ref="container6"
class="h-300px grow basis-300px flex items-center min-w-0 of-x-auto demo-border"
>
<div class="w-500px shrink-0"></div>
<re-popover
content="Scroll me to left boundary"
alt-boundary
:flip-options="{ fallbackPlacements: ['top', 'right', 'bottom'] }"
:visible="visible"
placement="left"
title="Title"
width="200"
>
<re-button>altBoundary: true</re-button>
</re-popover>
<div class="w-500px shrink-0"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const visible = ref<boolean>(true)
const container1 = ref()
const container2 = ref()
const container3 = ref()
const container4 = ref()
const container5 = ref()
const container6 = ref()
onMounted(() => {
container1.value?.scrollTo(0, 160)
container2.value?.scrollTo(220, 0)
container3.value?.scrollTo(0, 160)
container4.value?.scrollTo(220, 0)
container5.value?.scrollTo(0, 160)
container6.value?.scrollTo(220, 0)
})
</script>External Reference
Use is-external to activate external-ref, this allows you to place the reference element outside the Popover.
Basic External Trigger Reference
Multiple External Trigger References for singleton
<template>
<p>Basic External Trigger Reference</p>
<re-popover
content="Popover text Popover text Popover text"
trigger="hover"
is-external
:external-ref="externalRef"
title="Title"
width="200"
></re-popover>
<re-button ref="buttonRef" @mouseenter="setExternal">Hover me</re-button>
<p>Multiple External Trigger References for singleton</p>
<re-popover
content="Popover text Popover text Popover text"
trigger="hover"
title="Title"
width="200"
is-external
:external-ref="hoverRef"
></re-popover>
<re-button ref="buttonRef1" @mouseenter="setRef1()">Hover me 1</re-button>
<re-button ref="buttonRef2" @mouseenter="setRef2()">Hover me 2</re-button>
<re-button ref="buttonRef3" @mouseenter="setRef3()">Hover me 3</re-button>
<p></p>
<re-popover
content="Popover text Popover text Popover text"
trigger="click"
title="Title"
width="200"
is-external
:external-ref="clickRef"
></re-popover>
<re-button ref="buttonRef4" @click="setRef4()">Click me 1</re-button>
<re-button ref="buttonRef5" @click="setRef5()">Click me 2</re-button>
<re-button ref="buttonRef6" @click="setRef6()">Click me 3</re-button>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const buttonRef = ref()
const externalRef = ref()
const setExternal = () => {
externalRef.value = buttonRef.value
}
const buttonRef1 = ref()
const buttonRef2 = ref()
const buttonRef3 = ref()
const hoverRef = ref()
const setRef1 = () => {
hoverRef.value = buttonRef1.value
}
const setRef2 = () => {
hoverRef.value = buttonRef2.value
}
const setRef3 = () => {
hoverRef.value = buttonRef3.value
}
const buttonRef4 = ref()
const buttonRef5 = ref()
const buttonRef6 = ref()
const clickRef = ref()
const setRef4 = () => {
clickRef.value = buttonRef4.value
}
const setRef5 = () => {
clickRef.value = buttonRef5.value
}
const setRef6 = () => {
clickRef.value = buttonRef6.value
}
</script>Virtual Reference
Use is-virtual to activate virtual-ref, it accepts an object with layout properties to build the reference, visible maybe is needed.
<template>
<re-popover
content="Popover text Popover text Popover text"
:visible="visible"
is-virtual
:virtual-ref="virtualRef"
width="200"
title="Title"
></re-popover>
<re-button @click="visible = !visible">Click me</re-button>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'
const visible = ref<boolean>(false)
const position = ref<Record<string, number>>({
width: 0,
height: 0,
x: 0,
y: 0,
top: 0,
left: 0,
right: 0,
bottom: 0
})
const virtualRef = ref({
getBoundingClientRect: () => position.value
})
const move = ({ clientX, clientY }: MouseEvent) => {
position.value = {
width: 0,
height: 0,
x: clientX,
y: clientY,
top: clientY,
left: clientX,
right: clientX,
bottom: clientY
}
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
</script>Selection Range Build A Virtual Reference
Use is-virtual to activate virtual-ref, it also allows a selection Range to build the reference.
<template>
<re-popover
content="Popover text Popover text Popover text"
:visible="visible"
is-virtual
:virtual-ref="rangeRef"
width="200"
title="Title"
></re-popover>
<span ref="textRef">Select the text to visible the Popover</span>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const visible = ref<boolean>(false)
const textRef = ref<HTMLSpanElement | null>(null)
const range = ref<Range | null>()
const rangeRef = {
getBoundingClientRect: () => range.value?.getBoundingClientRect(),
getClientRects: () => range.value?.getClientRects()
}
const mouseup = () => {
range.value?.selectNode(textRef.value!)
const selection = window.getSelection()
if (!textRef.value || !selection || selection.rangeCount === 0) return
const currentRange = selection.getRangeAt(0)
const container = currentRange.commonAncestorContainer
const text = selection.toString().trim()
// Ensure the selection originates from the target element
if (textRef.value.contains(container!)) {
range.value = currentRange.cloneRange()
visible.value = !!text
}
}
const selectionChange = () => {
const selection = window.getSelection()
const text = selection?.toString()?.trim()
if (!text) {
visible.value = false
}
}
onMounted(() => {
document.addEventListener('mouseup', mouseup)
document.addEventListener('touchend', mouseup)
document.addEventListener('selectionchange', selectionChange)
})
onBeforeUnmount(() => {
document.removeEventListener('mouseup', mouseup)
document.removeEventListener('touchend', mouseup)
document.removeEventListener('selectionchange', selectionChange)
})
</script>Popover Attributes
| Name | Description | Type | Default |
|---|---|---|---|
| placement | Popover position | enum | top |
| trigger | The trigger event name | enum | hover |
| title | Popover title | string | '' |
| content | Popover content | string | '' |
| is-html | Whether content is treated as HTML string | boolean | — |
| disabled | Whether Popover is disabled | boolean | — |
| visible / v-model:visible | Whether Popover is visible | boolean | — |
| offset | Offset from the reference edge. See Floating UI OffsetOptions for details | number | interface | 10 |
| width | Popover width | number | string | — |
| height | Popover height | number | string | — |
| max-width | Popover max width | number | string | — |
| max-height | Popover max height | number | string | — |
| theme | Popover theme | 'dark' |'light' | light |
| reverse-theme | whether to reverse Popover theme color | boolean | false |
| append-to | Target element for appending Popover | cssSelector | HTMLElement | body |
| teleported | Whether to teleport Popover to the appendTo target | boolean | true |
| persistent | Whether to keep the floating open on hover | boolean | true |
| click-to-toggle | Whether to toggle the floating on click, only trigger='click' | boolean | true |
| floating-class | Custom class for the floating | string | object | Array | — |
| floating-style | Custom style for the floating | string | object | Array | — |
| show-delay | Delay(ms) before Popover appears , but not effective when visible is present or is-virtual or is-external | number | 0 |
| hide-delay | Delay(ms) before Popover disappears , but not effective when visible is present or is-virtual or is-external | number | 200 |
| show-arrow | Whether to show arrow for Popover | boolean | true |
| arrow-size | The arrow size | number | 8 |
| arrow-offset | Padding between the arrow and the Popover edge | number | 4 |
| arrow-options | Floating UI ArrowOptions Parameters | interface | — |
| transition | Name of Vue3 Transition component for customize transition | string | popover |
| css-transition | Css of Vue3 Transition component | boolean | true |
| transform | Whether to use transform positioning instead of top/left positioning | boolean | true |
| strategy | The type of CSS position property to use | 'absolute' | 'fixed' | absolute |
| css-duration | Duration of Vue3 Transition component for fade in/fade out | number | 0 |
| boundary | Boundary of Floating UI DetectOverflowOptions : interface | union | clippingAncestors |
| alt-boundary | AltBoundary of Floating UI DetectOverflowOptions | boolean | false |
| root-boundary | RootBoundary of Floating UI DetectOverflowOptions | union | viewport |
| element-context | ElementContext of Floating UI DetectOverflowOptions | 'reference' | 'floating' | floating |
| flip-offset | Offset from boundary before flipping, Padding of Floating UI DetectOverflowOptions | union | 8 |
| flip-options | Floating UI FlipOptions Parameters | interface | — |
| inline-options | Floating UI InlineOptions Parameters | interface | — |
| is-external | Whether external reference is enabled | boolean | — |
| external-ref | External reference target | HTMLElement | — |
| is-virtual | Whether virtual reference is enabled | boolean | — |
| virtual-ref | Virtual reference object for Floating UI VirtualElement | interface | — |
Popover Events
| Name | Description | Parameters |
|---|---|---|
| before-enter | Vue3 <Transition> event hook | Function |
| enter | Vue3 <Transition> event hook | Function |
| after-enter | Vue3 <Transition> event hook | Function |
| before-leave | Vue3 <Transition> event hook | Function |
| leave | Vue3 <Transition> event hook | Function |
| after-leave | Vue3 <Transition> event hook | Function |
| enter-cancelled | Vue3 <Transition> event hook | Function |
| leave-cancelled | Vue3 <Transition> event hook | Function |
Popover Slots
| Name | Description |
|---|---|
| default | Customize default reference |
| title | Customize the title |
| content | Customize default content |
Popover Exposes
| Name | Description | Type |
|---|---|---|
| update | Manually updates the Popover position | Function |
| show | Manually show the Popover | Function |
| hide | Manually hide the Popover | Function |
| triggerRef | The trigger refernce element | HTMLElement |
| floatingRef | The Popover layer element | HTMLElement |
| arrowRef | The Popover arrow element | HTMLElement |
