モダンUIコンポーネントの実装パターン
実装ガイドReact、Vue、Angularで共通して使えるUIパターンを解説
モバイルナビゲーションやフィルタパネルに最適なスライド式のサイドパネルコンポーネント
新しいメッセージ
田中さんからメッセージが届きました
タスク完了
プロジェクトのレビューが完了しました
左右上下からのスライドイン、用途に応じた最適な表示方向を選択可能
背景のスクロール防止、Escキーでの閉じる、適切なフォーカストラップ実装
CSS Transitionによる滑らかな開閉動作、オーバーレイのフェード効果
プロンプト例:
モバイルファーストのレスポンシブなナビゲーションドロワーを実装してください。メニューアイテムのネスト対応、アクティブ状態の管理、スムーズなアニメーション、アクセシビリティ配慮を含めてください。
プロンプト例:
ECサイトの商品検索画面で使用するフィルタドロワーを実装してください。カテゴリ選択、価格帯スライダー、ブランドチェックボックス、並び順選択、リアルタイム結果更新機能を含めてください。
プロンプト例:
サブメニューを持つ多階層ナビゲーションドロワーを実装してください。階層の開閉アニメーション、パンくずリスト、検索機能、お気に入り機能、履歴機能を含めてください。
import { useState, useEffect } from 'react';
interface DrawerProps {
isOpen: boolean;
onClose: () => void;
direction?: 'left' | 'right' | 'top' | 'bottom';
width?: string;
height?: string;
children: React.ReactNode;
}
export default function Drawer({
isOpen,
onClose,
direction = 'left',
width = '256px',
height = '256px',
children
}: DrawerProps) {
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = '';
};
}, [isOpen, onClose]);
const getDrawerStyles = () => {
const baseStyles = `fixed bg-white shadow-xl z-50 transition-transform duration-300`;
switch (direction) {
case 'left':
return `${baseStyles} top-0 left-0 h-full transform ${
isOpen ? 'translate-x-0' : '-translate-x-full'
}`;
case 'right':
return `${baseStyles} top-0 right-0 h-full transform ${
isOpen ? 'translate-x-0' : 'translate-x-full'
}`;
case 'top':
return `${baseStyles} top-0 left-0 right-0 transform ${
isOpen ? 'translate-y-0' : '-translate-y-full'
}`;
case 'bottom':
return `${baseStyles} bottom-0 left-0 right-0 transform ${
isOpen ? 'translate-y-0' : 'translate-y-full'
}`;
default:
return baseStyles;
}
};
if (!isOpen) return null;
return (
<>
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40 transition-opacity duration-300"
onClick={onClose}
/>
<div
className={getDrawerStyles()}
style={direction === 'left' || direction === 'right' ? { width } : { height }}
>
{children}
</div>
</>
);
}
// 基本的な使用例
<Drawer
isOpen={isNavOpen}
onClose={() => setIsNavOpen(false)}
direction="left"
>
<div className="p-4">
<h2 className="text-lg font-semibold mb-4">ナビゲーション</h2>
<nav>
<a href="#" className="block py-2">ホーム</a>
<a href="#" className="block py-2">プロフィール</a>
<a href="#" className="block py-2">設定</a>
</nav>
</div>
</Drawer>
// フィルタドロワー(右から)
<Drawer
isOpen={isFilterOpen}
onClose={() => setIsFilterOpen(false)}
direction="right"
>
<div className="p-4">
<h2 className="text-lg font-semibold mb-4">フィルタ</h2>
<div className="space-y-4">
<select className="w-full p-2 border rounded">
<option>カテゴリを選択</option>
</select>
<input type="range" className="w-full" />
</div>
</div>
</Drawer>