弹出框
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverTrigger
class="rounded-full w-[35px] h-[35px] inline-flex items-center justify-center text-grass11 bg-white shadow-[0_2px_10px] shadow-blackA7 hover:bg-green3 focus:shadow-[0_0_0_2px] focus:shadow-black cursor-default outline-none"
aria-label="Update dimensions"
>
<Icon icon="radix-icons:mixer-horizontal" />
</PopoverTrigger>
<PopoverPortal>
<PopoverContent
side="bottom"
:side-offset="5"
class="rounded p-5 w-[260px] bg-white shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.green7)] will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
>
<div class="flex flex-col gap-2.5">
<p class="text-mauve12 text-[15px] leading-[19px] font-semibold mb-2.5">
Dimensions
</p>
<fieldset class="flex gap-5 items-center">
<label
class="text-[13px] text-grass11 w-[75px]"
for="width"
> Width </label>
<input
id="width"
class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
defaultValue="100%"
>
</fieldset>
<fieldset class="flex gap-5 items-center">
<label
class="text-[13px] text-grass11 w-[75px]"
for="maxWidth"
> Max. width </label>
<input
id="maxWidth"
class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
defaultValue="300px"
>
</fieldset>
<fieldset class="flex gap-5 items-center">
<label
class="text-[13px] text-grass11 w-[75px]"
for="height"
> Height </label>
<input
id="height"
class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
defaultValue="25px"
>
</fieldset>
<fieldset class="flex gap-5 items-center">
<label
class="text-[13px] text-grass11 w-[75px]"
for="maxHeight"
> Max. height </label>
<input
id="maxHeight"
class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
defaultValue="none"
>
</fieldset>
</div>
<PopoverClose
class="rounded-full h-[25px] w-[25px] inline-flex items-center justify-center text-grass11 absolute top-[5px] right-[5px] hover:bg-green4 focus:shadow-[0_0_0_2px] focus:shadow-green7 outline-none cursor-default"
aria-label="Close"
>
<Icon icon="radix-icons:cross-2" />
</PopoverClose>
<PopoverArrow class="fill-white" />
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
功能
- 可以是受控的或不受控的。
- 自定义侧面、对齐方式、偏移量、碰撞处理。
- 可以选择渲染指向箭头。
- 焦点是完全管理和可定制的。
- 支持模态和非模态模式。
- 关闭和分层行为是高度可定制的。
安装
从命令行安装组件。
$ npm add radix-vue
剖析
导入所有部分并将其拼凑在一起。
<script setup>
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverTrigger />
<PopoverAnchor />
<PopoverPortal>
<PopoverContent>
<PopoverClose />
<PopoverArrow />
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
API 参考
根
包含弹出框的所有部分。
道具 | 默认 | 类型 |
---|---|---|
defaultOpen | false | boolean 弹出框在最初呈现时的打开状态。当您不需要控制其打开状态时使用。 |
modal | false | boolean 弹出框的模态性。当设置为 true 时,外部元素的交互将被禁用,并且只有弹出框内容对屏幕阅读器可见。 |
open | boolean 弹出框的受控打开状态。 |
发射 | 有效载荷 |
---|---|
update:open | [value: boolean] 弹出框的打开状态发生变化时调用的事件处理程序。 |
插槽 (默认) | 有效载荷 |
---|---|
open | boolean 当前打开状态 |
触发器
切换弹出框的按钮。默认情况下,PopoverContent
将相对于触发器定位。
道具 | 默认 | 类型 |
---|---|---|
as | 'button' | AsTag | Component 此组件应呈现为的元素或组件。可以被 |
asChild | boolean 更改为子元素传递的默认渲染元素,合并它们的 props 和行为。 阅读我们的 组合 指南以了解更多详细信息。 |
数据属性 | 价值 |
---|---|
[data-state] | "open" | "closed" |
锚点
一个可选的元素,用于将PopoverContent
定位到其旁边。如果未使用此部分,则内容将与PopoverTrigger
并排定位。
道具 | 默认 | 类型 |
---|---|---|
as | 'div' | AsTag | Component 此组件应呈现为的元素或组件。可以被 |
asChild | boolean 更改为子元素传递的默认渲染元素,合并它们的 props 和行为。 阅读我们的 组合 指南以了解更多详细信息。 | |
元素 | 可测量 |
门户
使用时,将内容部分门户到body
中。
道具 | 默认 | 类型 |
---|---|---|
disabled | boolean 禁用传送并内联渲染组件 | |
forceMount | boolean 用于在需要更多控制时强制挂载。当使用 Vue 动画库控制动画时很有用。 | |
to | string | HTMLElement Vue 原生传送组件道具 |
内容
弹出框打开时弹出的组件。
道具 | 默认 | 类型 |
---|---|---|
align | 'start' | 'center' | 'end' 相对于触发器的首选对齐方式。发生碰撞时可能会改变。 | |
alignOffset | number 从 | |
arrowPadding | number 箭头和内容边缘之间的填充。如果您的内容有圆角,这将防止它溢出角落。 | |
as | 'div' | AsTag | Component 此组件应呈现为的元素或组件。可以被 |
asChild | boolean 更改为子元素传递的默认渲染元素,合并它们的 props 和行为。 阅读我们的 组合 指南以了解更多详细信息。 | |
avoidCollisions | boolean 当为 | |
collisionBoundary | Element | (Element | null)[] | null 用作碰撞边界的元素。默认情况下,这是视窗,但您可以提供其他元素以将其包含在此检查中。 | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> 从边界边缘到应该发生碰撞检测的位置的像素距离。接受一个数字(所有边都相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。 | |
disableOutsidePointerEvents | boolean 当为 | |
forceMount | boolean 用于在需要更多控制时强制挂载。当使用 Vue 动画库控制动画时很有用。 | |
hideWhenDetached | boolean 当触发器完全被遮挡时是否隐藏内容。 | |
prioritizePosition | boolean 强制内容定位在视窗内。 可能会与引用元素重叠,这可能不是想要的。 | |
side | 'top' | 'right' | 'bottom' | 'left' 打开时渲染到触发器的首选侧面。当发生碰撞并且启用 avoidCollisions 时将被反转。 | |
sideOffset | number 与触发器之间的像素距离。 | |
sticky | 'partial' | 'always' 对齐轴上的粘性行为。 | |
trapFocus | boolean 是否应该将焦点困在 | |
updatePositionStrategy | 'always' | 'optimized' 在每一帧动画中更新浮动元素位置的策略。 |
发射 | 有效载荷 |
---|---|
closeAutoFocus | [event: Event] 关闭时自动聚焦时调用的事件处理程序。可以阻止。 |
escapeKeyDown | [event: KeyboardEvent] 按下 Esc 键时调用的事件处理程序。可以阻止。 |
focusOutside | [event: FocusOutsideEvent] 焦点移出 |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent] 在 |
openAutoFocus | [event: Event] 打开时自动聚焦时调用的事件处理程序。可以阻止。 |
pointerDownOutside | [event: PointerDownOutsideEvent] 在 |
数据属性 | 价值 |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS 变量 | 描述 |
---|---|
--radix-popover-content-transform-origin | 从内容和箭头位置/偏移计算的 transform-origin |
--radix-popover-content-available-width | 触发器和边界边缘之间的剩余宽度 |
--radix-popover-content-available-height | 触发器和边界边缘之间的剩余高度 |
--radix-popover-trigger-width | 触发器的宽度 |
--radix-popover-trigger-height | 触发器的高度 |
箭头
一个可选的箭头元素,用于与弹出框一起渲染。这可用于帮助视觉上将锚点与PopoverContent
连接起来。必须渲染在PopoverContent
内部。
道具 | 默认 | 类型 |
---|---|---|
as | 'svg' | AsTag | Component 此组件应呈现为的元素或组件。可以被 |
asChild | boolean 更改为子元素传递的默认渲染元素,合并它们的 props 和行为。 阅读我们的 组合 指南以了解更多详细信息。 | |
height | 5 | number 箭头的像素高度。 |
width | 10 | number 箭头的像素宽度。 |
关闭
关闭打开弹出框的按钮。
道具 | 默认 | 类型 |
---|---|---|
as | 'button' | AsTag | Component 此组件应呈现为的元素或组件。可以被 |
asChild | boolean 更改为子元素传递的默认渲染元素,合并它们的 props 和行为。 阅读我们的 组合 指南以了解更多详细信息。 |
示例
约束内容大小
您可能希望约束内容的宽度,使其与触发器宽度匹配。您可能还想约束其高度,使其不超过视窗。
我们公开了几个 CSS 自定义属性,例如--radix-popover-trigger-width
和--radix-popover-content-available-height
来支持这一点。使用它们来约束内容尺寸。
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent
class="PopoverContent"
:side-offset="5"
>
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
width: var(--radix-popover-trigger-width);
max-height: var(--radix-popover-content-available-height);
}
支持原点的动画
我们提供了一个 CSS 自定义属性 --radix-popover-content-transform-origin
。使用它可以根据 side
、sideOffset
、align
、alignOffset
和任何冲突来从内容的计算原点进行动画。
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent class="PopoverContent">
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
transform-origin: var(--radix-popover-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
支持碰撞的动画
我们提供了 data-side
和 data-align
属性。它们的 value 会在运行时改变以反映碰撞。使用它们可以创建支持碰撞和方向的动画。
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverTrigger>…</PopoverTrigger>
<PopoverPortal>
<PopoverContent class="PopoverContent">
…
</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.PopoverContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.PopoverContent[data-side="top"] {
animation-name: slideUp;
}
.PopoverContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
使用自定义锚点
如果你不想使用触发器作为锚点,可以将内容锚定到另一个元素。
// index.vue
<script setup>
import { PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
<PopoverRoot>
<PopoverAnchor as-child>
<div class="Row">
Row as anchor <PopoverTrigger>Trigger</PopoverTrigger>
</div>
</PopoverAnchor>
<PopoverPortal>
<PopoverContent>…</PopoverContent>
</PopoverPortal>
</PopoverRoot>
</template>
/* styles.css */
.Row {
background-color: gainsboro;
padding: 20px;
}
无障碍性
键盘交互
键 | 描述 |
---|---|
空格 | 打开/关闭弹出框。 |
回车 | 打开/关闭弹出框。 |
Tab | 将焦点移动到下一个可聚焦元素 |
Shift + Tab | 将焦点移动到上一个可聚焦元素 |
Esc | 关闭弹出框并将焦点移动到 PopoverTrigger 。 |
自定义 API
通过将基本部分抽象到您自己的组件中来创建您自己的 API。
抽象箭头并设置默认配置
此示例抽象了 PopoverArrow
部分并设置了默认 sideOffset
配置。
用法
<script setup lang="ts">
import { Popover, PopoverContent, PopoverTrigger } from './your-popover'
</script>
<template>
<Popover>
<PopoverTrigger>Popover trigger</PopoverTrigger>
<PopoverContent>Popover content</PopoverContent>
</Popover>
</template>
实现
// your-popover.ts
export { default as PopoverContent } from 'PopoverContent.vue'
export { PopoverRoot as Popover, PopoverTrigger } from 'radix-vue'
<!-- PopoverContent.vue -->
<script setup lang="ts">
import { PopoverContent, type PopoverContentEmits, type PopoverContentProps, PopoverPortal, useForwardPropsEmits, } from 'radix-vue'
const props = defineProps<PopoverContentProps>()
const emits = defineEmits<PopoverContentEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<PopoverPortal>
<PopoverContent v-bind="{ ...forwarded, ...$attrs }">
<slot />
</PopoverContent>
</PopoverPortal>
</template>