refactor(reorder): drop the Alt + Arrow shortcut, keep drag only

The reorder shortcut cannot be made reliable in this UI. `Alt + Arrow` is
the browser's tab-history / back-forward gesture on several platforms,
and the modal already binds bare `ArrowLeft`/`ArrowRight` to model
navigation, so the binding either did nothing — a keypress with nothing
focused never reaches a listener on the tag list — or fought the browser.
An affordance that occasionally navigates the page away is worse than
having no keyboard path at all, so drop it.

Reordering is pointer-only again: drag the chip (tags) or its `⠿` grip
(trigger words, whose chip body is click-to-edit). Everything that existed
only to serve the shortcut goes with it — the keydown listener, the hover
tracking used to resolve the target chip, the aria-live announcements, the
per-grip position labels and `moveItemWithinContainer`. The grip becomes a
decorative, non-focusable `<span>` (`aria-hidden`, behind a 5px drag
threshold) instead of a `<button>`, so it no longer promises a keyboard
action it cannot perform.

The tooltip and hint drop the shortcut mention in all 10 locales
(`common.reorder.dragHandle` = "Drag to reorder" and the localised
equivalents); `common.reorder.ariaLabel` and `common.reorder.announcement`
are pruned from every locale by the sync script. The i18n guidelines
record the decision so no shortcut is re-added without re-adding the keys.
This commit is contained in:
Will Miao
2026-09-17 09:07:24 +08:00
parent f67689b0f9
commit e09fe5888b
19 changed files with 116 additions and 405 deletions
@@ -8,14 +8,12 @@ const POINTER_SORT_MODULE = new URL(
describe("pointerSort", () => {
let enablePointerSort;
let disablePointerSort;
let moveItemWithinContainer;
beforeEach(async () => {
document.body.innerHTML = '';
const module = await import(POINTER_SORT_MODULE);
enablePointerSort = module.enablePointerSort;
disablePointerSort = module.disablePointerSort;
moveItemWithinContainer = module.moveItemWithinContainer;
});
function buildList(words) {
@@ -172,25 +170,4 @@ describe("pointerSort", () => {
expect(order()).toEqual(['a', 'b']);
});
it("moveItemWithinContainer moves items within bounds only", () => {
const { container } = buildList(['a', 'b', 'c']);
const items = Array.from(document.querySelectorAll('.item'));
expect(moveItemWithinContainer(items[0], 1, sortOptions()))
.toEqual({ index: 1, total: 3 });
expect(order()).toEqual(['b', 'a', 'c']);
expect(moveItemWithinContainer(items[2], -1, sortOptions()))
.toEqual({ index: 1, total: 3 });
expect(order()).toEqual(['b', 'c', 'a']);
// Out of range / unknown item moves are refused
const currentFirst = document.querySelector('.item');
expect(moveItemWithinContainer(currentFirst, -1, sortOptions())).toBeNull();
expect(moveItemWithinContainer(currentFirst, 3, sortOptions())).toBeNull();
expect(moveItemWithinContainer(document.createElement('div'), 1, {
...sortOptions(),
container,
})).toBeNull();
});
});