スイッチ (トグル)
スイッチは、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.Group
、Switch
、Switch.Label
コンポーネントを使用して、ラベルをボタンの兄弟としてレンダリングする方法を示します。Switch.Label
はSwitch
コンポーネントと一緒に動作し、両方とも親の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 MotionやReact Springなど、Reactエコシステムの他のアニメーションライブラリともうまく連携します。
デフォルトでは、Switch
の子はスクリーンリーダーのラベルとして使用されます。Switch.Label
を使用している場合、Switch
コンポーネントのコンテンツは支援技術によって無視されます。
Switch
またはSwitch.Label
をクリックすると、スイッチのオンとオフが切り替わります。
コマンド | 説明 |
Space | スイッチを切り替えます |
Enter フォーム内 | フォームを送信します |
プロパティ | デフォルト | 説明 |
as | button | String | コンポーネント
|
checked | — | Boolean スイッチがチェックされているかどうか。 |
defaultChecked | — | T 非制御コンポーネントとして使用する場合のデフォルトのチェックされた値。 |
onChange | — | (value: Boolean) => void スイッチが切り替えられたときに呼び出す関数。 |
name | — | String フォーム内でこのコンポーネントを使用する場合に使用される名前。 |
value | — | String このコンポーネントをフォーム内で使用する場合、チェックされている場合に使用される値。 |
レンダープロップ | 説明 |
checked |
スイッチがチェックされているかどうか。 |
プロパティ | デフォルト | 説明 |
as | label | String | コンポーネント
|
passive | false | Boolean trueの場合、ラベルをクリックしても |
プロパティ | デフォルト | 説明 |
as | p | String | コンポーネント
|
プロパティ | デフォルト | 説明 |
as | Fragment | String | コンポーネント
|