Disclosure

トグル可能なアコーディオンパネルのように、コンテンツを表示および非表示にするカスタムUIを構築するためのシンプルでアクセス可能な基盤。

まず、npm経由でHeadless UIをインストールします。

npm install @headlessui/react

ディスクロージャーは、DisclosureDisclosure.ButtonDisclosure.Panelコンポーネントを使用して構築されます。

ボタンをクリックするとパネルが自動的に開閉し、すべてのコンポーネントはaria-expandedaria-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>
<Transition
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"
>
<Disclosure.Panel>
Yes! You can purchase a license that you can share with your entire
team. </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. */}
<Transition
show={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 your
entire team.
</Disclosure.Panel>
</Transition> </> )} </Disclosure> ) }

レンダーレスであるため、Headless UIコンポーネントは、Framer MotionReact 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 Disclosure.Buttonにフォーカスがある場合。

パネルのトグル

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

メインのDisclosureコンポーネント。

プロップデフォルト説明
asFragment
String | Component

Disclosureがレンダリングする要素またはコンポーネント。

defaultOpenfalse
Boolean

Disclosureコンポーネントをデフォルトで開くかどうか。

レンダープロップ説明
open

Boolean

ディスクロージャーが開いているかどうか。

close

(ref?: ref | HTMLElement) => void

ディスクロージャーを閉じ、Disclosure.Buttonにフォーカスを戻します。オプションで、代わりにその要素にフォーカスするためのrefまたはHTMLElementを渡します。

ディスクロージャーを切り替えるトリガーコンポーネント。

プロップデフォルト説明
asbutton
String | Component

Disclosure.Buttonがレンダリングする要素またはコンポーネント。

レンダープロップ説明
open

Boolean

ディスクロージャーが開いているかどうか。

このコンポーネントには、ディスクロージャーの内容が含まれています。

プロップデフォルト説明
asdiv
String | Component

Disclosure.Panelがレンダリングする要素またはコンポーネント。

staticfalse
Boolean

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

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

unmounttrue
Boolean

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

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

レンダープロップ説明
open

Boolean

ディスクロージャーが開いているかどうか。

close

(ref?: ref | HTMLElement) => void

ディスクロージャーを閉じ、Disclosure.Buttonにフォーカスを戻します。オプションで、代わりにその要素にフォーカスするためのrefまたはHTMLElementを渡します。

Headless UIとTailwind CSSを使用した事前設計されたコンポーネントの例に興味がある場合は、私たちによって構築された美しくデザインされ、専門的に作成されたコンポーネントのコレクションであるTailwind UIをチェックしてください。

これは、このようなオープンソースプロジェクトでの私たちの取り組みをサポートする素晴らしい方法であり、それらを改善し、適切に維持できるようにします。