スイッチ (トグル)

スイッチは、2つの状態間で値を切り替えるための快適なインターフェースであり、ネイティブなチェックボックス要素と同じセマンティクスとキーボードナビゲーションを提供します。

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

npm install @headlessui/react

スイッチはSwitchコンポーネントを使用して構築されます。コンポーネントを直接クリックするか、フォーカス中にスペースキーを押してスイッチを切り替えることができます。

スイッチを切り替えると、checked値の反転バージョンでonChange関数が呼び出されます。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled} className={`${ enabled ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full`} > <span className="sr-only">Enable notifications</span> <span className={`${ enabled ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition`} /> </Switch> ) }

Headless UIは、どのスイッチオプションが現在選択されているか、ポップオーバーが開いているか閉じているか、メニューのどの項目がキーボードで現在アクティブであるかなど、各コンポーネントに関する多くの状態を追跡します。

ただし、コンポーネントはヘッドレスであり、ボックスから完全にスタイル設定されていないため、各状態に必要なスタイルを自分で指定するまで、UIでこの情報を確認することはできません。

各コンポーネントは、異なるスタイルを条件付きで適用したり、異なるコンテンツをレンダリングしたりするために使用できるレンダープロップを介して、現在の状態に関する情報を公開します。

たとえば、Switchコンポーネントは、スイッチが現在チェックされているかどうかを示すchecked状態を公開します。

import { useState, Fragment } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled} as={Fragment}>
{({ checked }) => (
/* Use the `checked` state to conditionally style the button. */ <button className={`${
checked ? 'bg-blue-600' : 'bg-gray-200'
}
relative inline-flex h-6 w-11 items-center rounded-full`
}
>
<span className="sr-only">Enable notifications</span> <span className={`${
checked ? 'translate-x-6' : 'translate-x-1'
}
inline-block h-4 w-4 transform rounded-full bg-white transition`
}
/>
</button> )} </Switch> ) }

各コンポーネントの完全なレンダープロップAPIについては、コンポーネントAPIドキュメントを参照してください。

各コンポーネントは、現在の状態に関する情報を、異なるスタイルを条件付きで適用するために使用できるdata-headlessui-state属性を介して公開します。

レンダープロップAPIの状態のいずれかがtrueの場合、これらの状態は、スペースで区切られた文字列としてこの属性にリストされるため、[attr~=value]形式のCSS属性セレクターでターゲットにすることができます。

たとえば、スイッチがチェックされている場合のSwitchコンポーネントのレンダリング結果を次に示します。

<!-- Rendered `Switch` --> <button data-headlessui-state="checked"></button>

Tailwind CSSを使用している場合は、@headlessui/tailwindcssプラグインを使用して、ui-checked:*のようなモディファイアでこの属性をターゲットにすることができます。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch checked={enabled} onChange={setEnabled}
className="relative inline-flex h-6 w-11 items-center rounded-full ui-checked:bg-blue-600 ui-not-checked:bg-gray-200"
>
<span className="sr-only">Enable notifications</span>
<span className="inline-block h-4 w-4 transform rounded-full bg-white transition ui-checked:translate-x-6 ui-not-checked:translate-x-1" />
</Switch> ) }

デフォルトでは、スイッチはbuttonと、それに渡す子要素をレンダリングします。これにより、子要素がボタン内にネストされるため、特定のUIを実装するのが難しくなる場合があります。

このような状況では、Switch.Labelコンポーネントを使用して柔軟性を高めることができます。

この例では、Switch.GroupSwitchSwitch.Labelコンポーネントを使用して、ラベルをボタンの兄弟としてレンダリングする方法を示します。Switch.LabelSwitchコンポーネントと一緒に動作し、両方とも親のSwitch.Groupコンポーネント内でレンダリングする必要があることに注意してください。

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return (
<Switch.Group>
<div className="flex items-center">
<Switch.Label className="mr-4">Enable notifications</Switch.Label>
<Switch checked={enabled} onChange={setEnabled} className={`${ enabled ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2`} > <span className={`${ enabled ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition-transform`} /> </Switch> </div>
</Switch.Group>
) }

デフォルトでは、ネイティブHTMLチェックボックスのラベルと同様に、Switch.Labelをクリックするとスイッチが切り替わります。ラベルをクリック不可にする場合は(設計上意味がない場合など)、Switch.Labelコンポーネントにpassiveプロパティを追加できます

import { useState } from 'react' import { Switch } from '@headlessui/react' function MyToggle() { const [enabled, setEnabled] = useState(false) return ( <Switch.Group>
<Switch.Label passive>Enable notifications</Switch.Label>
<Switch checked={enabled} onChange={setEnabled}> {/* ... */} </Switch> </Switch.Group> ) }

スイッチにnameプロパティを追加すると、非表示のinput要素がレンダリングされ、スイッチの状態と同期されます。

import { useState } from 'react' import { Switch } from '@headlessui/react' function Example() { const [enabled, setEnabled] = useState(true) return ( <form action="/notification-settings" method="post">
<Switch checked={enabled} onChange={setEnabled} name="notifications">
{/* ... */} </Switch> <button>Submit</button> </form> ) }

これにより、ネイティブHTML <form>内でスイッチを使用し、スイッチがネイティブHTMLフォームコントロールであるかのように従来のフォーム送信を行うことができます。

デフォルトでは、スイッチがチェックされている場合は値が'on'になり、スイッチがチェックされていない場合は値が存在しません。

<input type="hidden" name="notifications" value="on" />

必要に応じてvalueプロパティを使用して値をカスタマイズできます

import { useState } from 'react' import { Switch } from '@headlessui/react' function Example() { const [enabled, setEnabled] = useState(true) return ( <form action="/accounts" method="post"> <Switch checked={enabled} onChange={setEnabled}
name="terms"
value="accept"
>
{/* ... */} </Switch> <button>Submit</button> </form> ) }

非表示の入力は、スイッチがチェックされている場合、カスタム値を使用します

<input type="hidden" name="terms" value="accept" />

checkedプロパティの代わりにdefaultCheckedプロパティをSwitchに提供すると、Headless UIは状態を内部的に追跡し、非制御コンポーネントとして使用できます。

Switchコンポーネントのcheckedレンダープロップを介して現在の状態にアクセスできます。

import { Fragment } from 'react' import { Switch } from '@headlessui/react' function Example() { return ( <form action="/accounts" method="post">
<Switch name="terms-of-service" defaultChecked={true} as={Fragment}>
{({ checked }) => ( <button className={`${ checked ? 'bg-blue-600' : 'bg-gray-200' } relative inline-flex h-6 w-11 items-center rounded-full`} > <span className="sr-only">Enable notifications</span> <span className={`${ checked ? 'translate-x-6' : 'translate-x-1' } inline-block h-4 w-4 transform rounded-full bg-white transition`} /> </button> )} </Switch> <button>Submit</button> </form> ) }

これにより、リストボックスをHTMLフォームで使用する場合や、React状態を使用して追跡する代わりにFormDataを使用して状態を収集するフォームAPIで使用する場合に、コードを簡略化できます。

副作用を実行する必要がある場合にコンポーネントの値が変更されると、指定したonChangeプロパティは引き続き呼び出されますが、コンポーネントの状態を自分で追跡するために使用する必要はありません。

スイッチは通常、他のコンポーネントのようにマウント/アンマウントされるのではなく、常にDOMにレンダリングされるため、多くの場合、単純なCSSトランジションでスイッチをアニメーション化するのに十分です

import { useState } from "react"; import { Switch } from "@headlessui/react"; function MyToggle() { const [enabled, setEnabled] = useState(false); return ( <Switch checked={enabled} onChange={setEnabled}> <span /* Transition the switch's knob on state change */
className={`transform transition ease-in-out duration-200
${enabled ? "translate-x-9" : "translate-x-0"}
`}
/> {/* ... */} </Switch> ); }

レンダリングしないため、Headless UIコンポーネントは、Framer MotionReact Springなど、Reactエコシステムの他のアニメーションライブラリともうまく連携します。

デフォルトでは、Switchの子はスクリーンリーダーのラベルとして使用されます。Switch.Labelを使用している場合、Switchコンポーネントのコンテンツは支援技術によって無視されます。

SwitchまたはSwitch.Labelをクリックすると、スイッチのオンとオフが切り替わります。

コマンド説明

Space Switchにフォーカスがある場合

スイッチを切り替えます

Enter フォーム内

フォームを送信します

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

メインのスイッチコンポーネント。

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

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

checked
Boolean

スイッチがチェックされているかどうか。

defaultChecked
T

非制御コンポーネントとして使用する場合のデフォルトのチェックされた値。

onChange
(value: Boolean) => void

スイッチが切り替えられたときに呼び出す関数。

name
String

フォーム内でこのコンポーネントを使用する場合に使用される名前。

value
String

このコンポーネントをフォーム内で使用する場合、チェックされている場合に使用される値。

レンダープロップ説明
checked

Boolean

スイッチがチェックされているかどうか。

プロパティデフォルト説明
aslabel
String | コンポーネント

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

passivefalse
Boolean

trueの場合、ラベルをクリックしてもSwitchは切り替わりません。

プロパティデフォルト説明
asp
String | コンポーネント

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

プロパティデフォルト説明
asFragment
String | コンポーネント

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

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

これは、このようなオープンソースプロジェクトでの当社の活動をサポートする優れた方法であり、当社がプロジェクトを改善し、適切にメンテナンスし続けることを可能にします。