SpreadsheetGrid props
SpreadsheetGridProps<T> の全 prop と既定値
SpreadsheetGrid props (SpreadsheetGridProps<T>)
| Name | Type | Default | Description |
|---|---|---|---|
rows | readonly T[] | — | clientSide モードの行データ。readonly 配列も受け付ける(グリッドは入力配列を破壊的に変更しない。編集結果は onRowsChange が新配列で返す)。dataSource を指定した場合は無視され serverSide モードになる(両者は排他)。 |
columns | readonly GridColumn<T>[] | (required) | 列定義の配列。readonly 配列も受け付ける。 |
onRowsChange | (nextRows: T[]) => void | — | 行が変化したとき呼ばれる(rows を controlled にする)。 |
dataSource | ServerSideDataSource<T> | — | serverSide(SSRM)モードのデータ供給口。指定すると可視窓近傍のブロックだけを getRows で都度取得し、rows 系の clientSide パイプラインをバイパスする。updateRows(任意)を持たせるとセル編集の書き戻し(楽観更新つき)が有効になる(「セル編集の書き戻し」節)。 |
serverSideRefreshToken | number | — | serverSide のソフトリフレッシュ用トークン。値を増やすと、クエリ(フィルター/ソート/グローバル)を変えずにキャッシュを破棄して現在の可視レンジをサーバから取り直す。スクロール位置は保持し、件数は到着ブロックの totalRowCount で追従する(clientSide では無視)。命令的に呼びたい場合は同挙動のハンドル refreshServerSide() を使う。 |
onServerSideLoadError | (error, params) => void | — | serverSide の getRows が reject したときの通知(abort は正常キャンセルのため通知しない)。params は失敗した要求の view 空間レンジ { startIndex, endIndex }。グリッド内蔵のエラーバー(再試行 UI)とは独立に呼ばれる(利用側トースト / ログ用)。インライン関数可(latest-ref 経由で読む)。 |
onServerSideWriteError | (error, params) => void | — | serverSide の dataSource.updateRows が reject したときの通知。グリッド側は楽観更新をロールバック済みで、params.updates に失敗した行更新(rowKey / changes / previousRow)が入る(利用側トースト / リトライ導線用)。グリッド内蔵の保存失敗バーとは独立に呼ばれる。インライン関数可(latest-ref 経由で読む)。詳細は「セル編集の書き戻し」節。 |
onColumnsChange | (nextColumns: GridColumn<T>[]) => void | — | 列が変化したとき呼ばれる。列メニューの固定切替はこれが指定されている場合のみ反映。 |
rowKeyGetter | (row: T, index: number) => GridRowKey | index ベース | 安定した行キーを返す。 |
createRow | () => T | — | 行追加時に使う新規行ファクトリ。 |
createOverflowColumn | (columnIndex: number) => GridColumn<T> | — | 列追加時に使う列ファクトリ。 |
rowHeight | number | density 依存(standard: 36) | uniform 行の行高(px)。未指定時は density プリセット(compact: 28 / comfortable: 44)から解決。明示指定が常に優先(THEME-2)。 |
autoHeight | boolean | false | auto-height 行(可変行高)を有効化する大本のスイッチ。これに加えて少なくとも1列に column.autoHeight: true が必要(その列が折り返して行高を駆動)。両方 true かつ行数 ≤ 50,000のとき有効(超過時は uniform rowHeight へフォールバック)。詳細は「auto-height 行」節。 |
estimateRowHeight | number | rowHeight | 未測定行の推定行高(px)。 |
headerHeight | number | density 依存(standard: 40) | ヘッダー行の高さ(px)。未指定時は density プリセット(compact: 32 / comfortable: 48)から解決。明示指定が常に優先(THEME-2)。 |
density | 'compact' | 'standard' | 'comfortable' | 'standard' | 密度プリセット(THEME-2)。rowHeight / headerHeight の既定値と寸法トークン(セル横 padding / バー padding / アイコンボタン寸法 / セル文字の相対拡縮)を一括切替。'standard' は従来と同値。個別調整はトークン(--ssg-cell-pad-x 等)の上書きで可能。popover / menu 等のポータルは対象外。 |
theme | 'light' | 'dark' | 'auto' | 'light' | カラーテーマ(TH-DK-2)。'dark' でダークプリセット(.ssg-theme-dark のトークン一括上書き。Mantine dark 系パレット)をグリッド本体・全ポータル(popover / menu / panel)・ドラッグゴースト・ツールチップへ適用。'auto' は prefers-color-scheme へ追従(Mantine / HeroUI 等クラスベース dark 運用では、利用側カラースキームの解決値を 'light' | 'dark' で渡す使い方を推奨)。個別の色調整はトークン(--ssg-*)の上書きで可能。 |
rowHeaderWidth | number | 56 | 行番号列の幅(px)。 |
height | number | string | — | スクロールコンテナの明示高さ。'100%' で親要素に追従(親要素が確定高さを持つ前提。祖先まで高さが確定している/flex 子なら min-height: 0 が必要)。number は px。未指定時は maxHeight のクリップ挙動になる。 |
maxHeight | number | string | —(既定 480px) | スクロールコンテナの高さ上限。height・maxHeight が共に未指定のときのみ既定の 480px が効く(従来挙動)。height と併用すると「明示高さ+上限」。 |
readOnly | boolean | false | グリッド全体の編集を無効化。 |
dimReadOnlyCells | boolean | false | readonly セルの組み込み淡色表示(背景 + 文字色)を有効化(THEME-3)。false でもセマンティッククラス .ssg-body-cell--readonly は常時付与され、利用側 CSS のフックに使える。 |
canEditCell | (rowIndex, colIndex, row, column) => boolean | — | セル単位の編集可否ゲート。 |
enableUndoRedo | boolean | true | グリッド編集(セル編集 / ペースト / renderCell の setValue)の取り消し/やり直し。Ctrl/Cmd+Z = undo、Ctrl/Cmd+Shift+Z / Ctrl/Cmd+Y = redo(ハンドルの undo() / redo() でも可)。clientSide(rows + onRowsChange)専用で、serverSide(dataSource)/ readOnly / onRowsChange 未指定時は無効。履歴は「変更前 rows 配列」の参照スナップショット(未変更行は構造共有されるため低コスト)。onRowsChange で受け取った配列は参照そのまま rows へ戻すのが前提(map 等で作り直すと毎回「外部変更」と見なされ履歴が消える)。rows が grid 起点以外(親の直接 setState 等)で差し替わると履歴は自動破棄。エディタ内の文字入力の取り消しは input のネイティブ undo に委譲(グリッドの undo は確定済みの編集が対象)。 |
undoHistoryLimit | number | 100 | 保持する undo ステップ数の上限。超過分は古い順に破棄。 |
enableClearOnDelete | boolean | true | Delete / Backspace キーによる選択セル(なければアクティブセル)の値クリア。false でキーは何もしない(素通し)。ペースト・エディタでの上書き・undo/redo には影響しない(クリアのキーボード操作だけの opt-out)。 |
editorEnterMove | EditorEnterMove | 'down' | 組み込みエディタ(text / number / select / date)の Enter 確定後にアクティブセルをどこへ移すか('down' | 'up' | 'right' | 'left' | 'none'。Excel の「Enter キーを押したら、セルを移動する(方向)」相当)。'none' は移動せずその場に留まる。Tab / Shift+Tab(右 / 左)と Escape には影響しない。custom エディタはキーバインドが consumer 責務のため対象外(ctx.commit(value, direction) の direction で指定)。 |
onUndoRedoStateChange | (state: { canUndo, canRedo }) => void | — | undo / redo 可能状態が変化したときに呼ばれる(ツールバーの undo/redo ボタンの disabled 表示などリアクティブな UI 用)。初回マウントでは発火せず、同値では再発火しない。毎レンダーのインライン関数でも問題ない。 |
enableRangeSelection | boolean | true | 複数セル範囲選択。 |
enableRowSelection | boolean | false | チェックボックス行選択の有効化(マスタースイッチ)。true で行ヘッダ(行NO)ガターが行選択のヒット領域になり、Excel 風のガター起点セル範囲選択は off(ボディ側セルのドラッグ範囲選択は不変)。判定は O(1)・全選択は除外集合でキーを列挙しない(1M 行でも一定コスト)。 |
rowSelectionMode | 'single' | 'multiple' | 'multiple' | 単一/複数の選択モード。single は常に 1 行。multiple はクリックでトグル、shift+クリック/ガタードラッグで範囲選択。 |
enableSelectAllRows | boolean | enableRowSelection && multiple | ヘッダ左上コーナーの全選択チェック(tri-state: none/some/all)の有効化。 |
rowSelection | RowSelectionModel | — | controlled の行選択記述子。{ type:'include', rowKeys }=これらを選択 / { type:'exclude', rowKeys }=全選択のうち除外。全選択をキー列挙せず表現できる。指定時は controlled(内部 state を使わない)。 |
selectedRowKeys | GridRowKey[] | — | controlled 簡易版({ type:'include', rowKeys } の糖衣)。rowSelection と併用時は rowSelection を優先。全選択(exclude)は表現不可。 |
onRowSelectionChange | (model: RowSelectionModel) => void | — | 行選択変化の通知(controlled/uncontrolled いずれでも発火)。 |
enableGlobalFilter | boolean | true | グローバルフィルター機能の有効化。false で機能が無効になり、既定トップバーのフィルター入力欄も出ない(summary は showTopBarSummary に従う。トップバー自体を消すには showTopBar=false)。 |
enableColumnFilter | boolean | true | 列ごとのフィルター。 |
renderFilterDateInput | (ctx: FilterDateInputContext) => ReactNode | 内製の日付フィールド | dateSet フィルター条件の日付入力を利用側コンポーネント(Mantine DatePickerInput 等)へ差し替えるスロット。既定は内製フィールド(自由入力 + ドリルアップカレンダー)。値契約と外側クリック対策はソートとフィルターを参照。 |
enableSorting | boolean | true | ヘッダークリックでのソート。 |
enableColumnResize | boolean | true | 列幅の手動リサイズ可否のグリッド既定。各列 resizable 未指定時に継承(column.resizable ?? enableColumnResize)。 |
autoSizeColumns | 'onMount' | 'onDataChange' | false | false | データ投入時に全列幅を内容へ自動フィット。'onMount'=初回にデータが載った一度きり / 'onDataChange'=rows(参照)が変わるたび(= データ差し替えのたび。手動リサイズは上書き) / false=無効。計測は列メニュー「すべての列の幅を自動調整」と同一エンジン(suppressAutoSize / autoHeight 列は除外)。フィルター / ソート / 列並べ替えでは再フィットしません。serverSide(dataSource)では無効。詳細は「flex と autoSize」節。 |
showCellOverflowTooltip | boolean | false | セル内容が省略(…)される列で、ホバー時に全文ツールチップを表示。対象は既定テキストセルのみ(renderCell 列 / autoHeight 折り返し列は対象外)。表示はホバー時に scrollWidth > clientWidth を判定し、実際にクリップされているセルのみ。既存のカスタムツールチップ(data-ssg-tooltip)を共有。詳細は「ツールチップ」節。 |
showValidationMarks | boolean | true | invalid マーク(背景 + 右上マーカー + ホバーツールチップ)の表示可否。false で非表示 + 可視セルの validate 評価スキップ。getInvalidCells() と validationMode: 'reject' の書き込み拒否には影響しない(独立経路)。「送信時にだけマークを出す」UX は利用側 state で本 prop を切り替えて実現(「バリデーション」節のレシピ参照)。 |
enableColumnMenu | boolean | true | 列メニュー(⋮ + ヘッダー右クリック)。 |
enableRowHover | boolean | true | 行ホバー時に行全体を薄くハイライト。 |
enableColumnHeaderHover | boolean | true | 列ヘッダーのホバー時にヘッダーセルを薄くハイライト。 |
noMatchingRowsText | string | '一致する行がありません' | フィルター結果 0 行時のオーバーレイ文言。 |
noRowsText | string | '表示する行がありません' | rows が 0 件のときの文言。 |
showTopBar | boolean | true | 上部バー(ツールバー)の表示有無。false で renderTopBar / enableGlobalFilter に関わらず一切描画しない(表示のマスタースイッチ。矛盾指定時は renderTopBar より優先)。 |
showTopBarSummary | boolean | true | 既定トップバーの summary chips(件数/フィルター/ソート)の表示有無。renderTopBar 未指定時のみ有効。これと showTopBarFilter がともに非表示なら既定トップバーは描画されない(空バーを出さない)。 |
showTopBarCounts | boolean | true | 既定トップバーの Rows / Columns 件数 chips の表示有無。showTopBarSummary=true(かつ renderTopBar 未指定)のときのみ有効。Filter / Sort chips は対象外。 |
showTopBarFilter | boolean | true | 既定トップバーのグローバルフィルター入力欄の表示有無。renderTopBar 未指定時のみ有効。enableGlobalFilter=false のときは本値に関わらず非表示。 |
globalFilterPlaceholder | string | 'グローバルフィルター' | 既定トップバーのグローバルフィルター入力の placeholder。renderTopBar 未指定時のみ有効。 |
globalFilterIcon | ReactNode | 組み込み検索アイコン | 既定トップバーのグローバルフィルター入力の左アイコン。renderTopBar 未指定時のみ有効。undefined=組み込みの検索(虫眼鏡)アイコン / null(など falsy)=アイコン無し / 任意 ReactNode=差し替え。クリアボタンは入力枠の内側右に × で表示され、入力が空のときは出ない。 |
showBottomBar | boolean | true | 下部バー(ステータスバー)の表示有無。false で renderBottomBar に関わらず一切描画しない(表示のマスタースイッチ。矛盾指定時は renderBottomBar より優先)。 |
showBottomBarCounts | boolean | true | 既定ボトムバーの Rows / Columns 件数 chips(左側)の表示有無。renderBottomBar 未指定時のみ有効。右側の Active / Selection / 選択統計 / Cols は対象外。 |
showFilterChipBar | boolean | false | フィルターチップバー(適用中の列フィルターをトップバー直下にチップで常時表示)の表示有無(opt-in)。有効フィルター 0 件時はバーごと非表示(空バーは出さない)。showTopBar とは独立。チップ本体クリックで対象列へジャンプしてフィルター popover を開き、× で個別クリア、「すべてクリア」は列フィルターのみ対象(グローバルフィルターは対象外)。 |
renderTopBar | (ctx: SpreadsheetGridSlotContext<T>) => ReactNode | 内蔵トップバー | 上部バーの差し替え。未指定時は内蔵トップバー(summary chips + フィルター入力。内訳は showTopBarSummary / showTopBarFilter で制御。フィルター入力は enableGlobalFilter=true が前提)。showTopBar=false 時は本指定に関わらず描画されない。 |
renderBottomBar | (ctx: SpreadsheetGridSlotContext<T>) => ReactNode | 内蔵ボトムバー | 下部バーの差し替え。未指定時は内蔵ステータスバー。showBottomBar=false 時は本指定に関わらず描画されない。 |
className | string | — | ルート要素の class。 |
classNames | GridClassNames | — | パーツ別の追加 class スロット。現状 root / iconButton / bodyCell / bodyRow が配線済み(他は順次)。基底 class は未レイヤー・特異度 (0,1,0)(THEME-1)。確実な上書きは連結セレクタ(例: .ssg-root.my-theme)を推奨。Tailwind v4 は style.layer.css も利用可。 |
getRowClassName | (row: T, rowIndex: number, ctx: RowStyleContext<T>) => string | undefined | — | 行ごとの追加 class。行コンテナ + 各データセルに付与され、Tailwind 等での行ハイライトに使える。行ヘッダー「#」セルは現状対象外。第 3 引数 ctx は { row, rowIndex, sourceRowIndex, rowKey, isSelected }(「補助型」節参照)。既存の 2 引数関数もそのまま動く(後方互換)。 |
onStateChange | (state: GridState) => void | — | 永続スライス(手動リサイズ幅 / フィルター / ソート)が実際に変化したときに最新 GridState を渡して呼ばれる。保存タイミングの signal(例: localStorage 自動保存)。発火規約は「状態の保存 / 復元」節を参照。 |
onScroll | (params: GridScrollEventParams) => void | — | スクロール位置の変化通知(rAF で 1 フレーム 1 回に間引き・縦横どちらの変化でも発火)。params は { top, left, source }(px)。source: 'api' は setScrollPosition / scrollTo* 系由来、'user' はそれ以外。2 グリッドの双方向スクロール同期は source === 'user' のときだけ相手へ反映することでループを止められる。インライン関数可(latest-ref 経由)。 |
enableContextMenu | boolean | false | コンテキストメニュー機能の有効化(マスタースイッチ)。他機能の enable* と同じく既定 OFF。false のあいだは getContextMenuItems を渡しても発火せず、右クリックはブラウザ標準メニューのまま。現状はまだ機能 / UI に改善余地があるため既定 OFF で提供する(利用側で明示 opt-in)。 |
getContextMenuItems | (params: GridContextMenuParams<T>) => GridContextMenuItem[] | — | セル/行の完全カスタムコンテキストメニュー。右クリック時のみ呼ばれ、返した項目でメニューを描画する(ライブラリは固定の既定項目を持たない)。opt-in は enableContextMenu={true} かつ本コールバックの指定の両方。未指定、または [] を返したときはブラウザ標準の右クリックメニューへフォールスルー(空パネルは出さない)。SSRM 未ロード行では開かない。ヘッダー右クリックは列メニュー(enableColumnMenu)が担当し、本メニューはボディ(セル / 行NO ガター)専用。詳細は「コンテキストメニュー」節を参照。 |
onContextMenuOpen | (params: GridContextMenuParams<T>) => void | — | コンテキストメニューが実際に開いた直後の通知(項目が 1 件以上あり表示された場合のみ)。 |
scrollHint | boolean | ScrollHintOptions<T> | —(無効) | スクロール位置インジケーター。スクロール中にスクロールバー脇へ行番号バブル(「行 N / 総行数」+ 任意の列値)と行目盛りルーラーを表示し、スクロールバー帯のホバーで「行 N へ」のジャンプ先プレビューを出す。true は全既定({ bubble: true, ruler: true, scrollbar: true, trigger: 'scroll', minRows: 0 })と同義。minRows で「表示行数がしきい値以上のときだけ有効」のデータ量ゲートも掛けられる。表示は総行数とスクロール位置のみで駆動されるため clientSide / SSRM の全構成で動作。オーバーレイは pointer-events: none で既存操作へ一切干渉しない。詳細は「スクロール位置インジケーター」節とガイドを参照。 |
detailRow | DetailRowOptions<T> | —(無効) | 展開行(Master/Detail)。マスター行の直下に、行順(view index)を変えずに全幅の帯を差し込み、その中(カード)へ render の返す任意の React 要素(自前のサブグリッド / フォーム / 集計パネル等)を描画する。指定時のみ有効で、未指定なら既存の描画・状態・イベント経路は一切変わらない。{ render, height?, isExpandable?, showToggleColumn?, className? }。clientSide / serverSide の両方で使える(serverSide の制約は節内)。詳細は「展開行(Master/Detail)」節とガイドを参照。 |
onExpandedDetailRowKeysChange | (keys: GridRowKey[]) => void | — | 展開中の展開行のマスター行キー集合が変化したときに呼ばれる(開閉の永続化・外部同期用)。初回マウントでは発火しない。インライン関数可(latest-ref 経由)。 |
enableRowDrag | boolean | false | 行ドラッグ並び替え。先頭にドラッグハンドル列(幅 28px・タイトル無しの合成列。左固定列があれば左固定側)を挿入し、ハンドル(⋮⋮)を掴んで行を上下へ動かせる。確定時は onRowsChange へ移動後の新配列を渡し(履歴ラッパ経由 = undo/redo 対象)、続けて onRowMove を呼ぶ。clientSide(rows + onRowsChange)専用で、dataSource(serverSide)/ 行グルーピング中 / onRowsChange 未指定ではハンドル列を出さない。ソート / フィルター適用中はハンドルを淡色 + 理由ツールチップにして操作を無効化する(列は残る)。詳細は「行ドラッグ並び替え」節とガイド。 |
isRowDraggable | (row: T, ctx: RowDragContext) => boolean | 全行可 | 行ごとのドラッグ可否。false の行にはハンドルを描画しない。ctx = { rowKey, sourceRowIndex }。 |
onRowMove | (params: RowMoveParams<T>) => void | — | 行移動の確定後(onRowsChange の直後)に呼ばれる。params = { rowKey, fromIndex, toIndex, rows }(index は元 rows 配列基準、rows は onRowsChange と同じ新配列参照)。ハンドルの moveRow() による移動でも呼ばれる。 |
バーの表示制御(top / bottom)
トップ / ボトムバーは次の優先順で解決される。
showTopBar/showBottomBar(マスタースイッチ):falseならそのバーは一切描画されない(render*/enable*より優先)。renderTopBar/renderBottomBar(カスタム): 指定時はそのまま描画。トップバーの内訳 props(showTopBarSummary/showTopBarFilter)はカスタム側が中身を決めるため関与しない。- 既定バー:
render*未指定時のフォールバック。
既定トップバーは summary chips(左) と グローバルフィルター入力(右) の 2 パートからなり、独立に出し分けできる。
| やりたいこと | 設定 |
|---|---|
| バーごと消す | showTopBar={false} |
| summary だけ(フィルター入力なし) | showTopBarFilter={false} |
| フィルター入力だけ(summary なし) | showTopBarSummary={false} |
| トップの Rows/Columns 件数だけ消す | showTopBarCounts={false} |
| ボトムの Rows/Columns 件数だけ消す | showBottomBarCounts={false} |
| 適用中の列フィルターをチップで常時表示 | showFilterChipBar(既定 OFF) |
| フィルター機能ごと無効 + summary は残す | enableGlobalFilter={false} |
| 完全に自前のバー | renderTopBar={(ctx) => …} |
showTopBarSummary と showTopBarFilter(実効は showTopBarFilter && enableGlobalFilter)がともに false の場合、既定トップバーは描画されない(空バーを出さない)。
ボトムバーは Rows / Columns 件数のみ showBottomBarCounts で出し分けできる(右側の Active / Selection / 選択統計 / Cols は対象外)。それ以外の内訳を変えたい場合は renderBottomBar を使う。
コンテキストメニュー(enableContextMenu / getContextMenuItems)
セル / 行の右クリックで開く完全カスタムメニュー。既定は OFF(enableContextMenu={false})で、他機能の enable* と同じくマスタースイッチで有効化する(現状はまだ機能 / UI に改善余地があるため既定 OFF)。ライブラリは固定項目を一切持たず、getContextMenuItems が返した項目配列だけを描画する。用意されるのは「窓」(パネル外装 + 右クリック座標配置 + 開閉 / Escape / 外側クリック / スクロール close)だけで、中身(ラベル / アイコン / onSelect)はすべて利用側が渡す。列メニューと同じ .ssg-menu-panel / .ssg-menu-item 外装を再利用する。
opt-in と標準メニューへのフォールスルー
enableContextMenu未設定 /false(既定)→ ブラウザ標準の右クリックメニュー(getContextMenuItemsを渡していても発火しない)。enableContextMenu={true}かつgetContextMenuItemsが項目を返した → その項目が並んだメニューを右クリック座標に表示。enableContextMenu={true}でもgetContextMenuItems未指定 /[]を返した(対象で項目なし)→ ブラウザ標準メニュー(空パネルは浮かせない)。
対象と挙動
- ヘッダー右クリックは対象外(列メニュー
enableColumnMenuが担当)。本メニューはボディの データセルと 行NO ガター専用。 - SSRM 未ロード行(まだ取得できていない行)の上では開かない(標準メニューになる)。
- uncontrolled のみ。右クリックしてもセル選択は変化しない(対象セル/行の情報は
paramsで受け取る)。 - close 契機: 項目選択 / 外側クリック / Escape / スクロール。項目間のキーボード移動(矢印キー)は持たない。
GridContextMenuParams<T>(コールバック引数)
| フィールド | 型 | 説明 |
|---|---|---|
target | GridContextMenuTarget<T> | 右クリック対象。{ type:'cell', rowIndex, colIndex, rowKey, row, column, value } か { type:'rowHeader', rowIndex, rowKey, row }(行NO ガター)。rowIndex はビュー行 index、colIndex は論理列 index(視覚順 左→中央→右 = handle.selectCell と同一空間)。 |
clientX / clientY | number | 右クリックのビューポート座標(メニュー配置に使用済み。分岐の判断材料にも)。 |
selection | GridSelection | 現在のセル範囲選択。チェックボックス行選択は handle.getRowSelection() で別途取得。 |
activeCell | CellCoord | null | 現在のアクティブセル。 |
isTargetSelected | boolean | 対象(cell はそのセル / rowHeader はその行)が selection に含まれるか。「選択範囲への操作」か「単一対象への操作」かを分岐する簡便値。 |
GridContextMenuItem(判別共用体)
- action(既定):
{ kind?: 'action'; id?; label: ReactNode; icon?: ReactNode; disabled?; danger?; onSelect: () => void }— クリックでonSelect実行後に自動で閉じる。icon省略時も左 14px 枠が空スペーサになりラベル左端が揃う。dangerは削除など危険操作の赤系強調(Mantine のcolor="red"相当)。 - label(見出し):
{ kind: 'label'; id?; label: ReactNode }— 非インタラクティブなセクション見出し(Mantine のMenu.Label相当)。項目群のグルーピング表示に使う。 - separator:
{ kind: 'separator'; id? }— 区切り線(Mantine のMenu.Divider相当)。 - custom(レンダラ / エスケープハッチ):
{ kind: 'custom'; id?; render: (ctx: { close: () => void }) => ReactNode }— パネル内に任意 JSX を差し込む。close()で任意タイミングに閉じられる。
id は React key 用(省略時は配列 index)。項目は非ジェネリック: 行データは getContextMenuItems 内で params を通じてクロージャに閉じ込める。
例
<SpreadsheetGrid
// …
enableContextMenu // 既定 false。機能を使うにはこのマスタースイッチが必要
getContextMenuItems={(params) => {
const items: GridContextMenuItem[] = [];
items.push({ kind: 'label', label: '操作' }); // セクション見出し
if (params.target.type === 'cell') {
const value = params.target.value; // narrowing はローカルへ退避してから onSelect で使う
items.push({
label: '値をコピー',
icon: '📋',
onSelect: () => navigator.clipboard?.writeText(String(value ?? '')),
});
}
items.push({ kind: 'separator' });
items.push({
label: 'この行を削除',
danger: true, // 赤系強調
onSelect: () => deleteRow(params.target.rowKey),
});
items.push({ kind: 'separator' });
items.push({
label: '選択範囲を CSV 出力',
disabled: !params.isTargetSelected,
onSelect: () => gridRef.current?.downloadCsv('selection.csv', { scope: 'selection' }),
});
items.push({
kind: 'custom',
render: ({ close }) => (
<div style={{ padding: '6px 8px' }}>
行 {params.target.rowIndex}
<button type="button" onClick={close}>閉じる</button>
</div>
),
});
return items; // [] を返すと標準メニューへフォールスルー
}}
onContextMenuOpen={(params) => console.log('opened at', params.target)}
/>レシピ: 右クリックからフィルター管理パネルを開く
適用中フィルターの一覧 / 編集 / クリアを行うフィルター管理パネル(列メニュー「フィルターを管理…」と同じもの)は、ハンドルの openFilterManager() で任意の場所から開ける。コンテキストメニューに載せる場合:
const gridRef = useRef<SpreadsheetGridHandle<Row>>(null);
<SpreadsheetGrid
ref={gridRef}
enableContextMenu
getContextMenuItems={() => [
{
label: 'フィルターを管理…',
onSelect: () => gridRef.current?.openFilterManager(),
},
]}
/>;スクロール位置インジケーター(scrollHint)
大量行(特に 100 万行級)ではスクロールバー 1px の移動が数百〜数千行に相当し、移動中に「今どの行にいるか」を見失う。scrollHint は次の 3 つのオーバーレイでこれに答える(既定は完全無効。opt-in)。
- 行番号バブル(
bubble): スクロール中、スクロールバー脇(サム位置)に「行 N / 総行数」を表示。hintColumn/renderHintで任意の列値を添えられる。 - 行目盛りルーラー(
ruler): ボディ右端に行数スケール(切りのよい刻み。刻みが 1 万の倍数のときは「10万」等の圧縮表記へ全目盛り統一)。スクロール中とスクロールバー帯ホバー中のみフェード表示。 - ジャンプ先プレビュー(
rulerに含む): スクロールバー帯(右端)にポインタを置くと、スクロールバーのクリック / ドラッグと同じサム中心写像で「行 N へ」の点線 + ラベルを表示。 - カスタムスクロールバー(
scrollbar): ヘッダー下から始まる専用ガターに常時表示のトラック + 最小 30px サムを自前描画(ドラッグ / クリックジャンプ / ホイール対応)。macOS のオーバーレイスクロールバーは自動で消え、大量行ではサムが極小になり「掴む場所」を見失うための置き換え。有効時はネイティブ縦スクロールバーを非表示化する(Chromium / WebKit。Firefox はネイティブ縦バーが残るがガター操作は有効)。
ScrollHintOptions<T>:
| 項目 | 型 | 既定 | 説明 |
|---|---|---|---|
bubble | boolean | true | 行番号バブルの表示。 |
ruler | boolean | true | ルーラー + ジャンプ先プレビューの表示。 |
scrollbar | boolean | true | カスタム縦スクロールバー(専用ガター・常時表示)。false でネイティブバーのまま(バブル等は疑似サム位置に表示)。 |
trigger | 'scroll' | 'hover' | 'always' | 'scroll' | バブル / ルーラーの表示トリガー(スクロールバー自体は常時表示)。'scroll' = スクロール中のみ(停止約 1 秒でフェードアウト)/ 'hover' = グリッドホバー中 + スクロール中 / 'always' = 常時。 |
minRows | number | 0 | データ量ゲート。表示行数(フィルター / グルーピング適用後のビュー行数。SSRM はサーバー総行数)がこの値未満のあいだ、scrollHint 全体(カスタムスクロールバー含む)を自動 OFF にしてネイティブスクロールバー表示のままにする。0 = 常時有効(従来挙動)。 |
hintColumn | string | — | 行番号に添えて表示する列 key(= 行オブジェクトのフィールド名)。 |
renderHint | ({ rowIndex, rowData }) => ReactNode | — | 表示内容の完全カスタム(hintColumn より優先)。null / undefined を返すと行番号のみの既定表示。 |
// 簡易: 行番号 + 品番
<SpreadsheetGrid scrollHint={{ hintColumn: 'partNo' }} />
// 完全カスタム: 品番 — 品名
<SpreadsheetGrid
scrollHint={{
trigger: 'scroll',
renderHint: ({ rowData }) =>
rowData ? `${rowData.partNo} — ${rowData.partName}` : null,
}}
/>データ量ゲート(minRows): 小規模データではバブル / ルーラー / カスタムスクロールバーがノイズになるため、minRows を指定すると「表示行数がしきい値以上のときだけ出る」挙動にできる(例: scrollHint={{ minRows: 100 }})。判定はバブルの「/ 総行数」と同じ行数(フィルター / グルーピング適用後のビュー行数。SSRM はサーバー総行数)で、フィルターで絞り込んでしきい値を割ればその間は自動 OFF になる。既定は 0(常時有効)。注意: scrollbar 有効時はしきい値またぎでガター余白が付け外しされるため、行数が変動する画面では僅かなレイアウトシフトが起きる(気になる場合は scrollbar: false と併用する)。
SSRM / グルーピングでの挙動: バブル / プレビューの対象行が SSRM の未ロード行(またはグルーピングのグループ行)の場合、rowData は undefined になり、hintColumn 指定時は自動的に行番号のみへフォールバックする(renderHint には undefined がそのまま渡るので利用側で分岐する)。総行数とスクロール位置だけで駆動されるため、インジケーター自体は SSRM を含む全構成で動作する。
実装ノート: 行番号は仮想化の縦ジオメトリ(1M 行の pixel scaling / auto-height の prefix-sum)と同一の写像で解決するため常に正確。バブル / ルーラー / プレビューのオーバーレイは pointer-events: none で、スクロール・セル操作へ一切干渉しない。カスタムスクロールバーのガターのみポインタ操作を受けるが、コンテンツのスクロール自体はネイティブのまま(ガターは scrollTop を書くだけの鏡映し)。全行が viewport に収まりスクロール不能のときは何も表示しない。配色はテーマトークン(--ssg-pill-* / --ssg-scrollhint-*)で light / dark 両対応。
展開行(Master/Detail)(detailRow)
detailRow prop を渡すと、各行を「展開」してマスター行の直下に消費側 UI(カード)を差し込めます(AG Grid の Master/Detail 相当)。行グルーピングが「ライブラリがグループ行を作る」機能なのに対し、展開行は「行の下に何を出すかを利用側が全面的に決める」動線です。両者は独立で併用もできます。
<SpreadsheetGrid
rows={rows}
columns={columns}
rowKeyGetter={(row) => row.id}
detailRow={{
height: 220,
isExpandable: (row) => row.lines.length > 0,
render: ({ row, rowKey, collapse }) => (
<OrderLinesPanel order={row} onClose={collapse} />
),
}}
onExpandedDetailRowKeysChange={(keys) => save(keys)}
/>DetailRowOptions<T>:
| Name | Type | Default | Description |
|---|---|---|---|
render | (ctx: DetailRowRenderContext<T>) => ReactNode | (required) | 展開行の中身。ctx = { row, rowKey, rowIndex, sourceRowIndex, collapse }(rowIndex はビュー行 index、collapse() はその展開行を閉じる)。帯の内側のカード要素(.ssg-detail-card、data-ssg-detail 属性つき)に描画される。 |
height | number | 200 | 帯の高さ(px)。固定高で、中身が超えるとカード内でスクロールする(auto 高は非対応)。 |
isExpandable | (row: T, ctx: { rowKey; sourceRowIndex }) => boolean | 全行展開可 | 行ごとの展開可否。false の行はトグルが描画されず、命令的 API / ctx.detail.toggle() からの展開も no-op。 |
showToggleColumn | boolean | true | 専用トグル列(幅 28px・タイトル無し、行ヘッダーの右隣 = 先頭列。左固定列があるときは左固定側)を自動挿入する。false にすると列は挿入されず、任意の列の renderCell から ctx.detail.toggle() でトグルを自前配置する(下記)。 |
className | string | — | カード要素へ追加する class。 |
- 表示: 帯はマスター行の直下・グリッド全幅(3 ペインとも背景を描画)で、カードは中央ペインに
position: stickyで置かれ、横スクロールしてもビューポート左端(左固定ペインの右隣)に留まります。幅は中央ペインの可視幅です。展開しても行の順序・view index は変わらず(第 3 の行種は作らない)、後続行が帯の高さぶん下がります。展開時にスクロール位置は動かしません。 - 状態: 展開状態は
rowKeyGetterの行キーで保持されるため、ソート / フィルター / 行の追加削除を跨いで同じ行に追従します(フィルターで除外中の行は帯が出ず、復帰すると再表示)。UI 状態で undo/redo・getState()の対象外。永続化はonExpandedDetailRowKeysChange+setDetailRowExpanded()で行います。 - マウント: カードは仮想化の描画窓に載っている間だけマウントされます(スクロールアウトでアンマウント、戻ると再マウント)。カード内で保持したい状態は消費側で
rowKeyをキーに持ってください。 - イベント境界: カード内のキーボード / クリップボード / 右クリック / ダブルクリック / ドラッグ開始はグリッド本体へ伝播しません(カード内の input で矢印キーを押してもアクティブセルは動かない)。カード内にフォーカスがある間は、グリッド側のフォーカス復帰(編集確定・popover close 後)がフォーカスを奪いません。カード内に別の
SpreadsheetGridをネストしても、外側の自動高さ実測 / 列ヘッダー検索は内側のセルを対象にしません。 renderCellからの操作(ctx.detail):detailRow有効時、CellRenderContextにdetail: { expanded, expandable, toggle(), setExpanded(bool) }が入ります(無効時はundefined)。showToggleColumn: falseと組み合わせて、商品名セルの横などにトグルを自前配置できます。- 選択 / アクティブセル: 帯はセルではないため選択・アクティブセルの対象外です。帯を跨ぐ範囲選択のハイライトは帯を避けて分割描画されます。
- serverSide(
dataSource): 使えますが、(1) クエリ(フィルター / ソート / グローバル)が変わると展開状態はすべて閉じます(結果セットが総入れ替えされ、未ロード行のキーを走査できないため)。(2) 未ロード行の帯は表示されません。 - 上限: 帯は auto-height 行と同じ可変行高ジオメトリで描画するため、
rows × rowHeight + 展開中の帯の合計が 15,000,000px(36px 行で約 41 万行)を超える構成では帯を描画しません(開発時警告。展開状態は保持され、行数を絞ると表示されます)。 - 行グルーピング併用: 展開できるのは leaf 行のみ(グループ行は対象外)。
行ドラッグ並び替え(enableRowDrag)
enableRowDrag を付けると、先頭のハンドル列(⋮⋮)を掴んで行を上下へ動かせます(AG Grid の managed row dragging 相当)。並び替えの結果は通常の編集と同じく onRowsChange で新配列として返るため、消費側は rows を差し替えるだけです。
const [rows, setRows] = useState(initialRows);
<SpreadsheetGrid
rows={rows}
onRowsChange={setRows}
columns={columns}
rowKeyGetter={(row) => row.id}
enableRowDrag
isRowDraggable={(row) => !row.locked}
onRowMove={({ rowKey, fromIndex, toIndex, rows }) => saveOrder(rows)}
/>- 操作: ハンドルを押して上下へドラッグすると、挿入位置に水平のガイド線が出ます。ドロップで確定し、影響行が新しい位置へスライドします(
prefers-reduced-motionでは即時)。グリッドの枠外で離す /Escapeでキャンセルします。上下端に近づくと自動スクロールします(仮想化された画面外の行へも運べます)。 - ゴースト: ドラッグ中はポインタ追従のピルに、先頭の(合成列でない)表示列の表示値(
valueFormatter適用後)を出します。空なら「行 N」。 - データ契約: 確定時は「1 要素を移動した新配列」(未変更行は参照共有)を
onRowsChangeに渡し、その直後にonRowMoveを呼びます。掴んだ行の直上 / 直下(動かない位置)で離した場合はどちらも呼ばれません。履歴ラッパ経由のためCtrl/Cmd+Z/undo()で戻せます。 - 有効条件: clientSide(
rows+onRowsChange)専用です。dataSource(serverSide)/ 行グルーピング中 /onRowsChange未指定ではハンドル列を挿入しません。readOnlyは関係しません(並び替えはセル編集ではないため)。 - ソート / フィルター中: 表示順と
rowsの順が一致しないため、ハンドルは淡色(.ssg-row-drag-handle--disabled)+ 理由のツールチップになり操作できません(列はそのまま残るのでレイアウトは跳ねません)。解除すると復帰します。 - 展開行との併用: 展開中のマスター行は詳細パネルごと一緒に移動します。ドロップ位置の判定は詳細パネルの高さ込みで、パネルの上は「マスター行の下」として扱います。
- ハンドル列: 合成列のため、列メニュー / ソート / 列 DnD / autoSize / エクスポート / 並び替え管理パネルの対象外です。左固定列があるときは左固定側に、展開行トグル列よりさらに先頭に入ります。
- 命令的 API:
moveRow(rowKey, toIndex)は表示状態(ソート / フィルター)に関わらず元配列上で移動します(onRowsChange→onRowMoveの順)。 - 将来拡張: ドラッグ中に周囲の行がリアルタイムに退避する見せ方(
rowDragMotion: 'live'相当)は、スロット解決を共有したまま表示側だけ差し替えられる設計にしてあります(未実装)。 - スタイル:
.ssg-row-drag-handle(+--disabled)/.ssg-body-cell--row-drag-handle/.ssg-row-drop-indicator/ ドラッグ中の行.ssg-body-row[data-ssg-row-dragging]。色はトークン(--ssg-drop-indicator/--ssg-glyph-*/--ssg-ghost-*)です。
キーボード操作
グリッド本体フォーカス中(編集中でない)の操作一覧。フィルター入力等のフォーム要素にフォーカス中は無効。
| キー | 操作 |
|---|---|
矢印(+ Shift で範囲拡張)/ Tab / Shift+Tab | アクティブセル移動。 |
Enter / F2 / 印字キー直打ち | 編集開始(印字キーはその 1 文字を初期値に)。編集可否は readOnly / 列 / canEditCell に従う。 |
Escape | 選択解除。 |
Ctrl/Cmd+C / ペースト(Ctrl/Cmd+V) | 選択範囲の TSV コピー / アクティブセル起点の貼り付け(readOnly では no-op)。 |
Ctrl/Cmd+A | 全体選択(2 回目で解除)。 |
Delete / Backspace | 選択セル(なければアクティブセル)の値クリア。編集不可セルは対象外。クリア値は「空文字のペースト」と同じ規則(parseClipboardValue('') 経由、未定義なら '')。変更が無ければ no-op(undo 履歴にも積まれない)。 |
Ctrl/Cmd+Z / Ctrl/Cmd+Shift+Z / Ctrl/Cmd+Y | undo / redo(詳細は命令的 API の「undo / redo」節)。 |
編集エディタ内: Enter = 確定して下へ(移動先は editorEnterMove で変更可。既定 'down')、Tab / Shift+Tab = 確定して右 / 左へ、Escape = キャンセル、フォーカスアウト = 確定。IME 変換中(isComposing)の Enter / Escape / Tab は IME の操作としてのみ扱われ、セル編集の確定 / キャンセルには使われない。
確定後の移動先は「確定を反映した再レンダー後」の行数・列数でクランプされる。このため、onRowsChange で末尾に空行を追加する消費側(Excel 的な入力グリッドの定石パターン)では、最終行の Enter 確定で「増えた行」へそのまま移動できる。行が増えない場合は従来どおり最終行に留まる。
スタイリング用の状態クラス(公開契約)
cellClassName / getRowClassName の返すクラスは、下記の内部付与クラスと連結セレクタで組み合わせて使える(例: .ssg-body-cell.my-diff で基底に勝たせ、.ssg-body-cell.my-diff.ssg-body-cell--row-hovered でホバー時色を切替)。以下は公開契約とし、変更時は breaking 扱いにする。
| クラス | 付与先 / 条件 |
|---|---|
.ssg-root | グリッドのルート要素(className prop の付与先)。 |
.ssg-body-row | 行コンテナ(getRowClassName の付与先のひとつ)。 |
.ssg-body-cell | データセルの基底(未レイヤー・特異度 (0,1,0))。 |
.ssg-row-header-cell | 行ヘッダー「#」セル。 |
.ssg-body-cell--readonly | 読み取り専用セル(範囲選択に入っていないとき。dimReadOnlyCells と独立して常時付与)。 |
.ssg-body-cell--invalid | validation mark 表示中のセル。 |
.ssg-body-cell--row-hovered | 行ホバー中のセル(enableRowHover 有効時)。 |
.ssg-body-cell--autoheight | auto-height 列のセル。 |
.ssg-body-cell--align-center / .ssg-body-cell--align-right | column.align の水平寄せ。 |
.ssg-theme-dark | theme="dark"(または 'auto' のダーク解決)時に root と各ポータル(popover / menu / panel / ツールチップ)へ。 |
※上記以外の ssg-* クラス(内部構造クラス)は非公開の実装詳細で、予告なく変わり得る。セレクタで依存しないこと。
補助型(props で参照される shape)
GridRowKey = string | numberGridColumnPinned = 'left' | 'right'GridSelectFilterOption = { label: string; value: string }CellRenderContext<T> = { row, rowIndex, sourceRowIndex, rowKey, colIndex, value, column, isActive, isSelected, isEditing, readOnly, setValue, detail? }detail?: CellDetailContext = { expanded, expandable, toggle, setExpanded }はdetailRowprop 有効時のみ定義(「展開行(Master/Detail)」節)。
DetailRowOptions<T>/DetailRowRenderContext<T> = { row, rowKey, rowIndex, sourceRowIndex, collapse }/CellDetailContext(展開行。バレルから公開)RowDragContext = { rowKey, sourceRowIndex }(isRowDraggableの第 2 引数)/RowMoveParams<T> = { rowKey, fromIndex, toIndex, rows }(onRowMoveの引数。いずれもバレルから公開)CellStyleContext<T>= 上記からsetValueを除いた読み取り専用版(cellClassName関数へ渡る)。バレル(index.ts)から公開(import type { CellStyleContext } from '@ishibashi0112/spreadsheet-grid')rowIndexはビュー行 index(ソート / フィルター適用後の表示位置)、sourceRowIndexは元rowsの index、rowKeyは行キー(rowKeyGetter由来、既定は source index)。ソート / フィルター ON の画面で「エラー行 index の集合」など source 基準のデータと突き合わせるときはsourceRowIndex/rowKeyを使う(getInvalidCells()の返すsourceRowIndex/rowKeyと同一基準)。serverSide では view 順が正準のためsourceRowIndexは view index と同値。
RowStyleContext<T> = { row, rowIndex, sourceRowIndex, rowKey, isSelected }(getRowClassNameの第 3 引数。バレルから公開)rowIndex/sourceRowIndex/rowKeyの基準はCellStyleContextと同一。isSelectedはチェックボックス行選択(enableRowSelection)の選択状態(範囲選択とは別)。グループ行(grouping 有効時)は専用描画のためgetRowClassNameの対象外。
GridScrollPosition = { top: number; left: number }(getScrollPositionの返り値 /setScrollPositionの基準)GridScrollEventParams = { top: number; left: number; source: 'user' | 'api' }(onScrollの引数。sourceの意味は props 表のonScroll行を参照)HeaderRenderContext<T> = { colIndex, width, column, filterValue?, isFiltered? }SpreadsheetGridSlotContext<T> = { rows, filteredRows, columns, visibleColumns, globalFilterText, columnFilterValues, sortState, setGlobalFilterText, activeCell, selection, derivedSummary, globalFilterStatus, globalFilterProgress }derivedSummaryはSpreadsheetGridDerivedSummary(行/列/フィルター/ソートの summary 文字列・選択統計などを内包)。helper を import せずトップ/ボトムバーで使える。globalFilterStatus: GlobalFilterStatus('idle' | 'filtering' | 'ready')/globalFilterProgress: number(0..1)。グローバルテキストフィルタは行数が大きい(しきい値 50,000 行・条件はrows.length > 50000)とき、入力を主スレッドを塞がず時間分割で適用する。適用中はstatus='filtering'・progressが進捗(0..1)になる。空/無効は'idle'、確定は'ready'(progress=1)。50,000 行以下は同期適用のため即'ready'('filtering'を経由しない)。ローディング表示はグリッドが本体に重ねる組み込み overlay(autosize の計測中 overlay と同じ作法)で行うため、トップバーやカスタム UI 側で扱う必要は通常ない。 この 2 値は、入力の無効化や独自インジケータなどカスタム UI を出したい場合の参照用に公開している。serverSide では基本'idle'/'ready'(取得中表示は行スケルトンが担当)。