Drawer - Vibe Coding Showcase

Drawer

モバイルナビゲーションやフィルタパネルに最適なスライド式のサイドパネルコンポーネント

デザインプレビュー

ナビゲーションドロワー(左)

フィルタドロワー(右)

フィルタ

アクションドロワー(下)

アクション

通知ドロワー(上)

通知

新しいメッセージ

田中さんからメッセージが届きました

タスク完了

プロジェクトのレビューが完了しました

Drawerの特徴

4方向スライド対応

左右上下からのスライドイン、用途に応じた最適な表示方向を選択可能

フォーカス管理

背景のスクロール防止、Escキーでの閉じる、適切なフォーカストラップ実装

スムーズなアニメーション

CSS Transitionによる滑らかな開閉動作、オーバーレイのフェード効果

AI活用ガイド

レスポンシブナビゲーション

プロンプト例:

モバイルファーストのレスポンシブなナビゲーションドロワーを実装してください。メニューアイテムのネスト対応、アクティブ状態の管理、スムーズなアニメーション、アクセシビリティ配慮を含めてください。

フィルタパネル

プロンプト例:

ECサイトの商品検索画面で使用するフィルタドロワーを実装してください。カテゴリ選択、価格帯スライダー、ブランドチェックボックス、並び順選択、リアルタイム結果更新機能を含めてください。

マルチレベルメニュー

プロンプト例:

サブメニューを持つ多階層ナビゲーションドロワーを実装してください。階層の開閉アニメーション、パンくずリスト、検索機能、お気に入り機能、履歴機能を含めてください。

実装コード

ファイルサイズ: 2.1KB TypeScript

コンポーネント実装

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>