Skip to content

Commit 51824d4

Browse files
committed
feat(examples): add keyboard block actions with focus recovery
1 parent e5e3079 commit 51824d4

12 files changed

Lines changed: 716 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"playground": true,
3+
"docs": false,
4+
"author": "minwookshin",
5+
"tags": ["Intermediate", "Accessibility", "UI Components", "Block Side Menu"],
6+
"dependencies": { "react-icons": "^5.5.0" }
7+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Keyboard Block Actions
2+
3+
A runnable interaction proposal for [#2854](https://github.com/TypeCellOS/BlockNote/issues/2854). Place the caret in a block and press **Shift+F10** (or the Context Menu key) to open its actions. Arrow keys move between actions. Escape or Tab closes the menu and returns to the same editor selection; Tab keeps its existing indentation behavior while editing.
4+
5+
This example uses BlockNote's public editor APIs, `BlockPopover`, and the same Mantine menu primitives and `bn-menu-*` classes as the default adapter. It does not change library shortcuts or the hover side menu. The explicit “Block actions” button makes the interaction discoverable without requiring the shortcut.
6+
7+
It reuses an existing Mantine provider when embedded in the playground and supplies one when run independently. It also loads Mantine's core styles for the demo controls outside the editor, matching the playground setup.
8+
9+
The proposal covers one current block, including a nested block and its children. Multi-block selections, IME composition, and read-only editors do not intercept the shortcut. Selecting multiple blocks disables the visible action button and explains that the actions support one block at a time. Dismissing by clicking elsewhere preserves the newly clicked focus target. Deleting a block moves the caret to a surviving neighbor; duplication regenerates IDs for every descendant. Opening or cancelling the menu does not create an undo entry.
10+
11+
The menu intentionally demonstrates three structural actions rather than claiming full parity with the existing drag-handle menu. Color submenus, all three UI adapters, shortcut documentation/localization, and the final library API need maintainer agreement before promoting this example into a default behavior.
12+
13+
**Try it out:**
14+
15+
1. Put the caret in the middle of a sentence. Open the menu and press Escape, then type: the caret should be unchanged.
16+
2. Open again, use the arrow keys, and duplicate a nested block. Its content and children are copied with fresh IDs.
17+
3. Delete a block and use the editor's Undo shortcut.
18+
4. Toggle read-only mode. The action button is disabled and Shift+F10 is left to the browser.
19+
5. Select text across multiple blocks. The disabled button explains the unsupported selection; collapse the selection to use the actions again.
20+
21+
The browser regressions import this example directly and exercise keyboard focus, exact caret restoration, indentation, nested identities, Undo, unsupported selections, and narrow layout. Run them with the repository's Docker runner: `pnpm e2e keyboard-block-actions --retry=0 --maxWorkers=1`.
22+
23+
**Relevant Docs:**
24+
25+
- [Side Menu](/docs/react/components/side-menu)
26+
- [Manipulating Content](/docs/reference/editor/manipulating-content)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<html lang="en">
2+
<head>
3+
<meta charset="UTF-8" />
4+
<meta
5+
name="viewport"
6+
content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content"
7+
/>
8+
<title>Keyboard Block Actions</title>
9+
<script>
10+
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
11+
</script>
12+
</head>
13+
<body>
14+
<div id="root"></div>
15+
<script type="module" src="./main.tsx"></script>
16+
</body>
17+
</html>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
2+
import React from "react";
3+
import { createRoot } from "react-dom/client";
4+
import App from "./src/App.jsx";
5+
6+
const root = createRoot(document.getElementById("root")!);
7+
root.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>,
11+
);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@blocknote/example-ui-components-keyboard-block-actions",
3+
"description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
4+
"type": "module",
5+
"private": true,
6+
"version": "0.12.4",
7+
"scripts": {
8+
"start": "vite",
9+
"dev": "vite",
10+
"build:prod": "tsc && vite build",
11+
"preview": "vite preview"
12+
},
13+
"dependencies": {
14+
"@blocknote/ariakit": "latest",
15+
"@blocknote/core": "latest",
16+
"@blocknote/mantine": "latest",
17+
"@blocknote/react": "latest",
18+
"@blocknote/shadcn": "latest",
19+
"@mantine/core": "^9.0.2",
20+
"@mantine/hooks": "^9.0.2",
21+
"react": "^19.2.3",
22+
"react-dom": "^19.2.3",
23+
"react-icons": "^5.5.0"
24+
},
25+
"devDependencies": {
26+
"@types/react": "^19.2.3",
27+
"@types/react-dom": "^19.2.3",
28+
"@vitejs/plugin-react": "^6.0.1",
29+
"vite": "^8.0.0"
30+
}
31+
}

0 commit comments

Comments
 (0)