リスト - Vibe Coding Showcase

リスト

情報を整理して表示するための構造化されたリストコンポーネント。順序付き、アイコン付き、インタラクティブなど多様な形式に対応。

デザインプレビュー

基本的なリスト

順序なしリスト

  • 項目 1
  • 項目 2
  • 項目 3

順序付きリスト

  1. 最初のステップ
  2. 次のステップ
  3. 最後のステップ

アイコン付きリスト

  • 成功:タスクが完了しました
  • 警告:設定を確認してください
  • 情報:新機能が利用可能です

インタラクティブリスト

  • ダッシュボード
  • プロジェクト
  • 設定(無効)

バリアント

ストライプ

  • 奇数行は背景色付き
  • 偶数行は通常の背景
  • 奇数行は背景色付き
  • 偶数行は通常の背景

ホバー効果

  • マウスを乗せると
  • 背景色が変わります
  • 視覚的フィードバック

チェックリスト

説明リスト

垂直レイアウト

HTML
ウェブページの構造を定義
CSS
スタイルとレイアウトを制御
JavaScript
インタラクティブな動作を実装

水平レイアウト

React
UIライブラリ
Vue
プログレッシブフレームワーク
Angular
フルスタックフレームワーク

タイムライン

  • プロジェクト開始

    9:00

    要件定義とプランニング

  • デザイン完了

    11:30

    UIデザインとプロトタイプ作成

  • 開発フェーズ

    14:00

    実装とテスト

リストの特徴

多様なリスト形式

基本リスト、チェックリスト、説明リスト、タイムラインなど

インタラクティブ

クリック可能、選択可能、ドラッグ&ドロップ対応

カスタマイズ可能

アイコン、スタイル、レイアウトを自由に設定

AI活用ガイド

スマートタスクリスト

プロンプト例:

AI搭載のインテリジェントタスクリストを作成してください。優先度の自動判定、期限管理、進捗の可視化、関連タスクのグループ化、自然言語での追加機能を実装してください。

インタラクティブ目次

プロンプト例:

長文コンテンツ用の高機能な目次リストを開発してください。スクロール追従、現在位置のハイライト、セクション間のスムーズな移動、読了時間の表示、ブックマーク機能を含めてください。

ソーシャルフィード

プロンプト例:

SNS風の無限スクロールリストコンポーネントを実装してください。リアルタイム更新、いいね・コメント機能、画像・動画のプレビュー、ユーザーアクションのアニメーション、最適化されたパフォーマンスを含めてください。

実装コード

ファイルサイズ: 11.2KB TypeScript

コンポーネント実装

import React from 'react';
import type { BaseListProps } from '../types';

interface ListProps extends BaseListProps {
  children: React.ReactNode;
  className?: string;
}

interface ListItemProps {
  children: React.ReactNode;
  icon?: React.ReactNode;
  active?: boolean;
  disabled?: boolean;
  onClick?: () => void;
  className?: string;
}

export const List: React.FC<ListProps> = ({
  children,
  variant = 'default',
  size = 'md',
  ordered = false,
  className = '',
}) => {
  // サイズに基づくスタイル
  const getSizeClasses = () => {
    const sizes = {
      sm: 'text-sm space-y-1',
      md: 'text-base space-y-2',
      lg: 'text-lg space-y-3',
    };
    return sizes[size as keyof typeof sizes] || sizes.md;
  };

  // バリアントに基づくスタイル
  const getVariantClasses = () => {
    const variants = {
      default: '',
      bordered: 'border border-gray-200 rounded-lg p-4',
      striped: '[&>*:nth-child(odd)]:bg-gray-50',
      hover: '[&>*]:hover:bg-gray-50',
    };
    return variants[variant as keyof typeof variants] || '';
  };

  const Component = ordered ? 'ol' : 'ul';

  return (
    <Component
      className={`
        ${getSizeClasses()}
        ${getVariantClasses()}
        ${ordered ? 'list-decimal list-inside' : ''}
        ${className}
      `}
    >
      {children}
    </Component>
  );
};

export const ListItem: React.FC<ListItemProps> = ({
  children,
  icon,
  active = false,
  disabled = false,
  onClick,
  className = '',
}) => {
  const isInteractive = !!onClick;

  const itemClasses = `
    flex items-center px-3 py-2 rounded-lg transition-colors
    ${active ? 'bg-blue-50 text-blue-700' : ''}
    ${disabled ? 'opacity-50 cursor-not-allowed' : ''}
    ${isInteractive && !disabled ? 'cursor-pointer hover:bg-gray-100' : ''}
    ${className}
  `.trim();

  const content = (
    <>
      {icon && (
        <span className={`flex-shrink-0 mr-3 ${active ? 'text-blue-600' : 'text-gray-400'}`}>
          {icon}
        </span>
      )}
      <span className="flex-1">{children}</span>
    </>
  );

  if (onClick && !disabled) {
    return (
      <li
        className={itemClasses}
        onClick={onClick}
        role="button"
        tabIndex={0}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            onClick();
          }
        }}
      >
        {content}
      </li>
    );
  }

  return <li className={itemClasses}>{content}</li>;
};

// チェックリスト
export const CheckList: React.FC<{
  items: Array<{
    id: string;
    label: string;
    checked: boolean;
  }>;
  onToggle: (id: string) => void;
  className?: string;
}> = ({ items, onToggle, className = '' }) => {
  return (
    <ul className={`space-y-2 ${className}`}>
      {items.map((item) => (
        <li key={item.id} className="flex items-center">
          <input
            type="checkbox"
            id={item.id}
            checked={item.checked}
            onChange={() => onToggle(item.id)}
            className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
          />
          <label
            htmlFor={item.id}
            className={`ml-3 text-sm cursor-pointer ${
              item.checked ? 'line-through text-gray-500' : 'text-gray-700'
            }`}
          >
            {item.label}
          </label>
        </li>
      ))}
    </ul>
  );
};

// 説明リスト
export const DescriptionList: React.FC<{
  items: Array<{
    term: string;
    description: string;
  }>;
  layout?: 'horizontal' | 'vertical';
  className?: string;
}> = ({ items, layout = 'vertical', className = '' }) => {
  if (layout === 'horizontal') {
    return (
      <dl className={`grid grid-cols-1 sm:grid-cols-2 gap-4 ${className}`}>
        {items.map((item, index) => (
          <div key={index} className="flex">
            <dt className="font-medium text-gray-900 w-1/3">{item.term}</dt>
            <dd className="text-gray-700 w-2/3">{item.description}</dd>
          </div>
        ))}
      </dl>
    );
  }

  return (
    <dl className={`space-y-4 ${className}`}>
      {items.map((item, index) => (
        <div key={index}>
          <dt className="font-medium text-gray-900">{item.term}</dt>
          <dd className="mt-1 text-gray-700">{item.description}</dd>
        </div>
      ))}
    </dl>
  );
};

// タイムラインリスト
export const TimelineList: React.FC<{
  items: Array<{
    id: string;
    title: string;
    description?: string;
    time: string;
    icon?: React.ReactNode;
    active?: boolean;
  }>;
  className?: string;
}> = ({ items, className = '' }) => {
  return (
    <div className={`relative ${className}`}>
      {/* 垂直線 */}
      <div className="absolute left-4 top-0 bottom-0 w-0.5 bg-gray-300"></div>
      
      <ul className="space-y-8">
        {items.map((item, index) => (
          <li key={item.id} className="relative flex items-start">
            {/* ドット/アイコン */}
            <div
              className={`
                relative z-10 flex items-center justify-center w-8 h-8 rounded-full
                ${item.active ? 'bg-blue-600' : 'bg-white border-2 border-gray-300'}
              `}
            >
              {item.icon ? (
                <span className={item.active ? 'text-white' : 'text-gray-600'}>
                  {item.icon}
                </span>
              ) : (
                <div className={`w-3 h-3 rounded-full ${item.active ? 'bg-white' : 'bg-gray-400'}`} />
              )}
            </div>
            
            {/* コンテンツ */}
            <div className="ml-6 flex-1">
              <div className="flex items-center mb-1">
                <h4 className={`font-medium ${item.active ? 'text-blue-600' : 'text-gray-900'}`}>
                  {item.title}
                </h4>
                <span className="ml-auto text-sm text-gray-500">{item.time}</span>
              </div>
              {item.description && (
                <p className="text-sm text-gray-600">{item.description}</p>
              )}
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
};

// リストのデモコンポーネント
export const ListDemo: React.FC = () => {
  const [checkItems, setCheckItems] = React.useState([
    { id: '1', label: 'タスク1を完了する', checked: true },
    { id: '2', label: 'ミーティングに参加する', checked: false },
    { id: '3', label: 'レポートを提出する', checked: false },
  ]);

  const toggleCheck = (id: string) => {
    setCheckItems(items =>
      items.map(item =>
        item.id === id ? { ...item, checked: !item.checked } : item
      )
    );
  };

  return (
    <div className="space-y-12">
      {/* 基本的なリスト */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">基本的なリスト</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">順序なしリスト</h4>
            <List>
              <ListItem>項目 1</ListItem>
              <ListItem>項目 2</ListItem>
              <ListItem>項目 3</ListItem>
            </List>
          </div>
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">順序付きリスト</h4>
            <List ordered>
              <ListItem>最初のステップ</ListItem>
              <ListItem>次のステップ</ListItem>
              <ListItem>最後のステップ</ListItem>
            </List>
          </div>
        </div>
      </div>

      {/* アイコン付きリスト */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">アイコン付きリスト</h3>
        <List>
          <ListItem
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
            }
          >
            成功:タスクが完了しました
          </ListItem>
          <ListItem
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
              </svg>
            }
          >
            警告:設定を確認してください
          </ListItem>
          <ListItem
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
            }
          >
            情報:新機能が利用可能です
          </ListItem>
        </List>
      </div>

      {/* インタラクティブリスト */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">インタラクティブリスト</h3>
        <List variant="bordered">
          <ListItem
            onClick={() => alert('ダッシュボードをクリック')}
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
              </svg>
            }
            active
          >
            ダッシュボード
          </ListItem>
          <ListItem
            onClick={() => alert('プロジェクトをクリック')}
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
              </svg>
            }
          >
            プロジェクト
          </ListItem>
          <ListItem
            onClick={() => alert('無効化されています')}
            icon={
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
              </svg>
            }
            disabled
          >
            設定(無効)
          </ListItem>
        </List>
      </div>

      {/* バリアント */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">バリアント</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">ストライプ</h4>
            <List variant="striped">
              <ListItem>奇数行は背景色付き</ListItem>
              <ListItem>偶数行は通常の背景</ListItem>
              <ListItem>奇数行は背景色付き</ListItem>
              <ListItem>偶数行は通常の背景</ListItem>
            </List>
          </div>
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">ホバー効果</h4>
            <List variant="hover">
              <ListItem>マウスを乗せると</ListItem>
              <ListItem>背景色が変わります</ListItem>
              <ListItem>視覚的フィードバック</ListItem>
            </List>
          </div>
        </div>
      </div>

      {/* チェックリスト */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">チェックリスト</h3>
        <CheckList items={checkItems} onToggle={toggleCheck} />
      </div>

      {/* 説明リスト */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">説明リスト</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">垂直レイアウト</h4>
            <DescriptionList
              items={[
                { term: 'HTML', description: 'ウェブページの構造を定義' },
                { term: 'CSS', description: 'スタイルとレイアウトを制御' },
                { term: 'JavaScript', description: 'インタラクティブな動作を実装' },
              ]}
            />
          </div>
          <div>
            <h4 className="text-sm font-medium text-gray-700 mb-2">水平レイアウト</h4>
            <DescriptionList
              layout="horizontal"
              items={[
                { term: 'React', description: 'UIライブラリ' },
                { term: 'Vue', description: 'プログレッシブフレームワーク' },
                { term: 'Angular', description: 'フルスタックフレームワーク' },
              ]}
            />
          </div>
        </div>
      </div>

      {/* タイムライン */}
      <div className="space-y-4">
        <h3 className="text-lg font-semibold">タイムライン</h3>
        <TimelineList
          items={[
            {
              id: '1',
              title: 'プロジェクト開始',
              description: '要件定義とプランニング',
              time: '9:00',
              active: true,
            },
            {
              id: '2',
              title: 'デザイン完了',
              description: 'UIデザインとプロトタイプ作成',
              time: '11:30',
            },
            {
              id: '3',
              title: '開発フェーズ',
              description: '実装とテスト',
              time: '14:00',
              icon: (
                <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
                </svg>
              ),
            },
            {
              id: '4',
              title: 'リリース予定',
              time: '18:00',
            },
          ]}
        />
      </div>
    </div>
  );
};

export default List;

使用例

// 基本的な使用例
import { List, ListItem, CheckList, DescriptionList, TimelineList } from './list';

function App() {
  const [checkItems, setCheckItems] = useState([
    { id: '1', label: 'タスク1', checked: true },
    { id: '2', label: 'タスク2', checked: false },
  ]);

  return (
    <div>
      {/* 基本的なリスト */}
      <List>
        <ListItem>項目 1</ListItem>
        <ListItem>項目 2</ListItem>
        <ListItem>項目 3</ListItem>
      </List>

      {/* 順序付きリスト */}
      <List ordered>
        <ListItem>ステップ 1</ListItem>
        <ListItem>ステップ 2</ListItem>
        <ListItem>ステップ 3</ListItem>
      </List>

      {/* アイコン付きリスト */}
      <List>
        <ListItem icon={<CheckIcon />}>
          完了したタスク
        </ListItem>
        <ListItem icon={<AlertIcon />}>
          注意が必要
        </ListItem>
      </List>

      {/* インタラクティブリスト */}
      <List variant="bordered">
        <ListItem
          onClick={() => navigate('/dashboard')}
          icon={<HomeIcon />}
          active
        >
          ダッシュボード
        </ListItem>
        <ListItem
          onClick={() => navigate('/projects')}
          icon={<FolderIcon />}
        >
          プロジェクト
        </ListItem>
      </List>

      {/* バリアント */}
      <List variant="striped">
        <ListItem>ストライプ表示</ListItem>
        <ListItem>交互に背景色</ListItem>
      </List>

      {/* チェックリスト */}
      <CheckList
        items={checkItems}
        onToggle={(id) => {
          setCheckItems(items =>
            items.map(item =>
              item.id === id ? { ...item, checked: !item.checked } : item
            )
          );
        }}
      />

      {/* 説明リスト */}
      <DescriptionList
        items={[
          { term: 'React', description: 'UIライブラリ' },
          { term: 'TypeScript', description: '型安全なJavaScript' },
        ]}
      />

      {/* タイムライン */}
      <TimelineList
        items={[
          {
            id: '1',
            title: 'プロジェクト開始',
            description: '初期計画',
            time: '9:00',
            active: true,
          },
          {
            id: '2',
            title: '開発フェーズ',
            time: '14:00',
          },
        ]}
      />
    </div>
  );
}