Conversation
Reads one list's rows once per calculation and answers placement, availability, position, range and navigation questions from that snapshot with pure functions. Nothing uses it yet; the walks it replaces in list-dom, drag-and-drop and selection go next.
Selection resolved an item's list by walking the DOM to the nearest list element; the root resolved it by innermost outlet containment, without checking that the outlet belonged to it. The root now answers both through owns and ownerList, stopping at the nearest root, and hands its reference only to children it owns, which the README already promised for independently nested roots. The range list key uses the same separator as item keys.
Optimistic reorder, placement capture and restore and the ownership check a rollback relies on move to row-mutations.ts unchanged; the reorder resolves its anchor through the rendered list. list-dom.ts is left with the DOM contract: attribute names, selectors, item readers and mobility policy.
Drop resolution, menu moves, move availability, position announcements, keyboard navigation and Shift ranges now build one rendered-list snapshot per gesture and ask it. Deletes the six row walks they replaced in list-dom, drag-and-drop and selection together with their specs, whose cases live in rendered-list.spec. The cross-list check stays in the orchestrator, ahead of the range calculation.
Mobility is the noun the attribute, README and vocabulary use, and movable its predicate; orderable was the one name that did not fit. No behaviour change.
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
myabc
added this pull request to stack #25797
October 3, 2026 20:39
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.
Ticket
No work package yet; interim branch name, stacked on #25792 (merge that first).
What are you trying to accomplish?
Second slice of the sortable-lists deepening agreed in the architecture review. Six separate row walks answered "which item precedes this spot" (drop resolution, list-only drops, directional menu moves, the reorder anchor, Shift ranges, position announcements),
list-dom.tsthreaded(element, rowsContainer)through 23 functions and returnedstring|null|undefinedwith three meanings, and "which list owns this element" was decided twice: by DOMclosest()in selection and by outlet containment in the root, with a comment to keep them in agreement.This PR adds
rendered-list.ts: one snapshot of a list's rows, built per gesture and never kept across a Turbo morph, with pure functions for placement, availability, position, ranges and keyboard navigation. The root controller becomes the single owner of list topology (owns,ownerList); selection, drop resolution and menu moves all read from it. Row mutations move torow-mutations.ts, leavinglist-dom.tswith the DOM contract (attributes, selectors, item readers, mobility). Net: roughly 900 fewer lines in the three "bag" files, no DOM attribute, wire or consumer change.What approach did you choose and why?
Snapshot plus pure functions rather than a class per helper: placement maths has no lifetime, so an object would only relocate the interface. The snapshot classifies each direct row of the rows container once (item, truncation marker with hidden id and omitted count, or gap), and the single predecessor walk reads from that classification. The cross-list check stays in the selection orchestrator, ahead of the pure range calculation, so a Shift gesture into another list still restarts there.
One latent fix rides along: outlet-connected callbacks now hand the root reference only to children whose nearest root is this root, and disconnects are caller-aware, which the README already promised for independently nested roots. Every current consumer scopes its outlet selectors inside its root, so nothing changes for them. The last commit renames
orderabletomovable, matching themobilityattribute and the vocabulary.Two degenerate shapes behave differently from before and are pinned by specs: a row whose item element has no id is a gap rather than an item, and an item placed inside the list element but outside its rows container is not reachable with the arrow keys. No consumer renders either.
AI involvement
Merge checklist