feat(design): reorder without a drag everywhere a grip drags (WCAG 2.5.7, E28 S3e4a)
One rule, two forms: useOrderItems (Top / Up / Down / Bottom rows of a row menu) and OrderButtons (four buttons with the place, or a compact Up / Down pair). A move that would go nowhere is shown off. - YouTube playlist rows: the four moves now come from useOrderItems. - Plex playlist rows: a block among the blocks, an episode within its show, off while a write is in flight; drag and menu share moveBlock/moveEpisode. - Saved views editor: the selected view's four buttons (owner: A), and the grip's keyboard pick-up (its instructions promised one it did not have). - Side panel groups in edit mode: an Up / Down pair (owner: A); a group the stored order does not hold yet moves too; the name wraps there. - The grip's touch-action: none is now test-held.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { moveTarget } from "./moveInList";
|
||||
|
||||
describe("moveTarget", () => {
|
||||
it("sends an item to the top, one up, one down, or to the bottom", () => {
|
||||
expect(moveTarget(2, 5, "top")).toBe(0);
|
||||
expect(moveTarget(2, 5, "up")).toBe(1);
|
||||
expect(moveTarget(2, 5, "down")).toBe(3);
|
||||
expect(moveTarget(2, 5, "bottom")).toBe(4);
|
||||
});
|
||||
|
||||
it("is null for a move that goes nowhere: the first up, the last down", () => {
|
||||
expect(moveTarget(0, 5, "top")).toBeNull();
|
||||
expect(moveTarget(0, 5, "up")).toBeNull();
|
||||
expect(moveTarget(4, 5, "down")).toBeNull();
|
||||
expect(moveTarget(4, 5, "bottom")).toBeNull();
|
||||
expect(moveTarget(0, 1, "down")).toBeNull();
|
||||
});
|
||||
|
||||
it("is null for an index outside the list", () => {
|
||||
expect(moveTarget(-1, 5, "top")).toBeNull();
|
||||
expect(moveTarget(5, 5, "up")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
// The non-drag way to reorder a list (WCAG 2.5.7, docs/DESIGN.md "Reordering"): anything a grip can
|
||||
// drag also moves by Top / Up / Down / Bottom — a menu's rows or a row of buttons, one rule for both.
|
||||
|
||||
export type MoveTo = "top" | "up" | "down" | "bottom";
|
||||
|
||||
export const MOVES: readonly MoveTo[] = ["top", "up", "down", "bottom"];
|
||||
|
||||
/** Where `to` sends the item at `from` in a list of `count`, or null when it would not move (the
|
||||
* first item going up, the last going down, a stale index) — the caller shows that move as off. */
|
||||
export function moveTarget(from: number, count: number, to: MoveTo): number | null {
|
||||
if (from < 0 || from >= count) return null;
|
||||
const at = to === "top" ? 0 : to === "bottom" ? count - 1 : to === "up" ? from - 1 : from + 1;
|
||||
return at === from || at < 0 || at >= count ? null : at;
|
||||
}
|
||||
Reference in New Issue
Block a user