Popover 悬浮弹窗

当元素获取焦点、鼠标悬浮或点击时,弹出显示相关信息的浮层。基于 BrConfigProvider 实现全局主题配置,支持多触发方式、自适应定位。

组件特性

  • 🛠️ 多方向弹出:支持上下左右及其对齐偏移共 12 种方向,自动适应屏幕边缘。
  • 🖱️ 多种触发方式:支持点击、悬浮、聚焦等多种触发方式。
  • 🎨 主题定制:支持基于 BrConfigProvider 的全局主题定制,以及基于 TailwindCSS 的局部样式覆盖。

基础用法

最基础的悬浮弹窗展示,通过点击触发。

<script setup lang="ts">
import {
  BrPopover,
  BrPopoverTrigger,
  BrPopoverContent,
  BrPopoverArrow,
  BrPopoverClose,
  BrButton,
  BrInput
} from '@breezeui/vue'
</script>

<template>
  <BrPopover>
    <BrPopoverTrigger as-child>
      <BrButton variant="outline">Open Popover</BrButton>
    </BrPopoverTrigger>
    <BrPopoverContent 
      placement="bottom" 
      :side-offset="4" 
      class="w-80"
      @open-auto-focus="(e: Event) => e.preventDefault()"
    >
      <div class="space-y-2">
        <h4 class="font-medium leading-none">Dimensions</h4>
        <p class="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
        <div class="grid gap-2 pt-4">
          <div class="grid grid-cols-3 items-center gap-4">
            <label for="width" class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Width</label>
            <BrInput id="width" model-value="100%" class="col-span-2 h-8" />
          </div>
          <div class="grid grid-cols-3 items-center gap-4">
            <label for="height" class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Height</label>
            <BrInput id="height" model-value="25px" class="col-span-2 h-8" />
          </div>
        </div>
      </div>
      <BrPopoverArrow class="fill-popover" />
      <BrPopoverClose class="absolute top-2 right-2 text-muted-foreground hover:text-foreground">
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
      </BrPopoverClose>
    </BrPopoverContent>
  </BrPopover>
</template>

弹出位置

支持 placement 属性控制弹出位置,支持 12 种不同的方向。

<script setup lang="ts">
import {
  BrPopover,
  BrPopoverTrigger,
  BrPopoverContent,
  BrPopoverArrow,
  BrButton
} from '@breezeui/vue'

const positions = [
  'top-start',
  'top',
  'top-end',
  'right-start',
  'right',
  'right-end',
  'bottom-start',
  'bottom',
  'bottom-end',
  'left-start',
  'left',
  'left-end'
] as const
</script>

<template>
  <div class="flex flex-wrap gap-4 py-8 max-w-3xl justify-center mx-auto">
    <BrPopover v-for="pos in positions" :key="pos">
      <BrPopoverTrigger as-child>
        <BrButton variant="outline" class="w-28 text-xs">{{ pos }}</BrButton>
      </BrPopoverTrigger>
      <BrPopoverContent :placement="pos" class="w-auto p-4">
        <div class="text-sm font-medium">Placement: <span class="text-primary">{{ pos }}</span></div>
        <BrPopoverArrow class="fill-popover" />
      </BrPopoverContent>
    </BrPopover>
  </div>
</template>

自定义触发方式

支持 clickhoverfocus 触发方式,可通过 trigger 属性进行配置。

<script setup lang="ts">
import { ref } from 'vue'
import {
  BrPopover,
  BrPopoverTrigger,
  BrPopoverContent,
  BrPopoverArrow,
  BrButton,
  BrInput
} from '@breezeui/vue'

const inputValue = ref('')
</script>

<template>
  <div class="flex gap-8 flex-col sm:flex-row items-start sm:items-center">
    <!-- Hover Trigger -->
    <BrPopover trigger="hover" :open-delay="100">
      <BrPopoverTrigger as-child>
        <span class="underline decoration-dashed underline-offset-4 cursor-help text-sm font-medium">
          Hover over me
        </span>
      </BrPopoverTrigger>
      <BrPopoverContent placement="top" class="w-64 p-3">
        <div class="text-sm">
          <strong>BreezeUI</strong> is a modern UI library based on TailwindCSS and Vue 3.
        </div>
        <BrPopoverArrow class="fill-popover" />
      </BrPopoverContent>
    </BrPopover>

    <!-- Focus Trigger -->
    <BrPopover trigger="focus">
      <BrPopoverTrigger as-child>
        <BrInput v-model="inputValue" placeholder="Focus me..." class="w-64" />
      </BrPopoverTrigger>
      <BrPopoverContent placement="bottom-start" class="w-64 p-3">
        <div class="text-sm text-muted-foreground">
          Password must be at least 8 characters long and contain a number.
        </div>
        <BrPopoverArrow class="fill-popover" />
      </BrPopoverContent>
    </BrPopover>
  </div>
</template>

主题定制

结合 BrConfigProvider 可以全局覆盖组件的默认样式,也可以通过 TailwindCSS 局部覆盖样式。

<script setup lang="ts">
import {
  BrPopover,
  BrPopoverTrigger,
  BrPopoverContent,
  BrButton,
  BrConfigProvider
} from '@breezeui/vue'
</script>

<template>
  <BrConfigProvider
    :theme="{
      light: {
        radius: '1rem',
        popover: '220 14% 96%',
        'popover-foreground': '240 10% 3.9%',
      },
      dark: {
        radius: '1rem',
        popover: '240 10% 15%',
        'popover-foreground': '0 0% 98%',
      }
    }"
  >
    <BrPopover>
      <BrPopoverTrigger as-child>
        <BrButton>Custom Theme Popover</BrButton>
      </BrPopoverTrigger>
      <!-- Local override example -->
      <BrPopoverContent class="border-primary/20 shadow-xl p-6">
        <h4 class="font-medium mb-2">Global Theme Customization</h4>
        <p class="text-sm text-muted-foreground">
          This popover uses custom border radius and background color via BrConfigProvider.
        </p>
      </BrPopoverContent>
    </BrPopover>
  </BrConfigProvider>
</template>

加载与空状态

支持在弹窗内容中展示加载中 loading 和空数据 empty 状态。

<script setup lang="ts">
import {
  BrPopover,
  BrPopoverTrigger,
  BrPopoverContent,
  BrButton
} from '@breezeui/vue'
</script>

<template>
  <div class="flex gap-4">
    <BrPopover>
      <BrPopoverTrigger as-child>
        <BrButton variant="outline">Loading State</BrButton>
      </BrPopoverTrigger>
      <BrPopoverContent loading placement="bottom" />
    </BrPopover>
    
    <BrPopover>
      <BrPopoverTrigger as-child>
        <BrButton variant="outline">Empty State</BrButton>
      </BrPopoverTrigger>
      <BrPopoverContent empty placement="bottom" />
    </BrPopover>
  </div>
</template>

API 参考

BrPopover

属性类型默认值说明
open (v-model)boolean-受控模式下,弹窗的展开状态
defaultOpenbooleanfalse非受控模式下,弹窗的默认展开状态
trigger'click' | 'hover' | 'focus' | 'manual''click'触发方式
disabledbooleanfalse是否禁用触发器
openDelaynumber200悬浮触发时的展开延迟(毫秒)
closeDelaynumber300悬浮触发时的收起延迟(毫秒)

BrPopoverContent

属性类型默认值说明
placement'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end''bottom'弹窗出现的位置
sideOffsetnumber4弹窗距离触发元素的偏移量
maskbooleanfalse是否展示背景遮罩
loadingbooleanfalse是否处于加载态
emptybooleanfalse是否为空内容态
closeStrategyArray<'click-outside' | 'escape' | 'scroll'>['click-outside', 'escape']触发关闭的策略。可配置滚动时关闭 ('scroll')

BrPopoverTrigger

无特殊属性,通过 as-child 可以将事件委托给子元素(推荐使用)。

BrPopoverArrow

继承自 Radix Vue 的 PopoverArrow,可直接传递 class 进行样式覆盖(如 fill-popover 控制箭头颜色)。

BrPopoverClose

无特殊属性,用于包裹关闭弹窗的按钮,通过 as-child 可以将点击事件委托给子元素。