Popover
<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リファレンス
ルート
Popoverのすべての部品を含みます。
プロパティ | デフォルト値 | 型 |
---|---|---|
defaultOpen | false | boolean 最初にレンダリングされたときのPopoverの開閉状態。開閉状態を制御する必要がない場合に使用します。 |
modal | false | boolean Popoverのモーダル性。trueに設定すると、外部要素とのインタラクションが無効になり、スクリーンリーダーにはPopoverコンテンツのみが表示されます。 |
open | boolean Popoverの制御された開閉状態。 |
イベント | ペイロード |
---|---|
update:open | [value: boolean] Popoverの開閉状態が変更されたときに呼び出されるイベントハンドラー。 |
スロット (デフォルト) | ペイロード |
---|---|
open | boolean 現在の開閉状態 |
トリガー
Popoverを切り替えるボタン。デフォルトでは、`PopoverContent`はトリガーに対して配置されます。
プロパティ | デフォルト値 | 型 |
---|---|---|
as | 'button' | AsTag | Component このコンポーネントがレンダリングされる要素またはコンポーネント。`asChild`で上書きできます。 |
asChild | boolean 子として渡された要素に対してデフォルトのレンダリング要素を変更し、それらのプロパティと動作をマージします。 詳細は、コンポジションガイドをご覧ください。 |
データ属性 | 値 |
---|---|
[data-state] | "open" | "closed" |
アンカー
`PopoverContent`を配置するオプションの要素。この部分を使用しない場合、コンテンツは`PopoverTrigger`に沿って配置されます。
プロパティ | デフォルト値 | 型 |
---|---|---|
as | 'div' | AsTag | Component このコンポーネントがレンダリングされる要素またはコンポーネント。`asChild`で上書きできます。 |
asChild | boolean 子として渡された要素に対してデフォルトのレンダリング要素を変更し、それらのプロパティと動作をマージします。 詳細は、コンポジションガイドをご覧ください。 | |
要素 | 測定可能 |
ポータル
使用する場合、コンテンツ部分を`body`にポータルします。
プロパティ | デフォルト値 | 型 |
---|---|---|
disabled | boolean テレポートを無効にして、コンポーネントをインラインでレンダリングします。 | |
forceMount | boolean より多くの制御が必要な場合に強制的にマウントするために使用します。Vueアニメーションライブラリでアニメーションを制御する場合に便利です。 | |
to | string | HTMLElement Vueネイティブのテレポートコンポーネントプロパティ`:to` |
コンテンツ
Popoverが開いているときに表示されるコンポーネント。
プロパティ | デフォルト値 | 型 |
---|---|---|
align | 'start' | 'center' | 'end' トリガーに対する優先アライメント。衝突が発生した場合は変更される可能性があります。 | |
alignOffset | number `start`または`end`のアライメントオプションからのピクセル単位のオフセット。 | |
arrowPadding | number 矢印とコンテンツの端の間のパディング。コンテンツにborder-radiusがある場合、これにより角がオーバーフローするのを防ぎます。 | |
as | 'div' | AsTag | Component このコンポーネントがレンダリングされる要素またはコンポーネント。`asChild`で上書きできます。 |
asChild | boolean 子として渡された要素に対してデフォルトのレンダリング要素を変更し、それらのプロパティと動作をマージします。 詳細は、コンポジションガイドをご覧ください。 | |
avoidCollisions | boolean `true`の場合、境界線との衝突を防ぐために、サイドとアライメントの設定を上書きします。 | |
collisionBoundary | Element | (Element | null)[] | null 衝突境界として使用される要素。デフォルトではビューポートですが、このチェックに含める追加の要素を提供できます。 | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> 衝突検出が発生する境界線の端からのピクセル単位の距離。数値(すべての辺で同じ)、または部分的なパディングオブジェクト(例:{ top: 20, left: 20 })を受け付けます。 | |
disableOutsidePointerEvents | boolean `true`の場合、`DismissableLayer`の外側の要素でのホバー/フォーカス/クリック操作が無効になります。ユーザーは、外側の要素を操作するには2回クリックする必要があります。1回目は`DismissableLayer`を閉じ、2回目は要素をトリガーします。 | |
forceMount | boolean より多くの制御が必要な場合に強制的にマウントするために使用します。Vueアニメーションライブラリでアニメーションを制御する場合に便利です。 | |
hideWhenDetached | boolean トリガーが完全に隠されたときにコンテンツを隠すかどうか。 | |
prioritizePosition | boolean コンテンツをビューポート内に強制的に配置します。 参照要素と重なる可能性があり、望ましくない場合があります。 | |
side | 'top' | 'right' | 'bottom' | 'left' 開いているときにレンダリングするトリガーの優先側。衝突が発生し、avoidCollisionsが有効になっている場合は反転されます。 | |
sideOffset | number トリガーからのピクセル単位の距離。 | |
sticky | 'partial' | 'always' アライメント軸のスティッキー動作。`partial`は、トリガーが境界内に少なくとも部分的に存在する限り、コンテンツを境界内に維持します。「always」は、関係なくコンテンツを境界内に維持します。 | |
trapFocus | boolean `MenuContent`内でフォーカスをトラップするかどうか。 | |
updatePositionStrategy | 'always' | 'optimized' 各アニメーションフレームでフローティング要素の位置を更新する戦略。 |
イベント | ペイロード |
---|---|
closeAutoFocus | [event: Event] 閉じるときに自動フォーカスする際に呼び出されるイベントハンドラー。防止できます。 |
escapeKeyDown | [event: KeyboardEvent] Escキーが押されたときに呼び出されるイベントハンドラー。防止できます。 |
focusOutside | [event: FocusOutsideEvent] `DismissableLayer`の外側にフォーカスが移動したときに呼び出されるイベントハンドラー。防止できます。 |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent] `DismissableLayer`の外側でインタラクションが発生したときに呼び出されるイベントハンドラー。具体的には、外側で`pointerdown`イベントが発生した場合、またはその外側にフォーカスが移動した場合。防止できます。 |
openAutoFocus | [event: Event] 開くときに自動フォーカスする際に呼び出されるイベントハンドラー。防止できます。 |
pointerDownOutside | [event: PointerDownOutsideEvent] `DismissableLayer`の外側で`pointerdown`イベントが発生したときに呼び出されるイベントハンドラー。防止できます。 |
データ属性 | 値 |
---|---|
[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 | トリガーの高さ |
矢印
Popoverと共にレンダリングされるオプションの矢印要素。アンカーとPopoverContent
を視覚的に関連付けるために使用できます。PopoverContent
内側にレンダリングする必要があります。
プロパティ | デフォルト値 | 型 |
---|---|---|
as | SVG | AsTag | Component このコンポーネントがレンダリングされる要素またはコンポーネント。`asChild`で上書きできます。 |
asChild | boolean 子として渡された要素に対してデフォルトのレンダリング要素を変更し、それらのプロパティと動作をマージします。 詳細は、コンポジションガイドをご覧ください。 | |
高さ | 5 | number 矢印の高さをピクセル単位で指定します。 |
幅 | 10 | number 矢印の幅をピクセル単位で指定します。 |
閉じる
開いているPopoverを閉じるボタン。
プロパティ | デフォルト値 | 型 |
---|---|---|
as | 'button' | AsTag | Component このコンポーネントがレンダリングされる要素またはコンポーネント。`asChild`で上書きできます。 |
asChild | boolean 子として渡された要素に対してデフォルトのレンダリング要素を変更し、それらのプロパティと動作をマージします。 詳細は、コンポジションガイドをご覧ください。 |
例
コンテンツサイズを制限する
トリガーの幅に合わせてコンテンツの幅を制限したり、ビューポートを超えないように高さを制限したりする場合があります。
これをサポートするために、--radix-popover-trigger-width
や--radix-popover-content-available-height
などのCSSカスタムプロパティを公開しています。これらを使用してコンテンツのサイズを制限してください。
// 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
属性を公開しています。実行時に衝突を反映するように値が変更されます。これらを使用して、衝突と方向を認識したアニメーションを作成してください。
// 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;
}
アクセシビリティ
ダイアログWAI-ARIAデザインパターンに準拠しています。
キーボード操作
キー | 説明 |
---|---|
スペース | Popoverを開閉します。 |
Enter | Popoverを開閉します。 |
Tab | フォーカスを次のフォーカス可能な要素に移動します。 |
Shift + Tab | フォーカスを前のフォーカス可能な要素に移動します。 |
Esc | Popoverを閉じ、フォーカスを 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>