summaryrefslogtreecommitdiffhomepage
path: root/src/features/workspaces/logic/tile-layout.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-07 17:40:29 +0900
committerAdam Malczewski <[email protected]>2026-09-07 17:40:29 +0900
commit46b5f9d30f149c3e80f483c91f563c0a6fc75b25 (patch)
treed6554c2634b624ead9f87881ff506506f88a7cbe /src/features/workspaces/logic/tile-layout.test.ts
parent828d4e4fb822ec2925c8a625bc5bf326c3425964 (diff)
downloaddispatch-web-46b5f9d30f149c3e80f483c91f563c0a6fc75b25.tar.gz
dispatch-web-46b5f9d30f149c3e80f483c91f563c0a6fc75b25.zip
feat(workspaces): bound the drag append area — 3 columns past the content edge
Dragging a tile far right grew the wide canvas endlessly (the target cell resolved from the pointer with no upper bound), making precise placement impossible. Add a pure `dragAppendLimit(placements, geometry, extra = 3)`: the drag target may aim at most 3 columns (wide) / rows (tall) past the current span-aware content edge. The bound is measured on the COMMITTED layout — never the live preview — so it can't ratchet mid-drag; it steps forward only when a drop commits new content. cellAt applies it on the growth axis (wide: col, tall: row); the fixed cross-axis clamps are unchanged. Exported via the unit index. Verification: svelte-check 0 errors/0 warnings; vitest 1693/1693 (4 new dragAppendLimit tests); biome clean on touched files; build ok.
Diffstat (limited to 'src/features/workspaces/logic/tile-layout.test.ts')
-rw-r--r--src/features/workspaces/logic/tile-layout.test.ts31
1 files changed, 31 insertions, 0 deletions
diff --git a/src/features/workspaces/logic/tile-layout.test.ts b/src/features/workspaces/logic/tile-layout.test.ts
index a5d58c0..b538373 100644
--- a/src/features/workspaces/logic/tile-layout.test.ts
+++ b/src/features/workspaces/logic/tile-layout.test.ts
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import {
clampSpan,
displacePlace,
+ dragAppendLimit,
fillOrderKey,
firstEmptySlot,
type GridGeometry,
@@ -524,6 +525,36 @@ describe("placeAtCell", () => {
});
});
+describe("dragAppendLimit", () => {
+ it("caps a wide drag at 3 columns past the right-most content edge (span-aware)", () => {
+ // b spans cols 3-4 → the right-most occupied column is 4; +3 = 7.
+ const placements: Placement[] = [
+ { id: "a", col: 1, row: 1, colSpan: 1, rowSpan: 1 },
+ { id: "b", col: 3, row: 1, colSpan: 2, rowSpan: 1 },
+ ];
+ expect(dragAppendLimit(placements, WIDE)).toBe(7);
+ });
+
+ it("caps a tall drag at 3 rows past the bottom-most content edge (span-aware)", () => {
+ // b spans rows 3-4 → the bottom-most occupied row is 4; +3 = 7.
+ const placements: Placement[] = [
+ { id: "a", col: 1, row: 1, colSpan: 1, rowSpan: 2 },
+ { id: "b", col: 2, row: 3, colSpan: 1, rowSpan: 2 },
+ ];
+ expect(dragAppendLimit(placements, TALL)).toBe(7);
+ });
+
+ it("an empty grid still allows the first tiles within the first 3 columns", () => {
+ expect(dragAppendLimit([], WIDE)).toBe(3);
+ expect(dragAppendLimit([], TALL)).toBe(3);
+ });
+
+ it("honors a custom extra", () => {
+ const placements: Placement[] = [{ id: "a", col: 1, row: 1, colSpan: 1, rowSpan: 1 }];
+ expect(dragAppendLimit(placements, WIDE, 5)).toBe(6);
+ });
+});
+
describe("sortByRecentActivity", () => {
it("re-applies the recent-activity order (descending)", () => {
const tiles = [tile("a", 0), tile("b", 1), tile("c", 2)];