Popover

Popoverは、ナビゲーションメニュー、モバイルメニュー、フライアウトメニューなど、任意のコンテンツを含むフローティングパネルに最適です。

開始するには、npm経由でHeadless UIをインストールします。

このライブラリはVue 3のみサポートしていますのでご注意ください。

npm install @headlessui/vue

Popoverは、PopoverPopoverButtonPopoverPanelコンポーネントを使用して構築されています。

PopoverButtonをクリックすると、PopoverPanelが自動的に開閉します。パネルが開いている場合、そのコンテンツの外側をクリックするか、Escapeキーを押すか、タブキーでパネルから離れると、Popoverが閉じます。

<template> <Popover class="relative"> <PopoverButton>Solutions</PopoverButton> <PopoverPanel class="absolute z-10"> <div class="grid grid-cols-2"> <a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </div> <img src="/solutions.jpg" alt="" /> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>

これらのコンポーネントは完全にスタイルが設定されていないため、Popoverのスタイル設定方法はユーザー次第です。この例では、PopoverPanelに絶対位置付けを使用し、PopoverButtonの近くに配置し、通常のドキュメントの流れを妨げないようにしています。

Headless UIは、現在選択されているリストボックスのオプション、Popoverが開いているか閉じているか、キーボードで現在アクティブになっているPopover内のアイテムなど、各コンポーネントに関する多くの状態を追跡します。

しかし、コンポーネントはヘッドレスであり、すぐに使える状態で完全にスタイルが設定されていないため、各状態に必要なスタイルを自分で提供するまで、UIでこの情報は見えません

各コンポーネントは、スロットプロップを介して現在の状態に関する情報を公開し、これを使用して条件付きで異なるスタイルを適用したり、異なるコンテンツをレンダリングしたりできます。

たとえば、Popoverコンポーネントはopen状態を公開し、Popoverが現在開いているかどうかを示します。

<template>
<Popover v-slot="{ open }">
<!-- Use the `open` state to conditionally change the direction of the chevron icon. --> <PopoverButton> Solutions
<ChevronDownIcon :class="{ 'rotate-180 transform': open }" />
</PopoverButton> <PopoverPanel> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import { ChevronDownIcon } from '@heroicons/vue/20/solid' </script>

使用可能なすべてのスロットプロップの完全なリストについては、コンポーネントAPIドキュメントを参照してください。

各コンポーネントは、条件付きで異なるスタイルを適用するために使用できるdata-headlessui-state属性を介して現在の状態に関する情報を公開します。

スロットプロップAPIのいずれかの状態がtrueの場合、それらはスペースで区切られた文字列としてこの属性にリストされます。これにより、[attr~=value]形式のCSS属性セレクターでターゲットにできます。

たとえば、Popoverが開いている場合、Popoverコンポーネントは次のようにレンダリングされます。

<!-- Rendered `Popover` --> <div data-headlessui-state="open"> <button data-headlessui-state="open">Solutions</button> <div data-headlessui-state="open"> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </div> </div>

Tailwind CSSを使用している場合、@headlessui/tailwindcssプラグインを使用して、ui-open:*のような修飾子でこの属性をターゲットにできます。

<template> <Popover> <PopoverButton> Solutions
<ChevronDownIcon class="ui-open:rotate-180 ui-open:transform" />
</PopoverButton> <PopoverPanel> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import { ChevronDownIcon } from '@heroicons/vue/20/solid' </script>

デフォルトでは、PopoverPanelは、Popoverコンポーネント自体内で追跡される内部のopen状態に基づいて自動的に表示/非表示されます。

<template> <Popover> <PopoverButton>Solutions</PopoverButton> <!-- By default, the `PopoverPanel` will automatically show/hide when the `PopoverButton` is pressed. --> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>

自分で処理したい場合(たとえば、何らかの理由で追加のラッパー要素を追加する必要がある場合)、PopoverPanelstaticプロップを渡して常にレンダリングするように指示し、openスロットプロップを使用してパネルの表示/非表示を自分で制御できます。

<template>
<Popover v-slot="{ open }">
<PopoverButton>Solutions</PopoverButton> <div v-if="open">
<!--
Using the `static` prop, the `PopoverPanel` is always
rendered and the `open` state is ignored.
--> <PopoverPanel static> <!-- ... --> </PopoverPanel> </div> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>

Popoverにはフォームコントロールのようなインタラクティブなコンテンツが含まれる可能性があるため、Menuコンポーネントのように、内部の何かをクリックしたときに自動的に閉じることはできません。

パネルの子をクリックしたときにPopoverを手動で閉じたい場合は、その子をPopoverButtonとしてレンダリングします。:asプロップを使用して、レンダリングされる要素をカスタマイズできます。

<template> <Popover> <PopoverButton>Solutions</PopoverButton> <PopoverPanel>
<PopoverButton :as="MyLink" href="/insights">Insights</PopoverButton>
<!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' import MyLink from './MyLink' </script>

あるいは、非同期アクションを実行した後に命令的にパネルを閉じたい場合は、PopoverPopoverPanelclose()スロットプロップを公開します。

<template> <Popover> <PopoverButton>Solutions</PopoverButton>
<PopoverPanel v-slot="{ close }">
<button @click="accept(close)">Read and accept</button>
</PopoverPanel>
</Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
async function accept(close) {
await fetch('/accept-terms', { method: 'POST' })
close()
}
</script>

デフォルトでは、close()を呼び出した後、PopoverButtonがフォーカスを受け取りますが、refをclose(ref)に渡すことで変更できます。

Popoverを開くたびにアプリケーションUIの上に背景をスタイル設定したい場合は、PopoverOverlayコンポーネントを使用します。

<template> <Popover v-slot="{ open }"> <PopoverButton>Solutions</PopoverButton>
<PopoverOverlay class="fixed inset-0 bg-black opacity-30" />
<PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> </template> <script setup> import { Popover, PopoverOverlay, PopoverButton, PopoverPanel, } from '@headlessui/vue' </script>

この例では、パネルのコンテンツを覆わないように、DOM内でPopoverOverlayPanelの前に配置しています。

しかし、他のすべてのコンポーネントと同様に、PopoverOverlayは完全にヘッドレスなので、スタイル設定方法はユーザー次第です。

Popoverのパネルの開閉をアニメーション化するには、Vueの組み込み<transition>要素を使用できます。<transition>PopoverPanelをラップするだけで、トランジションが自動的に適用されます。

<template> <Popover> <PopoverButton>Solutions</PopoverButton> <!-- Use the built-in `transition` component to add transitions. -->
<transition
enter-active-class="transition duration-200 ease-out"
enter-from-class="translate-y-1 opacity-0"
enter-to-class="translate-y-0 opacity-100"
leave-active-class="transition duration-150 ease-in"
leave-from-class="translate-y-0 opacity-100"
leave-to-class="translate-y-1 opacity-0"
>
<PopoverPanel> <!-- ... --> </PopoverPanel> </transition> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>

Popoverの異なる子要素に対して複数のトランジションを調整したい場合は、Headless UIに含まれるTransitionコンポーネントを確認してください。

複数の関連するPopoverをレンダリングする場合(たとえば、サイトのヘッダーナビゲーションなど)、PopoverGroupコンポーネントを使用します。これにより、ユーザーがグループ内のPopover間をタブで移動している間はパネルが開いたままになり、ユーザーがグループの外側をタブで移動すると開いているパネルが閉じます。

<template>
<PopoverGroup>
<Popover> <PopoverButton>Product</PopoverButton> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover> <Popover> <PopoverButton>Solutions</PopoverButton> <PopoverPanel> <!-- ... --> </PopoverPanel> </Popover>
</PopoverGroup>
</template> <script setup> import { PopoverGroup, Popover, PopoverButton, PopoverPanel, } from '@headlessui/vue' </script>

Popoverとそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルトの要素をレンダリングします。PopoverOverlayPanelGroupコンポーネントはすべて<div>をレンダリングし、Buttonコンポーネントは<button>をレンダリングします。

これは、すべてのコンポーネントに存在するasプロップを使用して簡単に変更できます。

<template> <!-- Render a `nav` instead of a `div` -->
<Popover as="nav">
<PopoverButton>Solutions</PopoverButton> <!-- Render a `form` instead of a `div` -->
<PopoverPanel as="form"><!-- ... --></PopoverPanel>
</Popover> </template> <script setup> import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' </script>

開いているパネルでTabキーを押すと、パネルのコンテンツ内の最初のフォーカス可能な要素にフォーカスが移動します。PopoverGroupを使用している場合、Tabキーは開いているパネルのコンテンツの最後から次のPopoverのボタンに移動します。

PopoverButtonをクリックすると、パネルが開閉します。開いているパネルの外側をクリックすると、そのパネルが閉じます。

コマンド説明

EnterまたはSpacePopoverButtonにフォーカスした状態で押します。

パネルの切り替え

Esc

開いているPopoverをすべて閉じます

Tab

開いているパネルのコンテンツ間を移動します

開いているパネルからタブキーで離れると、そのパネルが閉じます。また、開いているパネルから兄弟のPopoverのボタン(PopoverGroup内)にタブキーで移動すると、最初のパネルが閉じます。

Shift + Tab

フォーカス順序を逆順に移動します

ネストされたPopoverがサポートされており、ルートパネルが閉じると、すべてのパネルが正しく閉じます。

関連するすべてのARIA属性が自動的に管理されます。

Popoverと他の同様のコンポーネントの比較について説明します。

  • <Menu />。Popover は Menu よりも汎用的です。Menu は非常に限定的なコンテンツしかサポートせず、特定のアクセシビリティセマンティクスを持っています。矢印キーでも Menu のアイテムを移動できます。Menu は、ほとんどのオペレーティングシステムのタイトルバーにあるようなメニューに似たUI要素に最適です。浮動パネルに画像や単純なリンク以上のマークアップが含まれる場合は、Popover を使用してください。

  • <Disclosure />。Disclosure は、アコーディオンのように通常はドキュメントのレイアウトを変更する要素に役立ちます。Popover は Disclosure に加えて、オーバーレイを表示し、ユーザーがオーバーレイをクリック(Popover のコンテンツの外側をクリック)するか、Escキーを押すと閉じられるという追加の動作があります。UI 要素にこの動作が必要な場合は、Disclosure の代わりに Popover を使用してください。

  • <Dialog />。Dialog は、ユーザーの完全な注意を引くことを目的としています。通常、画面中央に浮動パネルを表示し、バックドロップを使用してアプリケーションの残りのコンテンツを暗くします。また、フォーカスを取得し、Dialog が閉じられるまで Dialog のコンテンツからタブ移動できなくなります。Popover はよりコンテキストに依存し、通常はトリガーした要素の近くに配置されます。

メインの Popover コンポーネントです。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

Popover がレンダリングされる要素またはコンポーネントです。

スロットプロパティ説明
open

ブール値

Popover が開いているかどうか。

close

(ref?: ref | HTMLElement) => void

Popover を閉じ、PopoverButton にフォーカスを戻します。代わりにフォーカスするrefまたはHTMLElementをオプションで渡すことができます。

これは、Popover コンポーネントのオーバーレイを作成するために使用できます。オーバーレイをクリックすると、Popover が閉じます。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

PopoverOverlay がレンダリングされる要素またはコンポーネントです。

スロットプロパティ説明
open

ブール値

Popover が開いているかどうか。

これは、Popover を切り替えるためのトリガーコンポーネントです。PopoverPanel 内でこのPopoverButtonコンポーネントを使用することもできます。その場合、閉じボタンとして動作します。また、ボタンに適切なaria-*属性が設定されるようにします。

プロパティデフォルト値説明
asbutton
文字列 | コンポーネント

PopoverButton がレンダリングされる要素またはコンポーネントです。

スロットプロパティ説明
open

ブール値

Popover が開いているかどうか。

このコンポーネントは、Popover のコンテンツを含みます。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

PopoverPanel がレンダリングされる要素またはコンポーネントです。

focusfalse
ブール値

Popover が開いているときに、PopoverPanel 内に強制的にフォーカスを当てます。また、フォーカスがこのコンポーネントから離れると、Popover を閉じます。

staticfalse
ブール値

要素が内部的に管理されている開閉状態を無視するかどうか。

注: staticunmount は同時に使用できません。両方を使用しようとすると、TypeScript エラーが発生します。

unmounttrue
ブール値

開閉状態に基づいて、要素をアンマウントするか、非表示にするかどうか。

注: staticunmount は同時に使用できません。両方を使用しようとすると、TypeScript エラーが発生します。

スロットプロパティ説明
open

ブール値

Popover が開いているかどうか。

close

(ref?: ref | HTMLElement) => void

Popover を閉じ、PopoverButton にフォーカスを戻します。代わりにフォーカスするrefまたはHTMLElementをオプションで渡すことができます。

PopoverGroup で囲むことで、関連する兄弟 Popover をリンクします。ある PopoverPanel からタブ移動すると、次の Popover の PopoverButton にフォーカスが移動し、PopoverGroup の外側にタブ移動すると、グループ内のすべての Popover が閉じます。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

PopoverGroup がレンダリングされる要素またはコンポーネントです。

Headless UI と Tailwind CSS を使用した、事前にデザインされたコンポーネント例に興味がある場合は、Tailwind UI をご覧ください。これは、私たちによって構築された、美しくデザインされ、熟練した職人技で作られたコンポーネントのコレクションです。

これは、このようなオープンソースプロジェクトへの貢献をサポートする素晴らしい方法であり、それらを改善し、適切に維持することを可能にします。