Ignite UI for React の IgrSpreadsheet で、セルを右クリックしたときに表示される既定のコンテキストメニューを、任意の項目を持つ独自のメニューに置き換えたい場合は、contextMenuOpening イベントで既定のメニューをキャンセルし、代わりに任意の HTML で作成したメニューを表示します。本記事では例として、選択セルの背景色を変更する項目を持つカスタムメニューを実装します。

IgrSpreadsheet の設定

IgrSpreadsheetcontextMenuOpening イベントハンドラーを設定し、その表示状態・表示位置を保持する要素を別途用意します。

import { useCallback, useRef, useState, type MouseEvent } from "react";
import { IgrSpreadsheetModule } from "igniteui-react-spreadsheet";
import { IgrSpreadsheet, IgrSpreadsheetContextMenuOpeningEventArgs } from "igniteui-react-spreadsheet";

IgrSpreadsheetModule.register();

interface ContextMenuState {
  visible: boolean;
  x: number;
  y: number;
}

function App() {
  const spreadsheetRef = useRef<IgrSpreadsheet>(null);
  const [contextMenu, setContextMenu] = useState<ContextMenuState>({ visible: false, x: 0, y: 0 });

  const contextMenuOpening = useCallback((ss: IgrSpreadsheet, args: IgrSpreadsheetContextMenuOpeningEventArgs): void => {
    // ...
  }, []);

  // 選択されたセルに背景色を設定する(実装は後述)
  const handleColorItemClick = useCallback(() => { /* ... */ }, []);
  // 選択されたセルの背景色をクリアする(実装は後述)
  const handleClearFillItemClick = useCallback(() => { /* ... */ }, []);

  return (
    <>
      <IgrSpreadsheet ref={spreadsheetRef} contextMenuOpening={contextMenuOpening} height="700px" width="900px" />
      <div
        id="context-menu"
        className={contextMenu.visible ? "visible" : undefined}
        style={{ top: contextMenu.y, left: contextMenu.x }}
      >
        <div className="item" onClick={handleColorItemClick}>選択されたセルに背景色を設定</div>
        <div className="item" onClick={handleClearFillItemClick}>選択されたセルの背景色をクリア</div>
      </div>
    </>
  )
}

contextMenuOpening イベントの処理

contextMenuOpening イベントの引数 args.menuArea で右クリックされた領域を判定し、セル領域(値 2)の場合は args.cancel = true を設定して既定のコンテキストメニューをキャンセルします。その上でカスタムメニューの表示状態を有効にし、マウス座標の位置に表示します。

// IgrSpreadsheetContextMenuOpeningEventArgs.menuArea の値: 2 = セル領域(行/列ヘッダーやシートタブ等は対象外)
const CELL_MENU_AREA = 2;

const contextMenuOpening = useCallback((
  ss: IgrSpreadsheet,
  args: IgrSpreadsheetContextMenuOpeningEventArgs
): void => {
  // 規定のコンテキストメニューをキャンセルし、代わりにカスタムのコンテキストメニューを表示する
  if (args.menuArea === CELL_MENU_AREA) {
    args.cancel = true;
    setContextMenu({ visible: true, x: cursorPosition.current.x, y: cursorPosition.current.y });
  } else {
    setContextMenu((menu) => ({ ...menu, visible: false }));
  }
}, []);

選択セルへの背景色の設定

カスタムメニューの項目がクリックされた際は、activeSelection.cellRangesAddress から選択中の範囲を取得し、各セルの cellFormat.fillCellFill オブジェクトを設定することで背景色を変更します。ここで、前述の handleColorItemClick / handleClearFillItemClick の実装を行います。

// 選択セルに設定する背景色
const HIGHLIGHT_FILL = CellFill.createSolidFill("#CCFFFF");

// 選択されている各セルに対して指定した背景色(null の場合はクリア)を設定する
const applyFillToSelectedCells = useCallback((fill: CellFill | null) => {
  const activeWorksheet = spreadsheetRef.current?.activeWorksheet;
  if (activeWorksheet == null) {
    return;
  }

  // cellRangesAddress はカンマ区切りで複数選択範囲を表す(例: "A1:B2,D4")。単一セルは ":" を含まない
  const ranges = spreadsheetRef.current!.activeSelection.cellRangesAddress.split(",");
  ranges.forEach((range) => {
    if (range.includes(":")) {
      const region = activeWorksheet.getRegion(range);
      for (let row = region.firstRow; row <= region.lastRow; row++) {
        for (let column = region.firstColumn; column <= region.lastColumn; column++) {
          activeWorksheet.rows(row).cells(column).cellFormat.fill = fill as CellFill;
        }
      }
    } else {
      activeWorksheet.getCell(range).cellFormat.fill = fill as CellFill;
    }
  });
}, []);

// カスタムメニューの項目がクリックされた際に、選択セルに背景色を設定する
const handleColorItemClick = useCallback(() => {
  applyFillToSelectedCells(HIGHLIGHT_FILL);
  setContextMenu((menu) => ({ ...menu, visible: false }));
}, [applyFillToSelectedCells]);

// カスタムメニューの項目がクリックされた際に、選択セルの背景色をクリアする
const handleClearFillItemClick = useCallback(() => {
  applyFillToSelectedCells(null);
  setContextMenu((menu) => ({ ...menu, visible: false }));
}, [applyFillToSelectedCells]);

実行結果

セルを右クリックすると既定のコンテキストメニューの代わりに独自のメニューが表示され、「選択されたセルに背景色を設定」をクリックすると選択中のセルの背景色が変更されます。「選択されたセルの背景色をクリア」をクリックすると背景色が解除されます。

Tagged: