summaryrefslogtreecommitdiffhomepage
path: root/src/features/workspaces/logic/tile-layout.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-07 16:12:58 +0900
committerAdam Malczewski <[email protected]>2026-09-07 16:12:58 +0900
commit83fb6e3ce7f80a9c287764df26b6245e0945a941 (patch)
treeafe8a0046b40a45a85f3bc56a4bcd4c93a6f36c7 /src/features/workspaces/logic/tile-layout.test.ts
parent754450b539818f9ad94c9586888973b0edd74b68 (diff)
downloaddispatch-web-83fb6e3ce7f80a9c287764df26b6245e0945a941.tar.gz
dispatch-web-83fb6e3ce7f80a9c287764df26b6245e0945a941.zip
feat(workspaces): rework tile-grid visuals — Start-screen polish
The home canvas now reads as a real Windows 8 Start screen instead of a cramped, edge-hugging list: - Geometry: the wide unit is DERIVED from the canvas height (4 rows fill it: unit=height/5 clamped to 112-184px, gap=unit/9, margin=unit/3 clamped 24-64px) — tiles are no longer tiny, and the tile field always carries a proportional outer margin (never compressed against the left edge). Tall mode keeps fluid columns with a taller 148px row + 12px gap. - Tiles take a per-workspace SATURATED tone (new pure tileTone(id): FNV-1a hash → one of the six theme-semantic colors) with tone-content text — the Win8 colored-tile identity. Starred is now a ★ badge (a tile keeps its own color), not a gray-tint override. - The tile face is the Win8 layout: small uppercase slug caption at the top, a bold span-aware title (lg → 3xl across 1×1 → 4×2), and a single-line stat row pinned to the bottom (count compacts to 'conv.' on 1×1 tiles, truncation-safe so narrow tiles never clip). - Selection is a high-contrast offset ring (visible on any tile color); tiles lift on hover; the pan arrow is a solid elevated circle. - Header gains a 'WORKSPACES' kicker; empty/loading states are centered and styled for the Start-screen canvas. Persisted data (order + spans only) and all interaction semantics (drag/FLIP, long-press, action bar, resize picker, create popover) are untouched. Tests: geometry derivation/clamping, tileTone determinism, wide-vs-compact count label — 1664 green.
Diffstat (limited to 'src/features/workspaces/logic/tile-layout.test.ts')
-rw-r--r--src/features/workspaces/logic/tile-layout.test.ts53
1 files changed, 47 insertions, 6 deletions
diff --git a/src/features/workspaces/logic/tile-layout.test.ts b/src/features/workspaces/logic/tile-layout.test.ts
index f2fbc5b..be61da4 100644
--- a/src/features/workspaces/logic/tile-layout.test.ts
+++ b/src/features/workspaces/logic/tile-layout.test.ts
@@ -52,6 +52,44 @@ describe("geometryFor", () => {
it("resolves degenerate (unmeasured) sizes to wide", () => {
expect(geometryFor(0, 0).mode).toBe("wide");
});
+
+ it("derives the wide unit from the canvas height so the 4 rows fill it", () => {
+ // 4·unit + 3·gap + 2·margin ≈ height: an 800px canvas gets ~160px tiles,
+ // NOT the old fixed 120px (the "squares are so small" complaint).
+ const geometry = geometryFor(1440, 800);
+ const filled = 4 * geometry.unit.height + 3 * geometry.unit.gap + 2 * geometry.margin;
+ expect(geometry.unit.height).toBeGreaterThanOrEqual(140);
+ expect(Math.abs(filled - 800)).toBeLessThanOrEqual(8);
+ expect(geometry.unit.width).toBe(geometry.unit.height);
+ });
+
+ it("clamps the wide unit to its band on very short and very tall canvases", () => {
+ const short = geometryFor(1200, 300);
+ const huge = geometryFor(5000, 4000); // wide (width ≥ height) with a huge height
+ expect(short.unit.height).toBe(112);
+ expect(huge.unit.height).toBe(184);
+ // The margin never collapses (the "no margins" complaint) nor dominates.
+ expect(short.margin).toBeGreaterThanOrEqual(24);
+ expect(huge.margin).toBeLessThanOrEqual(64);
+ });
+
+ it("always carries a positive margin (the tile field never hugs the edge)", () => {
+ const sizes: [number, number][] = [
+ [1920, 1080],
+ [390, 844],
+ [0, 0],
+ [1200, 300],
+ ];
+ for (const [w, h] of sizes) {
+ expect(geometryFor(w, h).margin).toBeGreaterThan(0);
+ }
+ });
+
+ it("keeps the tall unit fixed with a gap (its columns are fluid)", () => {
+ expect(TALL.unit.width).toBe(0);
+ expect(TALL.unit.height).toBeGreaterThanOrEqual(120);
+ expect(TALL.unit.gap).toBeGreaterThan(8);
+ });
});
describe("clampSpan", () => {
@@ -429,12 +467,15 @@ describe("geometry-agnostic persistence", () => {
describe("type surface", () => {
it("exposes the geometry fields the picker and grid need", () => {
+ // A 500px-tall canvas: the raw unit (100) clamps UP to the 112px minimum,
+ // so the gap is 12px and the margin 37px (never the old fixed 8px gap).
const geometry: GridGeometry = geometryFor(1000, 500);
- expect([geometry.mode, geometry.flowRows, geometry.flowCols, geometry.unit.gap]).toEqual([
- "wide",
- 4,
- 0,
- 8,
- ]);
+ expect([
+ geometry.mode,
+ geometry.flowRows,
+ geometry.flowCols,
+ geometry.unit.gap,
+ geometry.margin,
+ ]).toEqual(["wide", 4, 0, 12, 37]);
});
});