SCALE — Build Lab
開発パターン · REACT PATTERN

一括操作パターン

CATEGORY開発パターン TYPEReact Pattern EFFORT90〜240分 DIFFICULTY
PRIMARY CODE
tsx
import { useState } from 'react';

// 一括選択 + アクションフック
export function useBulkSelection<T extends { id: string }>(items: T[]) {
  const [selected, setSelected] = useState<Set<string>>(new Set());

  const toggle = (id: string) => setSelected((s) => {
    const n = new Set(s);
    n.has(id) ? n.delete(id) : n.add(id);
    return n;
  });

  const toggleAll = () => setSelected((s) =>
    s.size === items.length ? new Set() : new Set(items.map((i) => i.id))
  );

  const clear = () => setSelected(new Set());

  return {
    selected, toggle, toggleAll, clear,
    count: selected.size,
    selectedItems: items.filter((i) => selected.has(i.id)),
    allChecked: selected.size === items.length && items.length > 0,
  };
}

// 一括アクションバー(選択数に応じて表示)
export function BulkActionBar({ count, onClear, children }: { count: number; onClear: () => void; children: React.ReactNode }) {
  if (count === 0) return null;
  return (
    <div className="sticky top-0 z-30 bg-indigo-500/10 border-b border-indigo-500/30 px-4 py-3 flex items-center gap-3">
      <span className="text-sm text-indigo-200">{count}件 選択中</span>
      <button onClick={onClear} className="text-xs text-zinc-400 hover:text-zinc-200">解除</button>
      <div className="ml-auto flex gap-2">{children}</div>
    </div>
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • タスク一覧
  • 応募者一覧

一括操作パターン

:LiTarget: 用途

複数選択→一括削除/編集/エクスポートのUIパターン。

:LiSparkle: 特徴

  • 複数選択
  • チェックボックス
  • 一括アクションメニュー

:LiCode: コード(コピペ用)

import { useState } from 'react';

// 一括選択 + アクションフック
export function useBulkSelection<T extends { id: string }>(items: T[]) {
  const [selected, setSelected] = useState<Set<string>>(new Set());

  const toggle = (id: string) => setSelected((s) => {
    const n = new Set(s);
    n.has(id) ? n.delete(id) : n.add(id);
    return n;
  });

  const toggleAll = () => setSelected((s) =>
    s.size === items.length ? new Set() : new Set(items.map((i) => i.id))
  );

  const clear = () => setSelected(new Set());

  return {
    selected, toggle, toggleAll, clear,
    count: selected.size,
    selectedItems: items.filter((i) => selected.has(i.id)),
    allChecked: selected.size === items.length && items.length > 0,
  };
}

// 一括アクションバー(選択数に応じて表示)
export function BulkActionBar({ count, onClear, children }: { count: number; onClear: () => void; children: React.ReactNode }) {
  if (count === 0) return null;
  return (
    <div className="sticky top-0 z-30 bg-indigo-500/10 border-b border-indigo-500/30 px-4 py-3 flex items-center gap-3">
      <span className="text-sm text-indigo-200">{count}件 選択中</span>
      <button onClick={onClear} className="text-xs text-zinc-400 hover:text-zinc-200">解除</button>
      <div className="ml-auto flex gap-2">{children}</div>
    </div>
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加