Vitral 0.2
Overlay

Popover

A non-modal dialog anchored to what opened it, closing on Escape, a press outside or tabbing out.

Import

main.ts
import { Popover } from '@vitral/vue';

Share

A flyout with a field and a button in it.

<script setup lang="ts">import { Button, InputText, Popover } from '@vitral/vue';import { ref } from 'vue'; const share = ref<InstanceType<typeof Popover> | null>(null);const copied = ref(false);const link = 'https://vitral.dev/s/7f3a9c'; function copy() {    navigator.clipboard?.writeText(link).catch(() => {});    copied.value = true;    setTimeout(() => (copied.value = false), 1500);}</script> <template>    <Button        label="Share"        icon="externalLink"        aria-haspopup="dialog"        :aria-expanded="share?.visible ? 'true' : 'false'"        :aria-controls="share?.visible ? share?.id : undefined"        @click="share?.toggle($event)"    />    <Popover ref="share" aria-labelledby="share-title">        <div style="display: flex; flex-direction: column; gap: 0.75rem; width: 20rem">            <strong id="share-title">Share this document</strong>            <div style="display: flex; gap: 0.5rem">                <InputText :model-value="link" readonly aria-label="Link" fluid />                <Button :label="copied ? 'Copied' : 'Copy'" :icon="copied ? 'check' : 'copy'" severity="secondary" @click="copy" />            </div>            <small style="color: var(--vt-text-muted-color)">Anyone with the link can view.</small>        </div>    </Popover></template>

Placement

`placement` picks the side; it flips when there is no room.

<script setup lang="ts">import { Button, Popover } from '@vitral/vue';import { ref } from 'vue'; const members = ref<InstanceType<typeof Popover> | null>(null);const people = [    { name: 'Ana Souza', role: 'Owner' },    { name: 'Bruno Lima', role: 'Editor' },    { name: 'Carla Dias', role: 'Viewer' }];</script> <template>    <Button        label="Team members"        icon="user"        severity="secondary"        aria-haspopup="dialog"        :aria-expanded="members?.visible ? 'true' : 'false'"        @click="members?.toggle($event)"    />    <Popover ref="members" placement="right-start" aria-label="Team members">        <ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; min-width: 14rem">            <li v-for="p in people" :key="p.name" style="display: flex; justify-content: space-between; gap: 1rem">                <span>{{ p.name }}</span>                <small style="color: var(--vt-text-muted-color)">{{ p.role }}</small>            </li>        </ul>        <Button label="Manage" variant="link" size="small" style="margin-top: 0.5rem" @click="members?.hide()" />    </Popover></template>

API

Read from packages/vue/src/components/Popover/types.ts, so it says what the component actually accepts.

Props

NameTypeDescription
placementOverlayPlacementWhere it opens relative to its target; it flips when there is no room. Defaults to `'bottom-start'`.
dismissablebooleanClose on a press outside. Defaults to true.
closeOnEscapebooleanDefaults to true.
offsetnumberGap to the target, in pixels. Defaults to 8.
appendTostring`'body'` (the default), `'self'` to render in place, or a selector.

Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.

Emits

EventPayloadDescription
show——
hide——

Slots

NameSlot propsDescription
default(props: { closeCallback: () => void })—