Allow disabling individual keyboard controls with null (#333)#365
Merged
Conversation
Any `keyboardControls` binding can now be set to `null` to disable it.
A disabled binding is no longer intercepted, so the key falls through to
its native browser behaviour — e.g. `{ tabForward: null, tabBack: null }`
restores normal Tab/Shift-Tab focus traversal instead of moving between
editable nodes.
- `eventMatch` treats a `null` control as "never matches"; since
`handleKeyPress` only `preventDefault()`s on a match, native behaviour
passes through.
- `getFullKeyboardControlMap` now distinguishes `undefined` (keep
default) from `null` (disable). The two modifier-array controls store
`[]` when disabled so their `.includes()` consumers stay null-safe.
- An explicitly disabled per-type confirm is no longer re-enabled by the
generic `confirm` fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundle size impact
|
| Format | Base raw | PR raw | Δ raw | Base gzip | PR gzip | Δ gzip |
|---|---|---|---|---|---|---|
| esm | 56.43 KB | 56.52 KB | 🔺 +87 B (+0.15%) | 20.12 KB | 20.15 KB | 🔺 +31 B (+0.15%) |
| cjs | 57.93 KB | 58.02 KB | 🔺 +87 B (+0.15%) | 20.21 KB | 20.24 KB | 🔺 +34 B (+0.16%) |
Measured from build/index.{cjs,esm}.js. Gzip at level 9.
The generic `confirm` already cascades its *value* to `stringConfirm` / `numberConfirm` / `booleanConfirm`, but a `null` (disable) was skipped by the fallback, so `confirm: null` left Enter-to-submit working on those nodes. Treat "explicitly provided" (value or `null`) as cascading and only "not provided" (`undefined`) as keeping the per-type defaults, so `confirm: null` disables Enter-submit across string, number, boolean and null editors at once. An explicit per-type confirm still wins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #333.
What
Lets consumers disable any
keyboardControlsbinding by setting it tonull. A disabled binding is no longer intercepted, so the key falls through to its native browser behaviour. The motivating case is Tab:{ tabForward: null, tabBack: null }turns off node-to-node Tab navigation so Tab/Shift-Tab resume normal focus traversal — useful for data sets where the built-in Tab behaviour isn't appropriate.This supersedes the issue's "define a nonsense/unreachable key" hypothesis: that works by accident (a non-matching control never
preventDefault()s) but is undiscoverable and doesn't read as intent.nullstates it in both the config and the types.How
eventMatchtreats anullcontrol as "never matches". BecausehandleKeyPressonly callspreventDefault()on a match, a disabled key passes straight through to the browser.getFullKeyboardControlMapnow distinguishesundefined(not supplied → keep default) fromnull(disable). The old truthy guard conflated them. The two modifier-array controls (clipboardModifier/collapseModifier) store[]when disabled, so their.includes()consumers stay null-safe and those fields remain real arrays.confirmfallback now checks=== undefined, so an explicitly disabled per-type confirm (e.g.stringConfirm: null) isn't silently re-enabled.KeyboardControlsfield accepts| null;KeyboardControlsFull'sKeyEvent-valued entries becomeKeyEvent | null.Tests
test/keyboard.test.ts: map behaviour (nullKeyEvent→null, null modifier →[], disabled per-type confirm not re-enabled) and behavioural assertions onhandleKeyPress(a disabled binding fires neither its action norpreventDefault; a default binding still does).eventMatchguard makes the suite fail to compile against the newKeyEvent | nulltype — the guard is load-bearing.pnpm compileclean.Note
jsdom doesn't move focus on Tab, so the unit tests pin the contract we control (no
preventDefault, no node-move). Native focus traversal after disabling is worth a quick manual confirm in a real browser.Docs: README "Keyboard customisation" gains a bullet documenting the
nulldisable. Changeset included (minor).🤖 Generated with Claude Code