データグリッドライブラリ比較

5つのアプローチで編集可能なデータグリッドを実装し、機能とスタイルを比較

共通のサンプルデータ

社員情報テーブルを共通データとして使用しています。各デモではセルをダブルクリック(AG Gridはシングルクリック)して編集できます。

[
  {
    "name": "田中太郎",
    "department": "エンジニアリング",
    "position": "シニアエンジニア",
    "salary": 7500000
  },
  {
    "name": "佐藤花子",
    "department": "デザイン",
    "position": "UIデザイナー",
    "salary": 6000000
  },
  {
    "name": "鈴木一郎",
    "department": "エンジニアリング",
    "position": "テックリード",
    "salary": 9000000
  }
]

... 他 5

1. AG Grid Community

業界標準のエンタープライズグリッド — ソート、フィルタ、セル編集などが内蔵

プレビュー

特徴

  • • セルクリックで即座にインライン編集が可能
  • • ソート・フィルタ・カラムリサイズが標準搭載
  • • 大量データ(数万行)でも高速な仮想スクロール
  • • Community版は無料、Enterprise版は有料機能あり

インストール

npm install ag-grid-react ag-grid-community
tsx
'use client';
import { AgGridReact } from 'ag-grid-react';
import { AllCommunityModule, ModuleRegistry, type ColDef } from 'ag-grid-community';
import { useState, useMemo } from 'react';

ModuleRegistry.registerModules([AllCommunityModule]);

interface Employee {
  id: number;
  name: string;
  department: string;
  salary: number;
}

const rowData: Employee[] = [
  { id: 1, name: '田中太郎', department: 'エンジニアリング', salary: 7500000 },
  { id: 2, name: '佐藤花子', department: 'デザイン', salary: 6000000 },
  { id: 3, name: '鈴木一郎', department: 'マーケティング', salary: 5500000 },
];

export function AgGridDemo() {
  const columnDefs = useMemo<ColDef<Employee>[]>(() => [
    { field: 'id', headerName: 'ID', width: 80 },
    { field: 'name', headerName: '氏名', editable: true },
    { field: 'department', headerName: '部署', editable: true },
    {
      field: 'salary',
      headerName: '年収',
      editable: true,
      valueFormatter: p => `¥${p.value.toLocaleString()}`,
    },
  ], []);

  const defaultColDef = useMemo(() => ({
    sortable: true,
    filter: true,
    resizable: true,
  }), []);

  return (
    <div className="ag-theme-alpine" style={{ height: 300, width: '100%' }}>
      <AgGridReact rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} />
    </div>
  );
}

AIプロンプトテンプレート

React + Tailwind CSSで、AG Grid Communityを使った編集可能なデータグリッドを実装してください。
- 使用ライブラリ: ag-grid-react、ag-grid-community(AllCommunityModule、ModuleRegistry)
- AgGridReact コンポーネントと ColDef 型を使ってカラム定義を行うこと
- editable: true でセルをクリックしてインライン編集できること
- defaultColDef に sortable: true、filter: true、resizable: true を設定すること
- valueFormatter でサラリー列を¥形式で書式化すること
- onCellValueChanged コールバックで編集後のデータを state に反映すること
- ag-theme-alpine クラスでスタイルを適用すること

このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

2. TanStack Table(React Table v8)

ヘッドレスUIライブラリ — ロジックのみ提供し、表示は完全にカスタム可能

プレビュー
ID氏名部署役職メール年収入社日
1田中太郎エンジニアリングシニアエンジニアtanaka@example.com¥7,500,0002020-04-01
2佐藤花子デザインUIデザイナーsato@example.com¥6,000,0002021-07-15
3鈴木一郎エンジニアリングテックリードsuzuki@example.com¥9,000,0002019-01-10
4高橋美咲マーケティングマーケターtakahashi@example.com¥5,500,0002022-03-20
5伊藤健太エンジニアリングフロントエンドエンジニアito@example.com¥6,500,0002021-11-01
6渡辺真理人事HRマネージャーwatanabe@example.com¥7,000,0002018-06-01
7山本大輔デザインプロダクトデザイナーyamamoto@example.com¥6,800,0002020-09-15
8中村さくらマーケティングコンテンツディレクターnakamura@example.com¥6,200,0002022-01-10

セルをダブルクリックで編集(IDは編集不可)

特徴

  • • ヘッドレスUIでスタイルに完全な自由度
  • • ソート・フィルタ・ページネーションのロジックを提供
  • • バンドルサイズが非常に小さい(~15KB gzipped)
  • • TypeScript ファーストで型安全

インストール

npm install @tanstack/react-table
tsx
'use client';
import {
  useReactTable,
  getCoreRowModel,
  flexRender,
  type ColumnDef,
} from '@tanstack/react-table';
import { useMemo } from 'react';

interface Employee {
  id: number;
  name: string;
  department: string;
  salary: number;
}

const data: Employee[] = [
  { id: 1, name: '田中太郎', department: 'エンジニアリング', salary: 7500000 },
  { id: 2, name: '佐藤花子', department: 'デザイン', salary: 6000000 },
  { id: 3, name: '鈴木一郎', department: 'マーケティング', salary: 5500000 },
];

export function TanStackTableDemo() {
  const columns = useMemo<ColumnDef<Employee>[]>(() => [
    { accessorKey: 'id', header: 'ID' },
    { accessorKey: 'name', header: '氏名' },
    { accessorKey: 'department', header: '部署' },
    {
      accessorKey: 'salary',
      header: '年収',
      cell: info => `¥${info.getValue<number>().toLocaleString()}`,
    },
  ], []);

  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });

  return (
    <div className="overflow-x-auto border border-gray-200 rounded-lg">
      <table className="w-full text-sm">
        <thead>
          {table.getHeaderGroups().map(hg => (
            <tr key={hg.id} className="bg-gray-100 border-b border-gray-200">
              {hg.headers.map(h => (
                <th key={h.id} className="text-left px-3 py-2.5 font-semibold text-gray-700">
                  {flexRender(h.column.columnDef.header, h.getContext())}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id} className="border-b border-gray-100 hover:bg-blue-50">
              {row.getVisibleCells().map(cell => (
                <td key={cell.id} className="px-3 py-2">
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

AIプロンプトテンプレート

React + Tailwind CSSで、TanStack Table(@tanstack/react-table)を使った編集可能なデータグリッドを実装してください。
- 使用ライブラリ: @tanstack/react-table(useReactTable、getCoreRowModel、flexRender、ColumnDef)
- ColumnDef 型でカラム定義し、useReactTable フックでテーブルを構築すること
- セルをダブルクリックしてインライン編集できること(IDカラムは編集不可)
- 編集中のセルは input 要素に切り替え、Enter/Escape/blur で確定・キャンセルすること
- 数値列(salary)は入力時に number 型の input を使用すること
- getHeaderGroups、getRowModel を使ってヘッダーと行をレンダリングすること

このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

3. MUI DataGrid

Material UIと統合されたデータグリッド — ソート・フィルタ・編集・ページネーションが標準装備

プレビュー

特徴

  • • ソート・フィルタ・ページネーション・インライン編集がFree版で利用可能
  • • 列の表示/非表示切り替えやチェックボックス選択が標準搭載
  • • Material UIのデザインシステムとシームレスに統合
  • • 列固定・行グループ化などの高度な機能はPro版で追加可能

インストール

npm install @mui/x-data-grid @mui/material @emotion/react @emotion/styled
tsx
'use client';
import { DataGrid, type GridColDef, type GridRowsProp } from '@mui/x-data-grid';
import { useState } from 'react';

const initialRows: GridRowsProp = [
  { id: 1, name: '田中太郎', department: 'エンジニアリング', role: 'シニアエンジニア', salary: 7500000 },
  { id: 2, name: '佐藤花子', department: 'デザイン', role: 'UIデザイナー', salary: 6000000 },
  { id: 3, name: '鈴木一郎', department: 'エンジニアリング', role: 'テックリード', salary: 9000000 },
  { id: 4, name: '山田次郎', department: 'マーケティング', role: 'マネージャー', salary: 8000000 },
  { id: 5, name: '伊藤三郎', department: 'デザイン', role: 'グラフィックデザイナー', salary: 5500000 },
];

const columns: GridColDef[] = [
  { field: 'name', headerName: '氏名', width: 130, editable: true },
  { field: 'department', headerName: '部署', width: 160, editable: true },
  { field: 'role', headerName: '役職', width: 160, editable: true },
  {
    field: 'salary',
    headerName: '年収',
    width: 130,
    editable: true,
    valueFormatter: (value) => `¥${Number(value).toLocaleString()}`,
  },
];

export function MUIDataGridDemo() {
  const [rows, setRows] = useState(initialRows);

  return (
    <div style={{ height: 350, width: '100%' }}>
      <DataGrid
        rows={rows}
        columns={columns}
        pageSizeOptions={[5, 10]}
        initialState={{ pagination: { paginationModel: { pageSize: 5 } } }}
        processRowUpdate={(newRow) => {
          setRows(rows.map(row => row.id === newRow.id ? newRow : row));
          return newRow;
        }}
        checkboxSelection
        disableRowSelectionOnClick
      />
    </div>
  );
}

AIプロンプトテンプレート

React + Tailwind CSSで、MUI DataGridを使った編集可能なデータグリッドを実装してください。
- 使用ライブラリ: @mui/x-data-grid、@mui/material、@emotion/react、@emotion/styled
- DataGrid コンポーネントと GridColDef 型でカラム定義を行うこと
- editable: true でセルをクリックしてインライン編集できること
- processRowUpdate コールバックで編集後のデータを state に反映すること
- pageSizeOptions と initialState.pagination でページネーションを設定すること
- valueFormatter で年収列を¥形式で書式化すること
- checkboxSelection で行選択チェックボックスを有効にすること

このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

4. カスタム実装(React + Tailwind CSS)

ライブラリ不要の完全カスタム実装 — 行の追加・削除・ソートも自作

プレビュー
ID氏名部署役職メール年収入社日
1田中太郎エンジニアリングシニアエンジニアtanaka@example.com¥7,500,0002020-04-01
2佐藤花子デザインUIデザイナーsato@example.com¥6,000,0002021-07-15
3鈴木一郎エンジニアリングテックリードsuzuki@example.com¥9,000,0002019-01-10
4高橋美咲マーケティングマーケターtakahashi@example.com¥5,500,0002022-03-20
5伊藤健太エンジニアリングフロントエンドエンジニアito@example.com¥6,500,0002021-11-01
6渡辺真理人事HRマネージャーwatanabe@example.com¥7,000,0002018-06-01
7山本大輔デザインプロダクトデザイナーyamamoto@example.com¥6,800,0002020-09-15
8中村さくらマーケティングコンテンツディレクターnakamura@example.com¥6,200,0002022-01-10

ヘッダーをクリックでソート、セルをダブルクリックで編集

特徴

  • • 外部ライブラリ不要で依存関係が少ない
  • • 行の追加・削除・ソート・インライン編集を自作
  • • デザインの自由度が最も高い
  • • バンドルサイズへの影響が最小限

インストール

追加パッケージ不要(React + Tailwind CSS のみ)
tsx
'use client';
import { useState, useMemo } from 'react';

interface Employee {
  id: number;
  name: string;
  department: string;
  salary: number;
}

const initialData: Employee[] = [
  { id: 1, name: '田中太郎', department: 'エンジニアリング', salary: 7500000 },
  { id: 2, name: '佐藤花子', department: 'デザイン', salary: 6000000 },
  { id: 3, name: '鈴木一郎', department: 'マーケティング', salary: 5500000 },
];

export function CustomGridDemo() {
  const [data, setData] = useState(initialData);
  const [sortField, setSortField] = useState<keyof Employee | null>(null);
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');

  const sorted = useMemo(() => {
    if (!sortField) return data;
    return [...data].sort((a, b) => {
      const av = a[sortField], bv = b[sortField];
      if (typeof av === 'number' && typeof bv === 'number') return sortDir === 'asc' ? av - bv : bv - av;
      return sortDir === 'asc' ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av));
    });
  }, [data, sortField, sortDir]);

  const handleSort = (field: keyof Employee) => {
    if (sortField === field) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortField(field); setSortDir('asc'); }
  };

  return (
    <div className="overflow-x-auto border border-gray-200 rounded-lg">
      <table className="w-full text-sm">
        <thead>
          <tr className="bg-slate-700 text-white">
            {(['id', 'name', 'department', 'salary'] as (keyof Employee)[]).map(key => (
              <th
                key={key}
                className="text-left px-3 py-2.5 font-semibold cursor-pointer hover:bg-white/10"
                onClick={() => handleSort(key)}
              >
                {key} {sortField === key ? (sortDir === 'asc' ? '▲' : '▼') : ''}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {sorted.map((row, i) => (
            <tr key={row.id} className={`border-b border-gray-100 ${i % 2 === 0 ? 'bg-white' : 'bg-gray-50'}`}>
              <td className="px-3 py-2">{row.id}</td>
              <td className="px-3 py-2">{row.name}</td>
              <td className="px-3 py-2">{row.department}</td>
              <td className="px-3 py-2">¥{row.salary.toLocaleString()}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

AIプロンプトテンプレート

React + Tailwind CSSで、ライブラリ不要のカスタムデータグリッドを実装してください。
- 使用ライブラリ: React + Tailwind CSS のみ(外部グリッドライブラリなし)
- セルをダブルクリックしてインライン編集できること(IDカラムは編集不可)
- 列ヘッダーをクリックで昇順・降順ソートできること(useMemo でソート済みデータを計算)
- 行の追加・削除ができること
- 数値列は¥形式で書式化して表示すること
- ソート中のカラムには▲▼アイコンを表示すること
- 偶数・奇数行で背景色を交互に変えるゼブラストライプを実装すること

このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

5. Glide Data Grid

canvasに直接描画するデータグリッド — 数万行でもDOMノードが増えず描画が軽い

プレビュー

データグリッドを読み込み中...

特徴

  • • 表全体をcanvas 1枚に描画するため、行数が増えてもDOMノードが増えない
  • • データを配列で渡さず getCellContent([col, row]) で座標からセルを返す設計
  • • 列ヘッダークリックでのソート・列リサイズ・行番号表示を標準サポート
  • • canvas描画のためSSR不可。next/dynamicssr:false が必須
  • • セルがDOMに存在しないため、検索・スクリーンリーダー・E2Eテストでは扱いが変わる

インストール

npm install @glideapps/glide-data-grid lodash marked react-responsive-carousel
tsx
"use client";

import { useCallback, useMemo, useState } from "react";
import {
  DataEditor,
  GridCellKind,
  type GridCell,
  type GridColumn,
  type Item,
} from "@glideapps/glide-data-grid";
import "@glideapps/glide-data-grid/dist/index.css";

interface Employee {
  id: number;
  name: string;
  department: string;
  position: string;
  email: string;
  salary: number;
  joinDate: string;
}

// 他セクションと同じ8名のサンプルデータ
const initialData: Employee[] = [
  { id: 1, name: "田中太郎", department: "エンジニアリング", position: "シニアエンジニア", email: "tanaka@example.com", salary: 7500000, joinDate: "2020-04-01" },
  { id: 2, name: "佐藤花子", department: "デザイン", position: "UIデザイナー", email: "sato@example.com", salary: 6000000, joinDate: "2021-07-15" },
  { id: 3, name: "鈴木一郎", department: "エンジニアリング", position: "テックリード", email: "suzuki@example.com", salary: 9000000, joinDate: "2019-01-10" },
  { id: 4, name: "高橋美咲", department: "マーケティング", position: "マーケター", email: "takahashi@example.com", salary: 5500000, joinDate: "2022-03-20" },
  { id: 5, name: "伊藤健太", department: "エンジニアリング", position: "フロントエンドエンジニア", email: "ito@example.com", salary: 6500000, joinDate: "2021-11-01" },
  { id: 6, name: "渡辺真理", department: "人事", position: "HRマネージャー", email: "watanabe@example.com", salary: 7000000, joinDate: "2018-06-01" },
  { id: 7, name: "山本大輔", department: "デザイン", position: "プロダクトデザイナー", email: "yamamoto@example.com", salary: 6800000, joinDate: "2020-09-15" },
  { id: 8, name: "中村さくら", department: "マーケティング", position: "コンテンツディレクター", email: "nakamura@example.com", salary: 6200000, joinDate: "2022-01-10" },
];

type SortKey = keyof Employee;

const columnDefs: { id: SortKey; title: string; width: number }[] = [
  { id: "id", title: "ID", width: 60 },
  { id: "name", title: "氏名", width: 110 },
  { id: "department", title: "部署", width: 150 },
  { id: "position", title: "役職", width: 190 },
  { id: "email", title: "メール", width: 190 },
  { id: "salary", title: "年収", width: 110 },
  { id: "joinDate", title: "入社日", width: 110 },
];

export default function GlideDataGridDemo() {
  // 列リサイズ結果を保持する(Glide は列幅を制御コンポーネントとして扱う)
  const [colWidths, setColWidths] = useState<Record<string, number>>(() =>
    Object.fromEntries(columnDefs.map((c) => [c.id, c.width]))
  );
  const [sortKey, setSortKey] = useState<SortKey>("id");
  const [sortAsc, setSortAsc] = useState(true);

  const columns = useMemo<GridColumn[]>(
    () =>
      columnDefs.map((c) => ({
        id: c.id,
        title: c.id === sortKey ? `${c.title} ${sortAsc ? "▲" : "▼"}` : c.title,
        width: colWidths[c.id],
      })),
    [colWidths, sortKey, sortAsc]
  );

  const rows = useMemo(() => {
    const sorted = [...initialData].sort((a, b) => {
      const av = a[sortKey];
      const bv = b[sortKey];
      if (typeof av === "number" && typeof bv === "number") return av - bv;
      return String(av).localeCompare(String(bv), "ja");
    });
    return sortAsc ? sorted : sorted.reverse();
  }, [sortKey, sortAsc]);

  // セルの内容は「座標 → セル」の関数で返す(データを配列で渡さないのが Glide の特徴)
  const getCellContent = useCallback(
    ([col, row]: Item): GridCell => {
      const employee = rows[row];
      const field = columnDefs[col].id;
      const value = employee[field];

      if (field === "salary") {
        return {
          kind: GridCellKind.Number,
          data: employee.salary,
          displayData: `¥${employee.salary.toLocaleString()}`,
          allowOverlay: false,
        };
      }

      return {
        kind: GridCellKind.Text,
        data: String(value),
        displayData: String(value),
        allowOverlay: false,
      };
    },
    [rows]
  );

  const onHeaderClicked = useCallback(
    (col: number) => {
      const field = columnDefs[col].id;
      setSortAsc((prev) => (field === sortKey ? !prev : true));
      setSortKey(field);
    },
    [sortKey]
  );

  const onColumnResize = useCallback((column: GridColumn, newSize: number) => {
    if (column.id === undefined) return;
    setColWidths((prev) => ({ ...prev, [column.id as string]: newSize }));
  }, []);

  return (
    <div>
      <p className="text-sm text-gray-600 mb-3">
        列ヘッダーをクリックでソート、列の境界をドラッグで幅を変更できます。
      </p>
      <div style={{ height: 320 }}>
        <DataEditor
          columns={columns}
          rows={rows.length}
          getCellContent={getCellContent}
          onHeaderClicked={onHeaderClicked}
          onColumnResize={onColumnResize}
          rowMarkers="number"
          smoothScrollX
          smoothScrollY
          width="100%"
          height={320}
        />
      </div>
      <p className="text-xs text-gray-500 mt-2">
        描画は canvas 1枚。表示中の行だけを描くため、行数が数万件に増えても DOM ノードは増えません。
      </p>
    </div>
  );
}

AIプロンプトテンプレート

React + Tailwind CSSで、Glide Data Grid(@glideapps/glide-data-grid)を使ったデータグリッドを実装してください。
- 使用ライブラリ: @glideapps/glide-data-grid の DataEditor(canvas描画)
- SSRできないため next/dynamic の ssr:false で読み込むこと
- データは配列で渡さず、getCellContent([col, row]) で座標からセルを返す方式にすること
- 列ヘッダーをクリックで昇順・降順ソートできること(onHeaderClicked)
- 列の境界をドラッグして幅を変更できること(onColumnResize で幅をstateに保持)
- 数値列は GridCellKind.Number を使い displayData で¥形式に書式化すること
- rowMarkers="number" で行番号を表示すること
- dist/index.css のインポートを忘れないこと

このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

ライブラリ比較表

項目AG GridTanStack TableMUI DataGridGlide Data Gridカスタム実装
インライン編集内蔵(高機能)自作(ロジック支援あり)内蔵(editable prop)内蔵(overlay editor)自作
ソート内蔵プラグイン提供✅ 標準装備自作(onHeaderClicked)自作
フィルタ内蔵(多種)プラグイン提供✅ 標準装備(Free版)自作自作
ページネーション内蔵プラグイン提供✅ 標準装備非対応(全行スクロール)自作
列固定内蔵内蔵⚠️ Pro版のみ内蔵(freezeColumns)自作
仮想スクロール内蔵外部ライブラリ併用✅ 標準装備canvas描画で標準自作(複雑)
バンドルサイズ大きめ(~300KB)小さい(~15KB)⚠️ 中(MUI全体で大)中〜大(canvasエンジン込み)最小
学習コスト中〜高低〜中中〜高低〜高(要件次第)
カスタマイズ性中(テーマ・API)高(ヘッドレス)中(MUIテーマ)中(canvas描画API)最高
おすすめ用途業務アプリ・管理画面独自デザインのテーブルMUI採用プロジェクト数万行の大規模データシンプルな要件

まとめ

AG Grid — 業務アプリや管理画面など、大量データを扱う本格的なデータグリッドが必要な場合に最適。ソート・フィルタ・編集・仮想スクロールがすべて内蔵されている。

TanStack Table — 独自のデザインシステムに合わせたテーブルを構築したい場合におすすめ。ロジックだけを提供するヘッドレス設計で、UIの自由度が高い。

MUI DataGrid — Material UIのデザインシステムと統合されたデータグリッド。ソート・フィルタ・ページネーション・インライン編集がFree版で使え、MUIを採用しているプロジェクトとの親和性が高い。

Glide Data Grid — 数万行規模のデータをスクロールさせたい場合に最適。表全体をcanvasに描画するためDOMノードが増えず描画が軽い。反面セルがDOMに存在しないため、ブラウザ内検索・スクリーンリーダー・E2Eテストの扱いは他ライブラリと変わる。

カスタム実装 — シンプルな要件やプロトタイプに最適。ライブラリ依存がなく、必要な機能だけを実装できる。

表で見せたあとに可視化する

グリッドで数値そのものを確認したあと、傾向・比較・分布をグラフで見せたい場合は、データの可視化カテゴリの比較ページが使えます。

関連する逆引きリファレンス

データグリッド(データテーブル)は、大量のデータを行・列で表示し、ソート・フィルタ・編集などの操作ができるテーブルUI。

管理画面のデータ一覧・スプレッドシート代替・在庫管理・分析ダッシュボードなど、大量のデータを操作する場面で使われる。

主なバリエーション
  • 固定ヘッダー型
  • 仮想スクロール型(大量データ対応)
  • インライン編集型
  • ソート・フィルタ付き型
  • 行選択・一括操作型