ポップオーバー
ポップオーバーは、ナビゲーションメニュー、モバイルメニュー、フライアウトメニューなど、任意のコンテンツを含むフローティングパネルに最適です。
インストール
始めるには、npm経由でHeadless UIをインストールします
npm install @headlessui/react
基本的な例
ポップオーバーは、Popover
、PopoverButton
、および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>
)
}
top
、right
、bottom
、または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を使用してgap
、offset
、および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 MotionやReact 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>
)
}
Popover
とPopoverPanel
は、非同期アクションを実行した後などに、パネルを強制的に閉じるために使用できる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
とそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルト要素をレンダリングします。
Popover
、PopoverBackdrop
、PopoverPanel
、PopoverGroup
コンポーネントはすべて<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キーを | パネルの表示/非表示を切り替える |
Esc | 開いているすべてのポップオーバーを閉じる |
Tab | 開いているパネルの内容を順番に移動する 開いているパネルからTabキーでフォーカスを外すと、そのパネルが閉じます。また、開いているパネルから( |
Shift + Tab | フォーカス順を逆方向に移動する |
プロパティ | デフォルト | 説明 |
as | div | String | Component ポップオーバーがレンダリングされる要素またはコンポーネント。 |
データ属性 | レンダープロップ | 説明 |
data-open | open |
ポップオーバーが開いているかどうか。要素またはコンポーネント。 |
— | close |
ポップオーバーを閉じて、 |
プロパティ | デフォルト | 説明 |
as | div | String | Component ポップオーバーがレンダリングされるポップオーバーの背景 |
transition | false | Boolean 要素が |
データ属性 | レンダープロップ | 説明 |
data-open | open |
ポップオーバーが開いているかどうか。要素またはコンポーネント。 |
プロパティ | デフォルト | 説明 |
as | button | String | Component ポップオーバーがレンダリングされるポップオーバーボタン |
disabled | false | Boolean ポップオーバーが開いているかどうか。ポップオーバーボタン無効になっている. |
autoFocus | false | Boolean ポップオーバーが開いているかどうか。ポップオーバーボタン最初にレンダリングされたときにフォーカスを受け取る必要がある。 |
データ属性 | レンダープロップ | 説明 |
data-open | open |
ポップオーバーが開いているかどうか。要素またはコンポーネント。 |
data-focus | focus |
ポップオーバーが開いているかどうか。ポップオーバーボタンフォーカスされている。 |
data-hover | hover |
ポップオーバーが開いているかどうか。ポップオーバーボタンホバーされている。 |
data-active | active |
ポップオーバーが開いているかどうか。ポップオーバーボタンアクティブまたは押下された状態にある。 |
data-autofocus | autofocus |
|
プロパティ | デフォルト | 説明 |
as | div | String | Component ポップオーバーがレンダリングされるポップオーバーパネル |
transition | false | Boolean 要素が |
anchor | — | Object パネルがボタンにアンカーされる方法を設定します。 |
anchor.to | bottom | String パネルをトリガーに対してどこに配置するか。ポップオーバーパネル
|
anchor.gap | 0 | Number | String トリガーとパネルの間のスペース。ポップオーバーボタンポップオーバーパネル.
|
anchor.offset | 0 | Number | String パネルを元の位置からどれだけずらすか。ポップオーバーパネル
|
anchor.padding | 0 | Number | String パネルとビューポート間の最小スペース。ポップオーバーパネル
|
static | false | Boolean 要素が内部で管理される開閉状態を無視するかどうか。 |
unmount | true | Boolean 開閉状態に基づいて、要素をアンマウントするか非表示にするか。 |
portal | false | Boolean 要素をポータルにレンダリングするかどうか。
|
modal | false | Boolean スクロールロックやフォーカストラップなどのアクセシビリティ機能を有効にするかどうか。 |
focus | false | Boolean これにより、ポップオーバーが開いているときに |
データ属性 | レンダープロップ | 説明 |
data-open | open |
ポップオーバーが開いているかどうか。要素またはコンポーネント。 |
— | close |
ポップオーバーを閉じて、 |
関連する兄弟ポップオーバーをPopoverGroup
でラップしてリンクします。1つのPopoverPanel
からTabキーでフォーカスを外すと、次のポップオーバーのPopoverButton
にフォーカスが移動し、PopoverGroup
の外側に完全にTabキーで移動すると、グループ内のすべてのポップオーバーが閉じます。
プロパティ | デフォルト | 説明 |
as | div | String | Component ポップオーバーがレンダリングされるポップオーバーグループ |
プロパティ | デフォルト | 説明 |
as | button | String | Component ポップオーバーがレンダリングされる閉じるボタン |
Headless UIを使用した、あらかじめデザインされたTailwind CSSポップオーバーコンポーネントの例にご興味がある場合は、Tailwind UIをご覧ください。これは、私たちが作成した美しくデザインされ、巧みに作られたコンポーネントのコレクションです。
これは、このようなオープンソースプロジェクトに対する私たちの活動をサポートするための素晴らしい方法であり、私たちがそれらを改善し、適切に維持することを可能にします。