Global shortcuts
Opt-in undo, redo, and other grid actions for focus positions outside the grid.
The keyboard shortcuts of the grid work only while DOM focus is inside the grid.
When focus is on a custom toolbar, a side panel, or body, Ctrl/Cmd+Z
does not touch the grid.
DataGridGlobalShortcuts mounts a window-level listener that keeps undo and redo
active at every focus position.
Edit a cell, then click the button or the input below (focus leaves the grid) and press Ctrl/Cmd+Z — the undo still lands in the grid. Typing into the input? Ctrl/Cmd+Z undoes the input's text, never the grid.
pnpm dlx shadcn@latest add @gridcn/data-grid-global-shortcuts-demo
How it works
DataGridGlobalShortcuts is a headless component in the core block.
It renders nothing.
Mount it inside DataGridRoot.
The component listens for keydown on the window.
A gate of seven rules decides whether an event reaches the grid:
- While IME composition is in progress, the gate skips the event.
- When the event is already
defaultPrevented, the gate skips it. (The page or the grid handled it first.) - When the event target is an editable element (
input,textarea,select,contenteditable), the gate skips it. The field keeps its native undo and redo. - When the target is inside a grid, the gate skips the event. The handler inside that grid owns the key.
- The gate requires this grid to be the last focused grid with the component. This rule breaks ties between grids.
- While an edit session is open, the gate skips the event. The editor owns the key, also in a popup such as the select list.
- The key must resolve to an enabled action through the effective keymap.
When the gate passes, the component dispatches the action through the in-grid keymap. For undo
and redo, that is the undo/redo command of the
data-grid-history plugin. The global shortcut behaves exactly like the
in-grid mod+Z binding.
With the data-grid-history add-on, undo reverses grid edits.
Without the add-on the layer is a no-op, like in-grid undo.
Usage
Mount the component inside DataGridRoot:
import { DataGridGlobalShortcuts } from "@/components/data-grid/data-grid";
function OrdersGrid() {
return (
<>
{/* your custom toolbar — OUTSIDE the grid */}
<MyToolbar />
<DataGridProvider {...grid} columns={columns}>
<DataGridRoot>
<DataGridGlobalShortcuts />
<DataGridHeader />
<DataGridBody />
</DataGridRoot>
</DataGridProvider>
</>
);
}Enable one or more actions with a true flag:
<DataGridGlobalShortcuts selectAll deleteRows />Omit every flag to enable the default set (undo and redo). Setting any flag replaces the default set with exactly the flags you pass.
| Prop | Description | Default |
|---|---|---|
undo | enables the effective keymap's undo binding on window keydown. Requires the Undo & redo add-on | enabled when no flag is set |
redo | enables the effective keymap's redo bindings on window keydown. Requires the Undo & redo add-on | enabled when no flag is set |
fillDown | enables the fill-down binding on window keydown. Requires the Fill add-on | off |
fillRight | enables the fill-right binding on window keydown. Requires the Fill add-on | off |
selectAll | enables the select-all binding on window keydown | off |
insertRowAbove | enables the insert-row-above binding on window keydown | off |
insertRowBelow | enables the insert-row-below binding on window keydown | off |
duplicateRow | enables the duplicate-row binding on window keydown | off |
deleteRows | enables the delete-rows binding on window keydown | off |
Each flagged action runs the same dispatch as its in-grid binding, with the same guards. For
example, insertRowBelow does nothing without createRow.
The listener reads the effective keymap from the store.
When you remap undo, the global binding follows (see Keybindings: Remapping).
Only actions whose every binding includes Ctrl/Cmd (mod) are available as
flags. A key without it types into the page, and a ctrl-only or alt-only chord collides with
the operating system (Ctrl+Space switches the input language on Windows).
Multiple grids
When several grids with the component share a page, the last focused grid owns the shortcut. When focus moves to grid B, undo and redo target the history of B. Until focus returns to grid A, they target the history of A. A grid without the component never claims the shortcut. If you add the component to one of two grids, nothing changes until you focus that grid. In a tabbed layout, the last DOM focus decides the active tab. When your app switches tabs without a focus change, call undo imperatively for the active tab.
Page-wide mod+z
If your app registers its own window-level mod+z (for example, a global undo), both listeners
see the same event.
The handler that calls preventDefault first wins.
When event.defaultPrevented is true, your handler skips the event.
This protects the layer from your listener and your listener from the layer.
Out of scope
- Global copy and paste: the active cell is unknown while focus is outside the grid. For a
programmatic paste, call
useDataGridClipboard().pasteFromClipboard(). - Global navigation: the arrow keys have no
modprefix, so they stay with the page. - Global select-all by default: the page owns
mod+auntil you set theselectAllflag.
Related
- Undo & redo: the
undo/redocommands this component reaches - Keybindings: the effective keymap and remapping
- Selection & keyboard: the in-grid keymap table