Disclosure

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

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

このライブラリはVue 3のみをサポートしていることに注意してください

npm install @headlessui/vue

Disclosureは、DisclosureDisclosureButtonDisclosurePanelコンポーネントを使用して構築されます。

ボタンをクリックするとパネルが自動的に開閉し、すべてのコンポーネントは、aria-expandedaria-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を使用する場合に特に便利です。

または、DisclosureDisclosurePanelは、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. -->
<transition
enter-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のパネルが開閉します。

コマンド説明

DisclosureButtonにフォーカスがあるときにEnter or Spaceを押します。

パネルを切り替えます

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

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

プロパティデフォルト説明
astemplate
文字列 | コンポーネント

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

defaultOpenfalse
ブール値

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

スロットプロパティ説明
open

ブール値

Disclosureが開いているかどうか。

close

(ref?: ref | HTMLElement) => void

Disclosureを閉じ、DisclosureButtonに再フォーカスします。オプションで、代わりにフォーカスするrefまたはHTMLElementを渡します。

Disclosureを切り替えるトリガーコンポーネント。

プロパティデフォルト説明
asbutton
文字列 | コンポーネント

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

スロットプロパティ説明
open

ブール値

Disclosureが開いているかどうか。

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

プロパティデフォルト説明
asdiv
文字列 | コンポーネント

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

staticfalse
ブール値

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

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

unmounttrue
ブール値

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

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

スロットプロパティ説明
open

ブール値

Disclosureが開いているかどうか。

close

(ref?: ref | HTMLElement) => void

Disclosureを閉じ、DisclosureButtonに再フォーカスします。オプションで、代わりにフォーカスするrefまたはHTMLElementを渡します。

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

これは、このようなオープンソースプロジェクトでの私たちの活動をサポートし、改善して適切にメンテナンスできるようにするための素晴らしい方法です。