Repository navigation
Conversation
A listener outside a shadow root sees a press from a web component's input retargeted to the shadow host, so the editable-target guard in keyRove and createTypeahead saw a plain element: arrows, Home/End and typed characters were consumed as moves and never reached the field. The guard now also checks the element the press began on, the first entry of the composed path, read off nativeEvent for React's synthetic events. Group resolution still uses the retargeted target. KeyRoveEvent gains two optional members, composedPath and nativeEvent.
fix: treat items disabled by a fieldset as disabled
fix: leave keys typed into a field inside a shadow root to the field
A move whose target refused focus (hidden, inert, display: none, visibility: hidden, inside a closed <details>) was a consumed no-op, so one such item in the middle of a group stopped the arrows from ever reaching the items past it. The move is now resolved again with the refused item passed over, until focus lands or nothing is left. keyRove, rove and typeahead walk on; focus keys and exit/enter, which name one destination, do not. The roving stop is put back on every item passed over, onMove fires once, and a move where nothing takes focus is the consumed no-op it was. A move whose first target takes focus costs the same as before. skip stays the recommended way to hide items: a run of 1,000 refusing items costs about 7 ms per press, against 0.3 ms when they are skipped.
pnpm bench:headless removed Chrome's profile while Chrome was still writing it after Browser.close. The removal threw ENOTEMPTY from runHarness's finally block and replaced the finished results, so the runner printed no report, wrote no --json file and exited 1. - Move Chrome handling into scripts/chrome.ts. withChrome runs a body in a fresh profile, waits up to 5 s for Chrome to exit, kills it with SIGKILL if it hasn't, then removes the profile. - Removing the profile or the scratch directory retries, and if it still fails it warns with the leftover path instead of throwing. A finished run keeps its report, and a cleanup error never hides a harness error. - Start cleanup as soon as the preview server is up, so a failed launch or DevTools connection also stops Chrome and the server. Pending DevTools calls now fail when the socket closes. - Test the cleanup with fake Chrome executables.
… ES modules
The exports map offered only the types and import conditions, so
require('@mixedrays/keyrove') failed with ERR_PACKAGE_PATH_NOT_EXPORTED
before Node could load the file, even on Node versions that load ES
modules through require(). A default condition points require, and any
resolver with its own conditions, at the same ESM build. No CommonJS
build is added.
On Node below 20.19 / 22.12, require now fails with ERR_REQUIRE_ESM,
which names the real cause.
The installation page notes that the package can be required on
Node >= 20.19 or >= 22.12.
feat: walk a move on past an item that refuses focus
A move whose destination refused focus resolved the whole move again from the focused item, so a run of N items that refuse focus cost O(N²) navigable tests: about 7.3–7.7 ms per press for 1,000 hidden rows on an M1 Pro. The move now takes each next destination from the same walk. walkTargets in position.ts hands out every destination in the order a stride tries them, its own scan and then its clamp or wrap, testing each item only as it reaches it. keyRove, rove and typeahead take the first destination from it and carry it on past each refusal by the stricter test land asks with. That test only grows stricter, so the walk lands where resolving again would, for every stride. 1,000 hidden rows now cost about 0.65 ms, and typeahead reads each label once however many matches refuse. The benchmark gains a "refused focus" section, and its recorded run and README are re-recorded with it.
Windows browsers report AltGr as Ctrl+Alt, so a press that types "@" or
"€" read as a command: a ctrl+alt+ focus key fired from inside a field,
and typeahead dropped characters typed with AltGr. hasCommandModifier now
treats a press with getModifierState('AltGraph') held as typing, in both
the editable-target guard and typeahead. Combo matching is unchanged, so
outside a field a ctrl+alt+ focus key still fires on the AltGr press.
KeyRoveEvent gains an optional getModifierState, asked only about
'AltGraph' so React's narrower ModifierKey parameter fits too. An event
without it behaves as before. The docs drop the AltGr caveat.
chore(bench): wait for Chrome to exit before removing its profile
fix: let CommonJS code require the package on Node versions that load ES modules
perf: walk on past refusing items in one pass
fix: treat AltGr presses as typing, not commands
`cols: 'auto'` counted only CSS grid tracks, so a flex-wrap or inline-block gallery fell back to a one-column list and needed hand-written glue to copy its column count into the attribute. Where the root has no more than one grid track, `auto` now counts the items on the first line: from the first item of the sequence, every item that starts above the first item's bottom edge, on each keypress. Items of different heights centred on one line count as that line. Only the first line is read, plus one item. An item with no box ends the count, so without layout (jsdom) the group stays a list. A grid with more than one track returns before any item is read, and a numeric count never reads layout. readConfig takes the group (its root and its lazily read items) in place of the root. This widens what `auto` means on a non-grid root: a single-line flex row becomes a one-row grid, so its next/prev keys move to ArrowLeft/Right. A toolbar should use data-keyrove-orientation instead.
With cols auto, readConfig read the layout on every keydown, before the key was matched. On a flex root an unbound key paid for reading the items and two rect calls per first-line item, and laid the page out when it had changed since the last read. readConfig now reads the layout on first ask. findBinding asks for it only when the press's code is a default key of some layout, or the press matches a combo some move is bound to. Any other press can match nothing but a focus key, and focus keys head every table, so it is resolved among them without the layout. Default keys are tested by code alone, a bound combo is parsed only where its text holds the press's code, and each move's attribute name is spelled once. countFirstLine reads one getClientRects() per item, its first box, in place of a getClientRects() and a getBoundingClientRect(). An unbound key now costs about 1.2 µs on every root, down from 2.5 µs on a list, 5.6 µs on a grid with auto and 10-25 µs on a flex root with auto, and none lays out a changed page.
…e benchmark The columns section timed only a four-column CSS grid, whose tracks auto counts without reading any item, so the first-line count went unmeasured. It now also lays groups out as a flex-wrap row of four, a flex row of all 20 and a flex column, with a fixed cols and with auto. Each page times the unbound key and a move with the layout laid out and with it changed just before each press, and one layout of the page with no handler. A probe skips the flex moves on versions that read a layout with no grid tracks as a list. Re-recorded, with the README's results and findings, and the bench tests' copied rows, updated to the new run.
feat: count the first line's items for cols auto on wrapping layouts
- Build TypeaheadOptions on RovingTabindexOptions, and read groups by it, in place of three copies of the same Pick of GroupOptions. GroupReadingOptions, identical to RovingTabindexOptions, is gone. - Type MoveAction and the keys option with BoundaryAction instead of spelling out 'exit' | 'enter'. - Export the KEYROVE_ATTR_* constants from index.ts directly rather than through keyRove.ts, and name the keyRove and createTypeahead exports, so the public surface reads at a glance in one file. - Move resolveTarget, used only by the position tests, into them. - Correct the ExplicitBinding comment: since 2.4.0 the keys option is read before the root's *-key attribute. The public surface is unchanged.
AnyMove, Move | TypeaheadMove, types an onMove in one config object that is passed to both keyRove and createTypeahead. Typed with Move alone, such an object fails to type-check against createTypeahead, whose moves have the action 'typeahead'. BoundaryAction, 'exit' | 'enter', lets a keys map for the boundary moves be typed, as StrideAction already does for the strides. The README's and the API reference's shared-config examples type onMove with AnyMove, and the docs site's readout takes it.
toggleTabIndex is a single setAttribute call. Its parameter is named root though callers pass an item, and its spelling differs from initRovingTabindex and RovingTabindexOptions. For a roving group, use initRovingTabindex to keep its one tab stop and followFocus to move it with focus; for a stop managed by hand, set element.tabIndex. The library and the tree demo no longer call it. Options, the earlier name of KeyRoveOptions, now carries @deprecated, so editors point to KeyRoveOptions. Both keep working and will be removed in the next major version.
data-keyrove-orientation="Horizontal" or " horizontal " silently gave a vertical list, while cols="auto", none and the boolean attributes are trimmed and read in any case. The attribute now is too. The orientation option is typed and is still compared as given.
feat: export AnyMove and BoundaryAction, deprecate toggleTabIndex and Options, fix orientation case
This branch has not been deployed
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.
No description provided.