## やりたいこと
[[🗃️daisyUI]]を使って、ドロップダウンメニューを作成したい
## 調査
- [[🗃️daisyUI]]でドロップダウンのメニューを作る方法は、いくつかある
- [Tailwind Dropdown Component — Tailwind CSS Components](https://daisyui.com/components/dropdown/)
1. `<details>`と`<summary>`を使う
2. `<label>`を使う
- `<label>`方式だと、キーボード操作での遷移が微妙な動きをする
- [[🗃️JavaScript]]での制御方法がいまいち、イメージがつかない
の方式の方が、楽な感じがするので1方式で行くことにした。
## 実装
- 素の状態だといくつか想定した通りの挙動にならなかったので、[[🗃️React]]側で制御を入れることにした
### 課題1 ドロップダウンの外をクリックしてもメニューが閉じない
- 想定する挙動としては、別要素をクリックしたときも閉じてほしいが、できない
#### 解決策
- [[🗃️Ref(React)]]を使って、制御する
- `open`を使って開閉するようにする
### 課題2 ドロップダウンの外をクリックしてもメニューが閉じない
- 画面遷移をするアプリケーションであれば問題ないが、 [[🗃️SPA]]になっていたため、差分だけが再レンダリングされて、メニューは開いたままの状態になってしまった
#### 解決策
- メニュー内のリンクを押下したときは、`open = false`にして閉じるようにする
最終的に、こんな感じの実装になった。
```tsx
export const Navbar: React.FC = () => {
const dropdownRef = useRef<HTMLDetailsElement | null>(null);
useEffect(() => {
const handleOutsideClick = (e: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node) &&
dropdownRef.current.open
) {
dropdownRef.current.open = false;
}
};
document.addEventListener('click', handleOutsideClick);
return () => {
document.removeEventListener('click', handleOutsideClick);
};
}, []);
const handleCloseRefElement = () => {
if (dropdownRef.current) {
dropdownRef.current.open = false;
}
};
return (
<header className='bg-base-300 navbar'>
<div className='flex-1'>
<a href='/' className='text-xl normal-case btn btn-ghost'>
App
</a>
</div>
<div className='flex-none'>
<ul className='p-0 menu menu-horizontal'>
<li>
<details ref={dropdownRef}>
<summary>Button</summary>
<ul className='p-2 bg-base-100'>
<li>
<a href='/' onClick={handleCloseRefElement}>Link 1</Link>
</li>
<li>
<a href='/' onClick={handleCloseRefElement}>Link 2</Link>
</li>
</ul>
</details>
</li>
</ul>
</div>
</header>
);
};
```
- ロジック部分は、 [[🗃️Custom Hooks]]で切り出してあげると良いかも。