ポップオーバー

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

インストール

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

npm install @headlessui/react

基本的な例

ポップオーバーは、PopoverPopoverButton、およびPopoverPanelコンポーネントを使用して構築されます。

PopoverButtonをクリックすると、PopoverPanelが自動的に開閉します。パネルが開いているときに、コンテンツの外側をクリックするか、Escキーを押すか、タブでフォーカスを外すと、ポップオーバーが閉じます。

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
      <PopoverPanel anchor="bottom" className="flex flex-col">
        <a href="/analytics">Analytics</a>
        <a href="/engagement">Engagement</a>
        <a href="/security">Security</a>
        <a href="/integrations">Integrations</a>
      </PopoverPanel>
    </Popover>
  )
}

スタイリング

Headless UIは、現在選択されているリストボックスオプション、ポップオーバーが開いているか閉じているか、キーボードで現在フォーカスされているポップオーバー内の項目など、各コンポーネントに関する多くの状態を追跡します。

しかし、コンポーネントはヘッドレスで、初期状態では完全にスタイルが適用されていないため、各状態に必要なスタイルを自分で提供するまで、UIにこの情報は表示されません。

データ属性の使用

Headless UIコンポーネントのさまざまな状態をスタイル設定する最も簡単な方法は、各コンポーネントが公開するdata-*属性を使用することです。

たとえば、Popoverコンポーネントは、ポップオーバーが現在開いているかどうかを示すdata-open属性を公開します。

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

CSS属性セレクターを使用して、これらのデータ属性の存在に基づいて条件付きでスタイルを適用します。Tailwind CSSを使用している場合は、データ属性修飾子を使用すると、これが簡単になります

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'

function Example() {
  return (
<Popover className="group">
<PopoverButton className="flex items-center gap-2"> Solutions
<ChevronDownIcon className="size-5 group-data-[open]:rotate-180" />
</PopoverButton> <PopoverPanel anchor="bottom" className="flex flex-col"> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </Popover> ) }

使用可能なすべてのデータ属性のリストについては、コンポーネントAPIを参照してください。

レンダープロップスの使用

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

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

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import clsx from 'clsx'

function Example() {
  return (
    <Popover>
{({ open }) => (
<> <PopoverButton className="flex items-center gap-2"> Solutions
<ChevronDownIcon className={clsx('size-5', open && 'rotate-180')} />
</PopoverButton> <PopoverPanel anchor="bottom" className="flex flex-col"> <a href="/insights">Insights</a> <a href="/automations">Automations</a> <a href="/reports">Reports</a> </PopoverPanel> </>
)}
</Popover> ) }

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

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

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
<PopoverGroup>
<Popover> <PopoverButton>Product</PopoverButton> <PopoverPanel>{/* ... */}</PopoverPanel> </Popover> <Popover> <PopoverButton>Solutions</PopoverButton> <PopoverPanel>{/* ... */}</PopoverPanel> </Popover> <Popover> <PopoverButton>Pricing</PopoverButton> <PopoverPanel>{/* ... */}</PopoverPanel> </Popover>
</PopoverGroup>
) }

パネル幅の設定

PopoverPanelにはデフォルトで幅が設定されていませんが、CSSを使用して追加できます

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverPanel anchor="bottom" className="w-52">
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

パネルの幅をPopoverButtonの幅と一致させたい場合は、PopoverPanel要素で公開されている--button-width CSS変数を使用します

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverPanel anchor="bottom" className="flex w-[var(--button-width)] flex-col">
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

パネルの配置

anchorプロップをPopoverPanelに追加して、パネルをPopoverButtonに対して自動的に配置します

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverPanel anchor="bottom start" className="flex flex-col">
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

toprightbottom、またはleftの値を使用して、パネルを適切なエッジに沿って中央揃えするか、startまたはendと組み合わせて、top startまたはbottom endなど、パネルを特定の隅に揃えます。

ボタンとパネルの間のギャップを制御するには、--anchor-gap CSS変数を使用します

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverPanel anchor="bottom start" className="flex flex-col [--anchor-gap:4px] sm:[--anchor-gap:8px]">
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

さらに、--anchor-offsetを使用して、パネルを元の位置から移動する距離を制御し、--anchor-paddingを使用して、パネルとビューポートの間に存在する必要がある最小スペースを制御できます。

anchorプロップは、JavaScriptを使用してgapoffset、およびpaddingの値を制御できるオブジェクトAPIもサポートしています

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverPanel anchor={{ to: 'bottom start', gap: '4px' }} className="flex flex-col">
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

これらのオプションの詳細については、PopoverPanel APIを参照してください。

バックドロップの追加

ポップオーバーを開くたびにアプリケーションUIにバックドロップをスタイル設定する場合は、PopoverBackdropコンポーネントを使用します

import { Popover, PopoverButton, PopoverBackdrop, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
<PopoverBackdrop className="fixed inset-0 bg-black/15" />
<PopoverPanel anchor="bottom" className="flex flex-col bg-white"> <a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

この例では、パネルの内容を覆い隠さないように、DOMでPanelの前にPopoverBackdropを配置します。

しかし、他のすべてのコンポーネントと同様に、PopoverBackdropは完全にヘッドレスであるため、どのようにスタイルを設定するかはあなた次第です。

トランジションの追加

ポップオーバーパネルの開閉をアニメーション化するには、transitionプロップをPopoverPanelコンポーネントに追加し、CSSを使用してトランジションのさまざまな段階をスタイル設定します

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover>
      <PopoverButton>Solutions</PopoverButton>
      <PopoverPanel
        anchor="bottom"
transition
className="flex origin-top flex-col transition duration-200 ease-out data-[closed]:scale-95 data-[closed]:opacity-0"
>
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

バックドロップがある場合は、transitionプロップをPopoverBackdropに追加することにより、パネルとは独立してアニメーション化できます

import { Popover, PopoverBackdrop, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover className="relative">
      <PopoverButton>Solutions</PopoverButton>
      <PopoverBackdrop
transition
className="fixed inset-0 bg-black/15 transition duration-100 ease-out data-[closed]:opacity-0"
/>
<PopoverPanel anchor="bottom"
transition
className="flex origin-top flex-col bg-white transition duration-200 ease-out data-[closed]:scale-95 data-[closed]:opacity-0"
>
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel> </Popover> ) }

内部的には、transitionプロップはTransitionコンポーネントとまったく同じ方法で実装されています。詳細については、トランジションのドキュメントを参照してください。

Framer Motionを使用したアニメーション

Headless UIは、Framer MotionReact SpringなどのReactエコシステムの他のアニメーションライブラリとも適切に連携します。これらのライブラリに状態を公開するだけです。

たとえば、Framer Motionでポップオーバーをアニメーション化するには、staticプロップをPopoverPanelコンポーネントに追加し、openレンダープロップに基づいて条件付きでレンダリングします

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { AnimatePresence, motion } from 'framer-motion'

function Example() {
  return (
    <Popover>
{({ open }) => (
<> <PopoverButton>Solutions</PopoverButton> <AnimatePresence>
{open && (
<PopoverPanel
static
as={motion.div} initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} anchor="bottom" className="flex origin-top flex-col" >
<a href="/analytics">Analytics</a> <a href="/engagement">Engagement</a> <a href="/security">Security</a> <a href="/integrations">Integrations</a> </PopoverPanel>
)}
</AnimatePresence> </>
)}
</Popover> ) }

ポップオーバーの手動クローズ

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

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

import { CloseButton, Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import MyLink from './MyLink'

function Example() {
  return (
    <Popover>
      <PopoverButton>Solutions</PopoverButton>
      <PopoverPanel anchor="bottom">
<CloseButton as={MyLink} href="/insights">
Insights
</CloseButton>
{/* ... */} </PopoverPanel> </Popover> ) }

PopoverPopoverPanelは、非同期アクションを実行した後などに、パネルを強制的に閉じるために使用できるcloseレンダープロップも公開しています

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'

function Example() {
  return (
    <Popover>
      <PopoverButton>Terms</PopoverButton>
      <PopoverPanel>
{({ close }) => (
<button
onClick={async () => {
await fetch('/accept-terms', { method: 'POST' })
close()
}}
>
Read and accept
</button>
)}
</PopoverPanel> </Popover> ) }

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

最後に、Headless UIは、ネストされたコンポーネントなど、closeレンダープロップに簡単にアクセスできない場合に、最も近いポップオーバーの祖先を強制的に閉じるために使用できるuseCloseフックも提供します

import { Popover, PopoverButton, PopoverPanel, useClose } from '@headlessui/react'

function MySearchForm() {
let close = useClose()
return ( <form onSubmit={(event) => { event.preventDefault() /* Perform search... */
close()
}}
>
<input type="search" /> <button type="submit">Submit</button> </form> ) } function Example() { return ( <Popover> <PopoverButton>Filters</PopoverButton> <PopoverPanel> <MySearchForm /> {/* ... */} </PopoverPanel> </Popover> ) }

useCloseフックは、Popover内にネストされたコンポーネントで使用しないと機能しません。

デフォルトでは、Popoverとそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルト要素をレンダリングします。

PopoverPopoverBackdropPopoverPanelPopoverGroupコンポーネントはすべて<div>をレンダリングし、PopoverButtonコンポーネントは<button>をレンダリングします。

as propを使用して、コンポーネントを異なる要素として、または独自のカスタムコンポーネントとしてレンダリングします。カスタムコンポーネントは、Headless UIが正しく機能するようにrefsを転送する必要があります。

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { forwardRef } from 'react'

let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function Example() { return (
<Popover as="nav">
<PopoverButton as={MyCustomButton}>Solutions</PopoverButton>
<PopoverPanel as="form">{/* ... */}</PopoverPanel>
</Popover> ) }

コマンド説明

Enter または SpaceキーをPopoverButtonにフォーカスがある状態で押下。

パネルの表示/非表示を切り替える

Esc

開いているすべてのポップオーバーを閉じる

Tab

開いているパネルの内容を順番に移動する

開いているパネルからTabキーでフォーカスを外すと、そのパネルが閉じます。また、開いているパネルから(PopoverGroup内の)兄弟ポップオーバーのボタンにTabキーで移動すると、最初のパネルが閉じます。

Shift + Tab

フォーカス順を逆方向に移動する

メインのポップオーバーコンポーネント。

プロパティデフォルト説明
asdiv
String | Component

ポップオーバーがレンダリングされる要素またはコンポーネント。

データ属性レンダープロップ説明
data-openopen

Boolean

ポップオーバーが開いているかどうか。要素またはコンポーネント。

close

(ref) => void

ポップオーバーを閉じて、PopoverButtonにフォーカスを戻します。オプションで、refまたはHTMLElementを渡して、代わりにその要素にフォーカスを当てることができます。

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

プロパティデフォルト説明
asdiv
String | Component

ポップオーバーがレンダリングされるポップオーバーの背景

transitionfalse
Boolean

要素がdata-closeddata-enterdata-leaveなどの遷移属性をレンダリングするかどうか。

データ属性レンダープロップ説明
data-openopen

Boolean

ポップオーバーが開いているかどうか。要素またはコンポーネント。

これは、ポップオーバーを切り替えるトリガーコンポーネントです。

プロパティデフォルト説明
asbutton
String | Component

ポップオーバーがレンダリングされるポップオーバーボタン

disabledfalse
Boolean

ポップオーバーが開いているかどうか。ポップオーバーボタン無効になっている.

autoFocusfalse
Boolean

ポップオーバーが開いているかどうか。ポップオーバーボタン最初にレンダリングされたときにフォーカスを受け取る必要がある。

データ属性レンダープロップ説明
data-openopen

Boolean

ポップオーバーが開いているかどうか。要素またはコンポーネント。

data-focusfocus

Boolean

ポップオーバーが開いているかどうか。ポップオーバーボタンフォーカスされている。

data-hoverhover

Boolean

ポップオーバーが開いているかどうか。ポップオーバーボタンホバーされている。

data-activeactive

Boolean

ポップオーバーが開いているかどうか。ポップオーバーボタンアクティブまたは押下された状態にある。

data-autofocusautofocus

Boolean

autoFocusプロパティがtrueに設定されているかどうか。

このコンポーネントには、ポップオーバーの内容が含まれています。

プロパティデフォルト説明
asdiv
String | Component

ポップオーバーがレンダリングされるポップオーバーパネル

transitionfalse
Boolean

要素がdata-closeddata-enterdata-leaveなどの遷移属性をレンダリングするかどうか。

anchor
Object

パネルがボタンにアンカーされる方法を設定します。

anchor.tobottom
String

パネルをトリガーに対してどこに配置するか。ポップオーバーパネル

toprightbottomleftの値を使用して、パネルを適切なエッジに沿って中央に配置するか、startまたはendと組み合わせて、top startまたはbottom endのようにパネルを特定の隅に配置します。 ポップオーバーパネルポップオーバーパネル

anchor.gap0
Number | String

トリガーとパネルの間のスペース。ポップオーバーボタンポップオーバーパネル.

--anchor-gap CSS変数を使用して制御することもできます。

anchor.offset0
Number | String

パネルを元の位置からどれだけずらすか。ポップオーバーパネル

--anchor-offset CSS変数を使用して制御することもできます。

anchor.padding0
Number | String

パネルとビューポート間の最小スペース。ポップオーバーパネル

--anchor-padding CSS変数を使用して制御することもできます。

staticfalse
Boolean

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

unmounttrue
Boolean

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

portalfalse
Boolean

要素をポータルにレンダリングするかどうか。

anchorプロパティが設定されている場合、自動的にtrueに設定されます。

modalfalse
Boolean

スクロールロックやフォーカストラップなどのアクセシビリティ機能を有効にするかどうか。

focusfalse
Boolean

これにより、ポップオーバーが開いているときにPopoverPanel内にフォーカスが強制されます。また、フォーカスがこのコンポーネントから外れると、ポップオーバーが閉じます。

データ属性レンダープロップ説明
data-openopen

Boolean

ポップオーバーが開いているかどうか。要素またはコンポーネント。

close

(ref) => void

ポップオーバーを閉じて、PopoverButtonにフォーカスを戻します。オプションで、refまたはHTMLElementを渡して、代わりにその要素にフォーカスを当てることができます。

関連する兄弟ポップオーバーをPopoverGroupでラップしてリンクします。1つのPopoverPanelからTabキーでフォーカスを外すと、次のポップオーバーのPopoverButtonにフォーカスが移動し、PopoverGroupの外側に完全にTabキーで移動すると、グループ内のすべてのポップオーバーが閉じます。

プロパティデフォルト説明
asdiv
String | Component

ポップオーバーがレンダリングされるポップオーバーグループ

このボタンをクリックすると、最も近いPopoverPanel祖先が閉じます。または、useCloseフックを使用して、ポップオーバーパネルを強制的に閉じます。

プロパティデフォルト説明
asbutton
String | Component

ポップオーバーがレンダリングされる閉じるボタン

Headless UIを使用した、あらかじめデザインされたTailwind CSSポップオーバーコンポーネントの例にご興味がある場合は、Tailwind UIをご覧ください。これは、私たちが作成した美しくデザインされ、巧みに作られたコンポーネントのコレクションです。

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