コンテンツへスキップ

Popover

ボタンによってトリガーされるポータルにリッチコンテンツを表示します。
vue
<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>

機能

  • 制御可能または非制御可能。
  • サイド、アライメント、オフセット、衝突処理をカスタマイズできます。
  • オプションで指し示す矢印を表示できます。
  • フォーカスは完全に管理され、カスタマイズ可能です。
  • モーダルモードと非モーダルモードをサポートしています。
  • 閉じ方とレイヤリングの動作は高度にカスタマイズ可能です。

インストール

コマンドラインからコンポーネントをインストールします。

sh
$ npm add radix-vue

構成

すべての部品をインポートして組み合わせます。

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カスタムプロパティを公開しています。これらを使用してコンテンツのサイズを制限してください。

vue
// 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>
CSS
/* styles.css */
.PopoverContent {
  width: var(--radix-popover-trigger-width);
  max-height: var(--radix-popover-content-available-height);
}

起点認識アニメーション

CSSカスタムプロパティ--radix-popover-content-transform-originを公開しています。sidesideOffsetalignalignOffset、および衝突に基づいて、計算された起点からコンテンツをアニメーション化するために使用してください。

vue
// 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>
CSS
/* 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属性を公開しています。実行時に衝突を反映するように値が変更されます。これらを使用して、衝突と方向を認識したアニメーションを作成してください。

vue
// 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>
CSS
/* 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);
  }
}

カスタムアンカーを使用する

トリガーをアンカーとして使用しない場合は、コンテンツを別の要素にアンカーできます。

vue
// 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>
CSS
/* styles.css */
.Row {
  background-color: gainsboro;
  padding: 20px;
}

アクセシビリティ

ダイアログWAI-ARIAデザインパターンに準拠しています。

キーボード操作

キー説明
スペース
Popoverを開閉します。
Enter
Popoverを開閉します。
Tab
フォーカスを次のフォーカス可能な要素に移動します。
Shift + Tab
フォーカスを前のフォーカス可能な要素に移動します。
Esc
Popoverを閉じ、フォーカスをPopoverTriggerに移動します。

カスタムAPI

基本的な部分を独自のコンポーネントに抽象化することで、独自のAPIを作成できます。

矢印を抽象化し、デフォルトの設定を行う

この例では、PopoverArrow部分を抽象化し、デフォルトのsideOffset設定を設定しています。

使用方法

vue
<script setup lang="ts">
import { Popover, PopoverContent, PopoverTrigger } from './your-popover'
</script>

<template>
  <Popover>
    <PopoverTrigger>Popover trigger</PopoverTrigger>
    <PopoverContent>Popover content</PopoverContent>
  </Popover>
</template>

実装

TypeScript
// your-popover.ts
export { default as PopoverContent } from 'PopoverContent.vue'

export { PopoverRoot as Popover, PopoverTrigger } from 'radix-vue'
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>