feat(*): one task editor, vim navigation and undo
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { step } from "../src/lib/grid";
|
||||
import type { Rect } from "../src/lib/grid";
|
||||
|
||||
/** Three cards per row, 100 wide and 40 tall, the way the tray wraps them. */
|
||||
function tray(count: number): { id: number; rect: Rect }[] {
|
||||
return Array.from({ length: count }, (_, index) => ({
|
||||
id: index,
|
||||
rect: {
|
||||
left: (index % 3) * 100,
|
||||
top: Math.floor(index / 3) * 40,
|
||||
width: 96,
|
||||
height: 36,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const rectOf = (item: { rect: Rect }): Rect => item.rect;
|
||||
const walk = (
|
||||
items: ReturnType<typeof tray>,
|
||||
from: number,
|
||||
direction: Parameters<typeof step>[2],
|
||||
): number | null => step(items, items[from], direction, rectOf)?.id ?? null;
|
||||
|
||||
describe("step", () => {
|
||||
it("moves along a row", () => {
|
||||
const items = tray(6);
|
||||
expect(walk(items, 0, "right")).toBe(1);
|
||||
expect(walk(items, 1, "left")).toBe(0);
|
||||
});
|
||||
|
||||
it("moves down a column, not just to the next card", () => {
|
||||
const items = tray(6);
|
||||
expect(walk(items, 1, "down")).toBe(4);
|
||||
expect(walk(items, 4, "up")).toBe(1);
|
||||
});
|
||||
|
||||
it("keeps the column when the last row is short", () => {
|
||||
const items = tray(5);
|
||||
expect(walk(items, 1, "down")).toBe(4);
|
||||
expect(walk(items, 2, "down")).toBe(4);
|
||||
});
|
||||
|
||||
it("carries on to the next row at the end of one", () => {
|
||||
const items = tray(6);
|
||||
expect(walk(items, 2, "right")).toBe(3);
|
||||
expect(walk(items, 3, "left")).toBe(2);
|
||||
});
|
||||
|
||||
it("stops at the top and the bottom", () => {
|
||||
const items = tray(6);
|
||||
expect(walk(items, 1, "up")).toBeNull();
|
||||
expect(walk(items, 4, "down")).toBeNull();
|
||||
});
|
||||
|
||||
it("stops at the very ends of the run", () => {
|
||||
const items = tray(6);
|
||||
expect(walk(items, 0, "left")).toBeNull();
|
||||
expect(walk(items, 5, "right")).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to the first card when the cursor is off the run", () => {
|
||||
const items = tray(3);
|
||||
const stray = { id: 99, rect: { left: 0, top: 0, width: 1, height: 1 } };
|
||||
expect(step(items, stray, "right", rectOf)?.id).toBe(0);
|
||||
});
|
||||
|
||||
it("reads a row by the band each card fills, not by its centre", () => {
|
||||
// Top-aligned cards of different heights still sit on one row.
|
||||
const items = [
|
||||
{ id: 0, rect: { left: 0, top: 0, width: 96, height: 67 } },
|
||||
{ id: 1, rect: { left: 100, top: 0, width: 96, height: 67 } },
|
||||
{ id: 2, rect: { left: 200, top: 0, width: 96, height: 51 } },
|
||||
{ id: 3, rect: { left: 0, top: 73, width: 96, height: 51 } },
|
||||
];
|
||||
expect(step(items, items[0], "up", rectOf)).toBeNull();
|
||||
expect(step(items, items[0], "right", rectOf)?.id).toBe(1);
|
||||
expect(step(items, items[2], "left", rectOf)?.id).toBe(1);
|
||||
expect(step(items, items[0], "down", rectOf)?.id).toBe(3);
|
||||
expect(step(items, items[3], "up", rectOf)?.id).toBe(0);
|
||||
});
|
||||
|
||||
it("handles a single card without moving", () => {
|
||||
const items = tray(1);
|
||||
expect(walk(items, 0, "right")).toBeNull();
|
||||
expect(walk(items, 0, "down")).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user