Navigate and interact with the grid using keyboard.
Keyboard navigation enables users to navigate and interact with the grid using only the keyboard. This improves accessibility and provides power users with efficient workflows.
<ActiveGrid
enableKeyboardNavigation={true}
onCellActivate={(row, columnId) => {
console.log('Cell activated:', row, columnId);
}}
{...}
/><ActiveGrid
enableKeyboardNavigation={true}
columns={columns}
data={data}
{...}
/>When enabled, the grid receives focus and responds to keyboard input.
The onCellActivate callback fires when a cell is activated with Enter or Space:
The grid automatically receives focus when enableKeyboardNavigation is true. The first data cell is focused by default.
Focused cells receive the .grid-cell--focused class:
For custom keyboard navigation implementations:
{ rowIndex: number; colIndex: number } | null(cell: { rowIndex: number; colIndex: number } | null) => void(rowIndex: number, colIndex: number) => object(rowIndex: number, colIndex: number, element: HTMLElement) => voidobjectKeyboard navigation enhances accessibility by:
The grid automatically includes:
role="grid"aria-label or aria-labelledbyaria-rowcount and aria-colcounttabindex managementWhen both keyboard navigation and editing are enabled:
Keyboard behavior:
When both keyboard navigation and selection are enabled:
Keyboard behavior:
Focus is automatically restored after:
Some cells should not receive focus (e.g., non-interactive cells):
Keyboard navigation is optimized for large datasets:
| Shortcut | Action | |----------|--------| | ↑↓←→ | Navigate cells | | Enter | Activate cell / Start editing | | Space | Activate cell / Toggle selection | | F2 | Start editing | | Tab | Next cell (save if editing) | | Shift + Tab | Previous cell | | Escape | Cancel / Clear focus | | Home | First cell in row | | End | Last cell in row | | Ctrl + Home | First cell in grid | | Ctrl + End | Last cell in grid | | Page Up | Scroll up one page | | Page Down | Scroll down one page | | Ctrl + A | Select all |
Full keyboard navigation support in:
<ActiveGrid
enableKeyboardNavigation={true}
onCellActivate={(row, columnId) => {
console.log('Activated:', row, columnId);
// Example: Open detail modal
if (columnId === 'name') {
openDetailModal(row);
}
// Example: Trigger action
if (columnId === 'actions') {
performAction(row);
}
}}
{...}
/>.grid-cell--focused {
outline: 2px solid hsl(var(--primary));
outline-offset: -2px;
z-index: 1;
}<ActiveGrid
enableKeyboardNavigation={true}
className="custom-focus"
{...}
/>.custom-focus .grid-cell--focused {
outline-color: blue;
outline-width: 3px;
background-color: rgba(0, 0, 255, 0.05);
}import { useKeyboardNavigation } from '@workspace/active-grid';
function CustomGrid() {
const table = useActiveGrid();
const {
focusedCell,
setFocusedCell,
getCellProps,
registerCellRef,
containerProps,
} = useKeyboardNavigation(table, {
enabled: true,
onCellActivate: (position) => {
console.log('Cell activated:', position);
},
});
return (
<div {...containerProps}>
{/* Grid implementation */}
</div>
);
}<ActiveGrid
enableKeyboardNavigation={true}
editMode="cell"
columns={columns}
data={data}
onDataCommit={handleCommit}
{...}
/>const columns = [
getSelectionColumn<User>(),
// ... other columns
];
<ActiveGrid
enableKeyboardNavigation={true}
columns={columns}
data={data}
{...}
/>{
accessorKey: 'avatar',
header: 'Avatar',
cell: ({ getValue }) => (
<img src={getValue()} alt="Avatar" />
),
meta: {
focusable: false, // Custom flag (implementation dependent)
},
}import { useState } from 'react';
import { ActiveGrid, getSelectionColumn } from '@workspace/active-grid';
type User = {
id: string;
name: string;
email: string;
role: string;
};
function UsersTable() {
const [selectedRows, setSelectedRows] = useState({});
const handleCellActivate = (row: User, columnId: string) => {
if (columnId === 'name') {
// Open user details
openUserDetails(row);
} else if (columnId === 'email') {
// Copy email
navigator.clipboard.writeText(row.email);
toast.success('Email copied!');
}
};
const columns = [
getSelectionColumn<User>(),
{
accessorKey: 'name',
header: 'Name',
meta: {
editable: true,
},
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'role',
header: 'Role',
meta: {
editable: true,
editType: 'select',
editOptions: [
{ label: 'Admin', value: 'admin' },
{ label: 'User', value: 'user' },
],
},
},
];
return (
<ActiveGrid
columns={columns}
data={users}
enableKeyboardNavigation={true}
onCellActivate={handleCellActivate}
editMode="cell"
onDataCommit={handleDataCommit}
state={{ rowSelection: selectedRows }}
onRowSelectionChange={setSelectedRows}
/>
);
}<ActiveGrid<User>
enableKeyboardNavigation={true}
onCellActivate={(row: User, columnId: string) => {
// Type-safe row and columnId
}}
{...}
/>