Disclosure
トグル可能なアコーディオンパネルのような、コンテンツの表示/非表示を行うカスタムUIを構築するためのシンプルでアクセス可能な基盤です。
まず、npm経由でHeadless UIをインストールします。
このライブラリはVue 3のみをサポートしていることに注意してください。
npm install @headlessui/vue
Disclosureは、Disclosure
、DisclosureButton
、DisclosurePanel
コンポーネントを使用して構築されます。
ボタンをクリックするとパネルが自動的に開閉し、すべてのコンポーネントは、aria-expanded
やaria-controls
のような適切なaria-*関連属性を受け取ります。
<template> <Disclosure> <DisclosureButton class="py-2"> Is team pricing available? </DisclosureButton> <DisclosurePanel class="text-gray-500"> Yes! You can purchase a license that you can share with your entire team. </DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' </script>
Headless UIは、どのリストボックスオプションが現在選択されているか、ポップオーバーが開いているか閉じているか、またはDisclosure内のどのアイテムがキーボードで現在アクティブになっているかなど、各コンポーネントに関する多くの状態を追跡します。
しかし、コンポーネントはヘッドレスであり、デフォルトでは完全にスタイルが適用されていないため、各状態に必要なスタイルを自分で提供するまで、UIでこの情報を確認することはできません。
各コンポーネントは、スロットプロパティを介して、現在の状態に関する情報を公開します。これは、異なるスタイルを条件付きで適用したり、異なるコンテンツをレンダリングするために使用できます。
たとえば、Disclosure
コンポーネントはopen
状態を公開します。これは、Disclosureが現在開いているかどうかを示します。
<template>
<Disclosure v-slot="{ open }"><!-- Use the `open` state to conditionally change the direction of an icon. --> <DisclosureButton class="py-2"> <span>Do you offer technical support?</span><ChevronRightIcon :class="open && 'rotate-90 transform'" /></DisclosureButton> <DisclosurePanel>No</DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' import { ChevronRightIcon } from '@heroicons/vue/20/solid' </script>
利用可能なすべてのスロットプロパティの完全なリストについては、コンポーネントAPIドキュメントを参照してください。
各コンポーネントは、data-headlessui-state
属性を介して現在の状態に関する情報も公開します。これは、異なるスタイルを条件付きで適用するために使用できます。
スロットプロパティAPIのいずれかの状態がtrue
の場合、これらの状態はスペースで区切られた文字列としてこの属性にリストされるため、[attr~=value]
形式のCSS属性セレクターでターゲットにできます。
たとえば、以下は、Disclosureが開いているときに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:*
のような修飾子でこの属性をターゲットにできます。
<template> <Disclosure> <DisclosureButton class="py-2"> <span>Do you offer technical support?</span>
<ChevronRightIcon class="ui-open:rotate-90 ui-open:transform" /></DisclosureButton> <DisclosurePanel>No</DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' import { ChevronRightIcon } from '@heroicons/vue/20/solid' </script>
デフォルトでは、DisclosurePanel
は、Disclosure
コンポーネント自体内で追跡される内部の開閉状態に基づいて自動的に表示/非表示になります。
<template> <Disclosure> <DisclosureButton>Is team pricing available?</DisclosureButton> <!-- By default, the `DisclosurePanel` will automatically show/hide when the `DisclosureButton` is pressed. --> <DisclosurePanel> Yes! You can purchase a license that you can share with your entire team. </DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' </script>
(何らかの理由で追加のラッパー要素を追加する必要があるなど)自分でこれを処理したい場合は、static
プロパティをDisclosurePanel
に渡して、常にレンダリングするように指示し、open
スロットプロパティを使用して、パネルの表示/非表示を自分で制御できます。
<template>
<Disclosure v-slot="{ open }"><DisclosureButton>Is team pricing available?</DisclosureButton><div v-show="open"><!-- Using the `static` prop, the `DisclosurePanel` is always rendered and the `open` state is ignored. --><DisclosurePanel static>Yes! You can purchase a license that you can share with your entire team. </DisclosurePanel> </div> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' </script>
パネルの子をクリックしたときにDisclosureを手動で閉じるには、その子をDisclosureButton
としてレンダリングします。:as
プロパティを使用して、レンダリングされる要素をカスタマイズできます。
<template> <Disclosure> <DisclosureButton>Open mobile menu</DisclosureButton> <DisclosurePanel>
<DisclosureButton :as="MyLink" href="/home">Home</DisclosureButton><!-- ... --> </DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' import MyLink from './MyLink' </script>
これは、次のページに移動するときにDisclosureを閉じる必要があるリンクを含むモバイルメニューのようなものにDisclosureを使用する場合に特に便利です。
または、Disclosure
とDisclosurePanel
は、close()
スロットプロパティを公開します。これを使用して、非同期アクションを実行した後などに、パネルを命令的に閉じることができます。
<template> <Disclosure> <DisclosureButton>Terms</DisclosureButton>
<DisclosurePanel v-slot="{ close }"><button @click="accept(close)">Read and accept</button></DisclosurePanel></Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue'async function accept(close) {await fetch('/accept-terms', { method: 'POST' })close()}</script>
デフォルトでは、close()
を呼び出した後にDisclosureButton
がフォーカスを受け取りますが、close(ref)
にrefを渡すことでこれを変更できます。
Disclosureのパネルの開閉をアニメーション化するには、Vueの組み込みの<transition>
コンポーネントを使用できます。<transition>
でDisclosurePanel
をラップするだけで、トランジションが自動的に適用されます。
<template> <Disclosure> <DisclosureButton>Is team pricing available?</DisclosureButton> <!-- Use the built-in `transition` component to add transitions. -->
<transitionenter-active-class="transition duration-100 ease-out"enter-from-class="transform scale-95 opacity-0"enter-to-class="transform scale-100 opacity-100"leave-active-class="transition duration-75 ease-out"leave-from-class="transform scale-100 opacity-100"leave-to-class="transform scale-95 opacity-0"><DisclosurePanel> Yes! You can purchase a license that you can share with your entire team. </DisclosurePanel> </transition> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' </script>
Disclosureの異なる子の複数のトランジションを調整したい場合は、Headless UIに含まれるTransitionコンポーネントを確認してください。
Disclosure
とそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルトの要素をレンダリングします。Button
は<button>
をレンダリングし、Panel
は<div>
をレンダリングします。対照的に、ルートのDisclosure
コンポーネントは、要素をレンダリングせず、代わりにデフォルトで子を直接レンダリングします。
これは、すべてのコンポーネントに存在するas
プロパティを使用すると簡単に変更できます。
<template> <!-- Render a `div` for the root `Disclosure` component -->
<Disclosure as="div"><!-- Don't render any element (only children) for the `DisclosureButton` component --><DisclosureButton as="template"><button>What languages do you support?</button> </DisclosureButton> <!-- Render a `ul` for the `DisclosurePanel` component --><DisclosurePanel as="ul"><li>HTML</li> <li>CSS</li> <li>JavaScript</li> </DisclosurePanel> </Disclosure> </template> <script setup> import { Disclosure, DisclosureButton, DisclosurePanel, } from '@headlessui/vue' </script>
DisclosureButton
をクリックすると、Disclosureのパネルが開閉します。
コマンド | 説明 |
| パネルを切り替えます |
プロパティ | デフォルト | 説明 |
as | template | 文字列 | コンポーネント
|
defaultOpen | false | ブール値
|
スロットプロパティ | 説明 |
open |
Disclosureが開いているかどうか。 |
close |
Disclosureを閉じ、 |
プロパティ | デフォルト | 説明 |
as | button | 文字列 | コンポーネント
|
スロットプロパティ | 説明 |
open |
Disclosureが開いているかどうか。 |
プロパティ | デフォルト | 説明 |
as | div | 文字列 | コンポーネント
|
static | false | ブール値 要素が内部で管理される開閉状態を無視するかどうか。 注意: |
unmount | true | ブール値 要素を開閉状態に基づいてアンマウントするか非表示にするか。 注意: |
スロットプロパティ | 説明 |
open |
Disclosureが開いているかどうか。 |
close |
Disclosureを閉じ、 |