Disclosure
トグル可能なアコーディオンパネルのように、コンテンツを表示および非表示にするカスタムUIを構築するためのシンプルでアクセス可能な基盤。
まず、npm経由でHeadless UIをインストールします。
npm install @headlessui/react
ディスクロージャーは、Disclosure
、Disclosure.Button
、Disclosure.Panel
コンポーネントを使用して構築されます。
ボタンをクリックするとパネルが自動的に開閉し、すべてのコンポーネントはaria-expanded
やaria-controls
などの適切なaria-*関連属性を受け取ります。
import { Disclosure } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button className="py-2"> Is team pricing available? </Disclosure.Button> <Disclosure.Panel className="text-gray-500"> Yes! You can purchase a license that you can share with your entire team. </Disclosure.Panel> </Disclosure> ) }
Headless UIは、どのリストボックスオプションが現在選択されているか、ポップオーバーが開いているか閉じているか、キーボードを使用してディスクロージャーのどの項目が現在アクティブであるかなど、各コンポーネントに関する多くの状態を追跡します。
ただし、コンポーネントはヘッドレスであり、初期状態では完全にスタイルが設定されていないため、各状態に必要なスタイルを自分で提供するまで、UIでこの情報を確認することはできません。
各コンポーネントは、レンダープロップスを介して現在の状態に関する情報を公開します。これを使用して、異なるスタイルを条件付きで適用したり、異なるコンテンツをレンダリングしたりできます。
たとえば、Disclosure
コンポーネントはopen
状態を公開します。これにより、ディスクロージャーが現在開いているかどうかがわかります。
import { Disclosure } from '@headlessui/react' import { ChevronRightIcon } from '@heroicons/react/20/solid' function MyDisclosure() { return ( <Disclosure>
{({ open }) => (/* Use the `open` state to conditionally change the direction of an icon. */ <> <Disclosure.Button> Do you offer technical support?<ChevronRightIcon className={open ? 'rotate-90 transform' : ''} /></Disclosure.Button> <Disclosure.Panel>No</Disclosure.Panel> </> )} </Disclosure> ) }
各コンポーネントの完全なレンダープロップAPIについては、コンポーネントAPIドキュメントを参照してください。
各コンポーネントは、data-headlessui-state
属性を介して現在の状態に関する情報も公開します。これを使用して、異なるスタイルを条件付きで適用できます。
レンダープロップAPIの状態がtrue
の場合、それらはスペースで区切られた文字列としてこの属性にリストされます。そのため、[attr~=value]
形式のCSS属性セレクターを使用してそれらをターゲットにできます。
たとえば、ディスクロージャーが開いているときにDisclosure
コンポーネントがレンダリングする内容は次のとおりです
<!-- Rendered `Disclosure` --> <div data-headlessui-state="open"> <button data-headlessui-state="open">Do you offer technical support?</button> <div data-headlessui-state="open">No</div> </div>
Tailwind CSSを使用している場合は、@headlessui/tailwindcssプラグインを使用して、ui-open:*
などの修飾子でこの属性をターゲットにできます。
import { Disclosure } from '@headlessui/react' import { ChevronRightIcon } from '@heroicons/react/20/solid' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button> Do you offer technical support?
<ChevronRightIcon className="ui-open:rotate-90 ui-open:transform" /></Disclosure.Button> <Disclosure.Panel>No</Disclosure.Panel> </Disclosure> ) }
パネルの子をクリックしたときにディスクロージャーを手動で閉じるには、その子をDisclosure.Button
としてレンダリングします。as
プロップを使用して、レンダリングされる要素をカスタマイズできます。
import { Disclosure } from '@headlessui/react' import MyLink from './MyLink' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Open mobile menu</Disclosure.Button> <Disclosure.Panel>
<Disclosure.Button as={MyLink} href="/home">Home</Disclosure.Button>{/* ... */} </Disclosure.Panel> </Disclosure> ) }
これは、モバイルメニューのようにリンクが含まれており、次のページに移動するときにディスクロージャーを閉じたい場合に特に役立ちます。
または、Disclosure
およびDisclosure.Panel
は、非同期アクションを実行した後などで、パネルを命令的に閉じるために使用できるclose()
レンダープロップを公開します
import { Disclosure } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Terms</Disclosure.Button> <Disclosure.Panel>
{({ close }) => (<button onClick={async () => {await fetch('/accept-terms', { method: 'POST' })close()}} > Read and accept </button> )} </Disclosure.Panel> </Disclosure> ) }
デフォルトでは、close()
を呼び出した後、Disclosure.Button
がフォーカスを受け取りますが、close(ref)
にrefを渡すことでこれを変更できます。
メニューパネルの開閉をアニメーション化するには、提供されているTransition
コンポーネントを使用します。必要なのは、Disclosure.Panel
を<Transition>
でラップするだけで、トランジションが自動的に適用されます。
import { Disclosure, Transition } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Is team pricing available?</Disclosure.Button>
<Transitionenter="transition duration-100 ease-out"enterFrom="transform scale-95 opacity-0"enterTo="transform scale-100 opacity-100"leave="transition duration-75 ease-out"leaveFrom="transform scale-100 opacity-100"leaveTo="transform scale-95 opacity-0"><Disclosure.Panel>Yes! You can purchase a license that you can share with your entireteam. </Disclosure.Panel> </Transition> </Disclosure> ) }
デフォルトでは、組み込みのTransition
コンポーネントは、Disclosure
コンポーネントと自動的に通信して開閉状態を処理します。ただし、この動作をより詳細に制御する必要がある場合は、明示的に制御できます。
import { Disclosure, Transition } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure>
{({ open }) => (<><Disclosure.Button>Is team pricing available?</Disclosure.Button> {/* Use the `Transition` + `open` render prop argument to add transitions. */}<Transitionshow={open}enter="transition duration-100 ease-out"enterFrom="transform scale-95 opacity-0"enterTo="transform scale-100 opacity-100"leave="transition duration-75 ease-out"leaveFrom="transform scale-100 opacity-100"leaveTo="transform scale-95 opacity-0">{/* Don't forget to add `static` to your `Disclosure.Panel`! */}<Disclosure.Panel static>Yes! You can purchase a license that you can share with yourentire team.</Disclosure.Panel></Transition> </> )} </Disclosure> ) }
レンダーレスであるため、Headless UIコンポーネントは、Framer MotionやReact SpringなどのReactエコシステム内の他のアニメーションライブラリともうまく構成されます。
Disclosure
とそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルト要素をレンダリングします。Button
は<button>
をレンダリングし、Panel
は<div>
をレンダリングします。対照的に、ルートのDisclosure
コンポーネントは、要素をレンダリングしません。代わりに、デフォルトでその子を直接レンダリングします。
コンポーネントを異なる要素としてレンダリングするか、独自のカスタムコンポーネントとしてレンダリングするには、as
プロップを使用します。カスタムコンポーネントがHeadless UIが正しく接続できるようにforward refsであることを確認してください。
import { forwardRef, Fragment } from 'react' import { Disclosure } from '@headlessui/react'
let MyCustomButton = forwardRef(function (props, ref) {return <button className="..." ref={ref} {...props} />})function MyDisclosure() { return (<Disclosure as="div"><Disclosure.Button as={MyCustomButton}>What languages do you support? </Disclosure.Button><Disclosure.Panel as="ul"><li>HTML</li> <li>CSS</li> <li>JavaScript</li> </Disclosure.Panel> </Disclosure> ) }
Disclosure.Button
をクリックすると、Disclosureのパネルが開閉します。
コマンド | 説明 |
Enter または Space | パネルのトグル |
プロップ | デフォルト | 説明 |
as | Fragment | String | Component
|
defaultOpen | false | Boolean
|
レンダープロップ | 説明 |
open |
ディスクロージャーが開いているかどうか。 |
close |
ディスクロージャーを閉じ、 |
プロップ | デフォルト | 説明 |
as | button | String | Component
|
レンダープロップ | 説明 |
open |
ディスクロージャーが開いているかどうか。 |
プロップ | デフォルト | 説明 |
as | div | String | Component
|
static | false | Boolean 要素が内部で管理される開閉状態を無視するかどうか。 注: |
unmount | true | Boolean 開閉状態に基づいて要素をアンマウントするか非表示にするかどうか。 注: |
レンダープロップ | 説明 |
open |
ディスクロージャーが開いているかどうか。 |
close |
ディスクロージャーを閉じ、 |