Landscape is the one shape this layout has never really fitted, and the audit was calling two separate faults there. The felt worked its own height out as `100vh - bar - 7rem`, guessing that everything above it came to 7rem. That is 108px at 640x360, which the guess covers, and 119px at 844x390, which it doesn't — so on an iPhone on its side the bottom row of seats sat up to 26px inside the action bar. The measurement it needed was already being published for the wide layout: the bar's real height, the wrap's real offset and the bottom seat's overhang, all taken off the window. It uses that now. The floor under it drops from 220px to 96px, because 220 was not a size a 390px-tall window has to give — holding out for it was itself how seats ended up behind the Fold button, which is the one thing the floor exists to prevent. That still left the header taking two rows, and the second row is 44px of the 390. The title block and the five buttons came to 829px inside a 765px header, missing one line by 64px. The page title goes — the one thing on this screen a dealer already knows — hidden from the eye rather than removed, so the heading is still there for a screen reader. The buttons tighten across but never down: they stay a 44px thumb target. The seats themselves were spaced by angle, which is only even spacing on a circle. On a 601x126 felt, ten seats at 36° apart put four of them down each steep end, 20px apart, with pucks 72px tall — a seat's centre belonged to its neighbour, so tapping a player got the wrong one. They now walk the rim by distance instead, which puts them along the two long rails the way a real table seats people. Closest neighbours go from 33px apart to 59px. Measured at 640x360, 667x375 and 844x390 with 9-, 10- and 12-seat tables: every combination the audit used to fail is clean, bar a 10-seat table at 640x360, where two seats at the end of a rim that flat still overlap. Portrait, tablet and desktop are unchanged — the floor never bound there, and on a rounder felt walking the rim gives back the same angles it always did. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
74 lines
3.3 KiB
JavaScript
74 lines
3.3 KiB
JavaScript
// How tall the felt is allowed to be.
|
|
//
|
|
// The bar is fixed to the bottom of the screen and grows with the hand, so the table
|
|
// has to be told what is left. Everything below is the arithmetic that decides it —
|
|
// the part that can be wrong without anyone noticing until a seat is behind the Fold
|
|
// button.
|
|
const test = require("node:test");
|
|
const { describe } = require("node:test");
|
|
const assert = require("node:assert");
|
|
const { ActionBar } = require("./load");
|
|
|
|
// window height, bar height, and how far down the page the felt starts.
|
|
const room = (windowHeight, barHeight, top) => ActionBar.feltRoomFor(windowHeight, barHeight, top);
|
|
|
|
describe("the room left for the felt", () => {
|
|
test("is what the window has after the bar, the header and the bottom seat", () => {
|
|
const reserved = ActionBar.SEAT_OVERHANG_PX + ActionBar.FELT_GAP_PX;
|
|
assert.equal(room(900, 127, 146), 900 - 127 - 146 - reserved);
|
|
});
|
|
|
|
test("shrinks when the bar grows mid-hand", () => {
|
|
// Side pots make the bar taller exactly when the dealer most needs to see who
|
|
// is still in.
|
|
const quiet = room(900, 117, 146);
|
|
const sidePots = room(900, 172, 146);
|
|
assert.equal(quiet - sidePots, 55);
|
|
});
|
|
|
|
test("leaves the bottom seat's overhang out of it", () => {
|
|
// The seat pucks hang below the felt's edge; the felt has to stop short by that
|
|
// much or the ring ends up in the bar even though the felt doesn't.
|
|
assert.ok(ActionBar.SEAT_OVERHANG_PX > 30, "half a puck");
|
|
assert.ok(room(900, 127, 146) < 900 - 127 - 146);
|
|
});
|
|
});
|
|
|
|
describe("a window too short to give what was asked", () => {
|
|
// A phone on its side: 390px tall, 127px of that is the bar and 119px is
|
|
// everything above the felt. What's left is under 100.
|
|
const landscape = () => room(390, 127, 119);
|
|
|
|
test("gets the measurement, not a comfortable number", () => {
|
|
// The floor used to be 220px, which this window hasn't got. Holding out for it
|
|
// put the bottom row of seats 26px inside the action bar — the exact fault the
|
|
// measurement exists to prevent.
|
|
assert.ok(landscape() < 150, `${landscape()}px`);
|
|
assert.ok(landscape() + 119 + 127 <= 390, "the felt still ends above the bar");
|
|
});
|
|
|
|
test("still draws a table rather than a line", () => {
|
|
assert.equal(ActionBar.feltRoomFor(200, 160, 100), ActionBar.FELT_FLOOR_PX);
|
|
assert.equal(ActionBar.feltRoomFor(0, 0, 0), ActionBar.FELT_FLOOR_PX);
|
|
});
|
|
|
|
test("never returns something a browser can't lay out", () => {
|
|
[[390, 300, 200], [100, 90, 90], [-10, 0, 0]].forEach(([h, bar, top]) => {
|
|
const answer = ActionBar.feltRoomFor(h, bar, top);
|
|
assert.ok(Number.isFinite(answer) && answer > 0, `${h}/${bar}/${top} gave ${answer}`);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("a window with room to spare", () => {
|
|
test("hands over everything that's left, floor or no floor", () => {
|
|
const reserved = ActionBar.SEAT_OVERHANG_PX + ActionBar.FELT_GAP_PX;
|
|
assert.equal(room(1000, 127, 146), 1000 - 127 - 146 - reserved);
|
|
assert.equal(room(1366, 120, 206), 1366 - 120 - 206 - reserved);
|
|
});
|
|
|
|
test("gives a whole number of pixels", () => {
|
|
assert.equal(room(844.4, 127.6, 223.2) % 1, 0);
|
|
});
|
|
});
|