Hold'em: every seat on the ring is the same seat, and the ring is measured before it is believed #122
@@ -3200,12 +3200,98 @@ body.hasActionBar #holdemApp {
|
|||||||
font-size: var(--fs-sm);
|
font-size: var(--fs-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionWho {
|
/* Who is being waited on, and what they have behind.
|
||||||
|
|
||||||
|
It already said `text-overflow: ellipsis`, and the ellipsis never once appeared. A
|
||||||
|
flex item's `min-width` is `auto`, which means "never smaller than your own content",
|
||||||
|
so instead of giving way this pushed the clock and the undo pill onto a third line —
|
||||||
|
and the row is in a bar fixed to the bottom, so a third line comes straight off the
|
||||||
|
felt. Measured at 360x640 mid-street: "Dee · $600 behind" left the bar 151px tall,
|
||||||
|
"Christopher · $1,200 behind" made it 173, and the table resized between one player
|
||||||
|
and the next. Whose turn it was decided how big the felt was.
|
||||||
|
|
||||||
|
`min-width: 0` is what lets the ellipsis do the job it was already written for. What
|
||||||
|
gives way is the name — see .actionWhoName. */
|
||||||
|
/* The second line of the status block, and it is a line because it is written as one.
|
||||||
|
|
||||||
|
`flex: 1 1 100%` is what makes it take a line of its own inside a wrapping parent —
|
||||||
|
which is the whole point. Flex settles its line breaks before it decides what to
|
||||||
|
shrink, so a long name in the wrapping row did not give way, it pushed the clock and
|
||||||
|
the undo pill onto a third line. Here the three of them are on a row that can shrink
|
||||||
|
instead of a row that can wrap. */
|
||||||
|
.actionActing {
|
||||||
|
/* Only what it holds, when all it holds is the undo pill. Claiming a line
|
||||||
|
unconditionally cost one in the state that can least afford it: while a board is
|
||||||
|
running out there is nobody to act, so this row is the pill alone — it used to sit
|
||||||
|
up beside "All-in — running out the board" and now it wanted a line of its own.
|
||||||
|
That is 22px off the felt on the twelve-handed 360x640 table where the pucks are
|
||||||
|
already at their 44px floor, and the sweep caught it as a seat's "ALL IN" going
|
||||||
|
back under the blind badge next door. */
|
||||||
|
flex: 0 1 auto;
|
||||||
|
display: flex;
|
||||||
|
/* The one that matters. Left wrapping, this row reproduced the fault it was made to
|
||||||
|
fix one level down: the undo pill is a fixed 9rem, so a long name pushed it onto a
|
||||||
|
line of its own and the block went back to being 84px for some players and 66 for
|
||||||
|
others. Not wrapping is what forces the shrink, and the shrink is what the name
|
||||||
|
is set up to absorb. */
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: var(--sp-1) var(--sp-2);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And a line of its own the moment there is somebody being waited on, which is the case
|
||||||
|
this row exists for — the name, what they have behind, the clock, and the way back.
|
||||||
|
`flex: 1 1 100%` is what takes the line; nowrap above is what makes the name give way
|
||||||
|
inside it instead of pushing the pill onto another one. */
|
||||||
|
.actionActing:has(.actionWho) {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On a phone the pill gives some of its width back to the name beside it.
|
||||||
|
10rem is 160px, and at 360px the acting row has 319 to spend: the pill took half, the
|
||||||
|
clock took 50, and what was left over went to the player being waited on — measured,
|
||||||
|
"Christopher" was cut to 23px of its 75, which reads "Ch…". A third of the row is
|
||||||
|
enough for a safety net when the other thing on the line is the question the dealer is
|
||||||
|
actually answering.
|
||||||
|
|
||||||
|
Still one width at a time, which is the property that matters: the pill names the last
|
||||||
|
action, so its label changes on every tap, and it must not resize under a thumb.
|
||||||
|
A media query keeps that true. `min(10rem, 34%)` does not — the percentage resolves
|
||||||
|
against this row, the row is sized to its contents, and the two chase each other: the
|
||||||
|
sweep caught the pill drawn 38px wide at ten viewports. */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.actionUndo {
|
||||||
|
width: 7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pushed to the far end of its own line, where it was before — the difference is that
|
||||||
|
it is now the end of a line this block chose rather than wherever the text ran out. */
|
||||||
|
.actionActing .actionUndo {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionWho {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The half that shrinks. */
|
||||||
|
.actionWhoName {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And the half that doesn't. The same rule the seat pucks keep: a number read wrong is
|
||||||
|
a worse failure than a name read short, so the stack keeps every digit and the name
|
||||||
|
is what loses characters. */
|
||||||
|
.actionWhoStack {
|
||||||
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stopwatch, not a shot clock: it colours up as a prompt to the dealer, and does
|
/* Stopwatch, not a shot clock: it colours up as a prompt to the dealer, and does
|
||||||
@@ -5248,6 +5334,13 @@ body.hasActionBar #toastHost {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The rail is 190px wide, so its acting row is a stack rather than a line — every
|
||||||
|
one of these readings is on its own line here regardless, and squeezing a name
|
||||||
|
into a fifth of the column would be shrinking to fit a row that isn't there. */
|
||||||
|
.actionActing {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.actionUndo {
|
.actionUndo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,13 @@ const ActionBar = {
|
|||||||
// What is under the felt is the bar in one layout and the two links in the
|
// What is under the felt is the bar in one layout and the two links in the
|
||||||
// other. Either way it is the same question — how much of the bottom is
|
// other. Either way it is the same question — how much of the bottom is
|
||||||
// already spoken for — so it is the same argument.
|
// already spoken for — so it is the same argument.
|
||||||
this.publishFeltRoom(rail ? this.roomBelowFelt() : height);
|
//
|
||||||
|
// Except that the felt is asked a slightly different question from everything
|
||||||
|
// else that reads --actionBarHeight. The toast and the scroll padding want to
|
||||||
|
// know what is covering the bottom of the window *right now*; the table wants
|
||||||
|
// to know how big to be, and being a different size for the few seconds a pad
|
||||||
|
// is open over it is not an answer, it is a jolt. See restingBarHeight.
|
||||||
|
this.publishFeltRoom(rail ? this.roomBelowFelt() : this.restingBarHeight(height));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Kept so anything that moves the table without touching the bar can ask for
|
// Kept so anything that moves the table without touching the bar can ask for
|
||||||
@@ -58,6 +64,42 @@ const ActionBar = {
|
|||||||
publish();
|
publish();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// How tall the bar is when it is only a bar.
|
||||||
|
//
|
||||||
|
// The raise pad is 57px taller than the row it replaces, and the felt was sized
|
||||||
|
// against it. Measured with five players nine-handed, pressing Raise:
|
||||||
|
//
|
||||||
|
// 1280x900 felt 738x532 -> 669x481, pucks 120 -> 109, worst seat moved 73px
|
||||||
|
// 360x640 felt 283x329 -> 283x279, pucks 44 -> 48, worst seat moved 50px
|
||||||
|
// 390x844 felt 309x463 -> 309x451, worst seat moved 11px
|
||||||
|
//
|
||||||
|
// The whole table jumped, re-ran the ten-candidate width search, redrew at a
|
||||||
|
// different size — and then undid all of it when the dealer pressed Raise to. Every
|
||||||
|
// raise, all night, at the one moment they are reading the felt to decide what to
|
||||||
|
// type. A phone held sideways already did the right thing here and scored 0px: the
|
||||||
|
// rail is a column down the side, so its height never was the table's business.
|
||||||
|
//
|
||||||
|
// A pad over the bar is a transient surface, and the thing a transient surface must
|
||||||
|
// not do is take the context away from underneath itself. So the felt is sized
|
||||||
|
// against the bar the thumb is coming back to, and the pad grows up over the bottom
|
||||||
|
// of the felt for as long as it is open.
|
||||||
|
//
|
||||||
|
// Only the felt's question is answered this way. --actionBarHeight stays what it has
|
||||||
|
// always been — what is actually covering the bottom of the window — because that is
|
||||||
|
// what the toast has to clear and what the page has to scroll past.
|
||||||
|
//
|
||||||
|
// The remembered height goes stale if the window changes size while the pad is open,
|
||||||
|
// which on a phone means turning it over mid-raise. The next publish after the pad
|
||||||
|
// closes is a measurement, so it puts itself right; a stale felt for the seconds in
|
||||||
|
// between is the same trade being made everywhere else here.
|
||||||
|
restingBarHeight(measured) {
|
||||||
|
if (!this.raiseOpen) {
|
||||||
|
this.barHeightAtRest = measured;
|
||||||
|
return measured;
|
||||||
|
}
|
||||||
|
return this.barHeightAtRest === undefined ? measured : this.barHeightAtRest;
|
||||||
|
},
|
||||||
|
|
||||||
// Is the bar a rail down the side rather than a bar across the bottom?
|
// Is the bar a rail down the side rather than a bar across the bottom?
|
||||||
//
|
//
|
||||||
// Asked of the stylesheet rather than answered again here. The layout is chosen by
|
// Asked of the stylesheet rather than answered again here. The layout is chosen by
|
||||||
@@ -336,20 +378,59 @@ const ActionBar = {
|
|||||||
row.appendChild(runout);
|
row.appendChild(runout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The second line of this block, held as a line rather than left to whatever the
|
||||||
|
// wrapping happens to produce.
|
||||||
|
//
|
||||||
|
// The row wraps, and flex decides its line breaks before it decides what to
|
||||||
|
// shrink — so a name too long for the space left beside the clock and the undo
|
||||||
|
// pill did not give way, it pushed them onto a third line. That is 22px, and the
|
||||||
|
// bar is fixed to the bottom, so it came off the felt: measured at 360x640
|
||||||
|
// mid-street, "Dee · $600 behind" left the table 283x329 and "Christopher ·
|
||||||
|
// $1,200 behind" made it 283x310, with every seat moving 24px between one player
|
||||||
|
// and the next. Whose turn it was decided how big the table was.
|
||||||
|
//
|
||||||
|
// On its own line the three of them are a row that can shrink instead of a row
|
||||||
|
// that can wrap, and the block is two lines whoever is acting.
|
||||||
|
const acting = document.createElement("div");
|
||||||
|
acting.className = "actionActing";
|
||||||
|
|
||||||
const index = this.state.actingSeatIndex;
|
const index = this.state.actingSeatIndex;
|
||||||
if (index !== null && index !== undefined && !Betting.isHandOver(this.state)) {
|
if (index !== null && index !== undefined && !Betting.isHandOver(this.state)) {
|
||||||
const player = this.state.seats[index];
|
const player = this.state.seats[index];
|
||||||
|
// Two spans rather than one sentence, because only one half of it is
|
||||||
|
// allowed to give way. This row wraps, and a name long enough to push the
|
||||||
|
// clock and the undo pill onto a third line takes 22px off the felt — so
|
||||||
|
// whose turn it is decided how big the table was. Measured at 360x640:
|
||||||
|
// "Dee · $600 behind" left the bar 151px tall and "Christopher · $1,200
|
||||||
|
// behind" made it 173, and the felt resized between one player and the next.
|
||||||
|
//
|
||||||
|
// So the name shrinks and the number does not. A name cut to "Christophe…"
|
||||||
|
// is still the player the dealer is looking at; a stack cut to "$1,2…" is a
|
||||||
|
// figure they would have to go and find somewhere else.
|
||||||
const who = document.createElement("span");
|
const who = document.createElement("span");
|
||||||
who.className = "actionWho";
|
who.className = "actionWho";
|
||||||
who.textContent = `${HoldemState.displayName(player, index)} · ${Money.format(Betting.stackOf(this.state, player))} behind`;
|
|
||||||
row.appendChild(who);
|
const whoName = document.createElement("span");
|
||||||
|
whoName.className = "actionWhoName";
|
||||||
|
whoName.textContent = HoldemState.displayName(player, index);
|
||||||
|
// The whole of it, for the one name on the narrowest phone that still has to
|
||||||
|
// give up a few characters. Same arrangement the seat pucks have.
|
||||||
|
whoName.title = whoName.textContent;
|
||||||
|
who.appendChild(whoName);
|
||||||
|
|
||||||
|
const whoStack = document.createElement("span");
|
||||||
|
whoStack.className = "actionWhoStack";
|
||||||
|
whoStack.textContent = ` · ${Money.format(Betting.stackOf(this.state, player))} behind`;
|
||||||
|
who.appendChild(whoStack);
|
||||||
|
|
||||||
|
acting.appendChild(who);
|
||||||
|
|
||||||
const tank = document.createElement("span");
|
const tank = document.createElement("span");
|
||||||
tank.className = "actionTank";
|
tank.className = "actionTank";
|
||||||
tank.id = "actionTank";
|
tank.id = "actionTank";
|
||||||
tank.title = "How long this player has been thinking";
|
tank.title = "How long this player has been thinking";
|
||||||
tank.textContent = "0:00";
|
tank.textContent = "0:00";
|
||||||
row.appendChild(tank);
|
acting.appendChild(tank);
|
||||||
|
|
||||||
// Rendered every time and shown by the tick once the wait is long enough
|
// Rendered every time and shown by the tick once the wait is long enough
|
||||||
// to be worth it, so a clock never costs a re-render at the moment the
|
// to be worth it, so a clock never costs a re-render at the moment the
|
||||||
@@ -375,7 +456,7 @@ const ActionBar = {
|
|||||||
clock.title = `${this.CLOCK_THINK_S} seconds, then a ${this.CLOCK_COUNTDOWN_S}-second countdown`;
|
clock.title = `${this.CLOCK_THINK_S} seconds, then a ${this.CLOCK_COUNTDOWN_S}-second countdown`;
|
||||||
clock.addEventListener("click", () => this.callClock());
|
clock.addEventListener("click", () => this.callClock());
|
||||||
}
|
}
|
||||||
row.appendChild(clock);
|
acting.appendChild(clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A standing control rather than a toast per action: a dealer taps this bar
|
// A standing control rather than a toast per action: a dealer taps this bar
|
||||||
@@ -410,9 +491,12 @@ const ActionBar = {
|
|||||||
key.textContent = this.KEY_HINTS.actionUndo;
|
key.textContent = this.KEY_HINTS.actionUndo;
|
||||||
undo.appendChild(key);
|
undo.appendChild(key);
|
||||||
undo.addEventListener("click", () => this.hooks.undoLast());
|
undo.addEventListener("click", () => this.hooks.undoLast());
|
||||||
row.appendChild(undo);
|
acting.appendChild(undo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nothing to act on and nothing to take back — no second line at all.
|
||||||
|
if (acting.childElementCount) row.appendChild(acting);
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -156,9 +156,19 @@ describe("controls that must not resize under a thumb", () => {
|
|||||||
test("the undo pill has a fixed width, not a content width", () => {
|
test("the undo pill has a fixed width, not a content width", () => {
|
||||||
// It names the last action, so its label changes on every tap. Measured across
|
// It names the last action, so its label changes on every tap. Measured across
|
||||||
// one hand before this: 148, 110, 109, 108, 109, 110, 120.
|
// one hand before this: 148, 110, 109, 108, 109, 110, 120.
|
||||||
const rule = css.match(/\.actionUndo\s*\{[^}]*\}/);
|
//
|
||||||
assert.ok(rule, ".actionUndo rule not found");
|
// A length at every breakpoint, never a percentage: `min(10rem, 34%)` was tried
|
||||||
assert.match(rule[0], /width:\s*[0-9.]+rem/);
|
// and the percentage resolves against the row it sits in, which is sized to its
|
||||||
|
// own contents — the two chase each other, and the sweep caught the pill drawn
|
||||||
|
// 38px wide at ten viewports. A phone gets a narrower one through a media query
|
||||||
|
// instead, which is still one width at a time.
|
||||||
|
const rules = [...css.matchAll(/\.actionUndo\s*\{([^}]*)\}/g)].map(m => m[1]);
|
||||||
|
assert.ok(rules.length, ".actionUndo rule not found");
|
||||||
|
rules.filter(body => /(?<![\w-])width:/.test(body)).forEach(body =>
|
||||||
|
assert.match(body, /(?<![\w-])width:\s*([0-9.]+rem|100%)/,
|
||||||
|
"a content width would let the pill resize under the dealer's thumb"));
|
||||||
|
assert.ok(rules.some(body => /(?<![\w-])width:\s*[0-9.]+rem/.test(body)),
|
||||||
|
"the pill should still state a fixed width somewhere");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("and its label is the part that ellipsizes", () => {
|
test("and its label is the part that ellipsizes", () => {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const { ActionBar, Render } = require("./load");
|
|||||||
|
|
||||||
const CSS = fs.readFileSync(
|
const CSS = fs.readFileSync(
|
||||||
path.join(__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "css", "casino", "holdem.css"), "utf8");
|
path.join(__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "css", "casino", "holdem.css"), "utf8");
|
||||||
|
const CSS_JS = fs.readFileSync(
|
||||||
|
path.join(__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "js", "casino", "holdem", "actionBar.js"), "utf8");
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -160,6 +162,138 @@ describe("the button seat's name", () => {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("the table is sized against the bar, not against a pad over it", () => {
|
||||||
|
// The raise pad is 57px taller than the row it replaces, and the felt was sized
|
||||||
|
// against it. Measured with five players nine-handed, pressing Raise:
|
||||||
|
//
|
||||||
|
// 1280x900 felt 738x532 -> 669x481, pucks 120 -> 109, worst seat moved 73px
|
||||||
|
// 360x640 felt 283x329 -> 283x279, pucks 44 -> 48, worst seat moved 50px
|
||||||
|
//
|
||||||
|
// The whole table jumped, re-ran the ten-candidate width search, and undid all of it
|
||||||
|
// when the dealer pressed Raise to. Every raise, all night, at the one moment they
|
||||||
|
// are reading the felt to decide what to type.
|
||||||
|
//
|
||||||
|
// A phone held sideways already scored 0px, because the rail is a column down the
|
||||||
|
// side and its height never was the table's business. This is the upright layout
|
||||||
|
// being held to what the sideways one already does.
|
||||||
|
test.afterEach(() => { ActionBar.raiseOpen = false; delete ActionBar.barHeightAtRest; });
|
||||||
|
|
||||||
|
test("a closed bar is measured, and remembered", () => {
|
||||||
|
ActionBar.raiseOpen = false;
|
||||||
|
assert.equal(ActionBar.restingBarHeight(151), 151);
|
||||||
|
assert.equal(ActionBar.barHeightAtRest, 151);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and an open pad gets the height the bar had before it opened", () => {
|
||||||
|
ActionBar.raiseOpen = false;
|
||||||
|
ActionBar.restingBarHeight(151);
|
||||||
|
ActionBar.raiseOpen = true;
|
||||||
|
assert.equal(ActionBar.restingBarHeight(208), 151,
|
||||||
|
"the felt would resize under the dealer's thumb mid-raise");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and gets it back the moment the pad closes", () => {
|
||||||
|
ActionBar.raiseOpen = false;
|
||||||
|
ActionBar.restingBarHeight(151);
|
||||||
|
ActionBar.raiseOpen = true;
|
||||||
|
ActionBar.restingBarHeight(208);
|
||||||
|
ActionBar.raiseOpen = false;
|
||||||
|
assert.equal(ActionBar.restingBarHeight(172), 172,
|
||||||
|
"a bar that really did grow — a side pot to show — still takes its room");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a pad open before anything was ever measured takes what it can see", () => {
|
||||||
|
// Nothing has been remembered yet, so the measurement is the only answer there
|
||||||
|
// is. Better a felt sized for the pad than one sized for undefined.
|
||||||
|
ActionBar.raiseOpen = true;
|
||||||
|
delete ActionBar.barHeightAtRest;
|
||||||
|
assert.equal(ActionBar.restingBarHeight(208), 208);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("the bar that is actually covering the window is still reported honestly", () => {
|
||||||
|
// Only the felt's question is answered from the resting height. The toast has to
|
||||||
|
// clear whatever is really there, and so does the page's scroll padding, so
|
||||||
|
// --actionBarHeight stays a measurement.
|
||||||
|
const publish = CSS_JS.slice(CSS_JS.indexOf("const publish = () => {"));
|
||||||
|
const body = publish.slice(0, publish.indexOf("};"));
|
||||||
|
assert.match(body, /--actionBarHeight[^\n]*Math\.round\(height\)/,
|
||||||
|
"--actionBarHeight should still be the measured height");
|
||||||
|
assert.match(body, /publishFeltRoom\(.*restingBarHeight\(height\)/,
|
||||||
|
"the felt should be the one asking for the resting height");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("the status block is the same number of lines whoever is acting", () => {
|
||||||
|
// The bar is fixed to the bottom, so a line added to this block comes straight off
|
||||||
|
// the felt. The block wraps, and flex settles its line breaks before it decides what
|
||||||
|
// to shrink — so a name too long for the space left beside the clock and the undo
|
||||||
|
// pill did not give way, it pushed them onto a third line.
|
||||||
|
//
|
||||||
|
// Measured at 360x640 mid-street: "Dee · $600 behind" left the table 283x329 and
|
||||||
|
// "Christopher · $1,200 behind" made it 283x310, with every seat moving 24px between
|
||||||
|
// one player and the next. Whose turn it was decided how big the table was.
|
||||||
|
const acting = CSS.match(/\.actionActing \{([^}]*)\}/);
|
||||||
|
const who = CSS.match(/\n\.actionWho \{([^}]*)\}/);
|
||||||
|
const name = CSS.match(/\.actionWhoName \{([^}]*)\}/);
|
||||||
|
const stack = CSS.match(/\.actionWhoStack \{([^}]*)\}/);
|
||||||
|
|
||||||
|
test("the acting row takes a line of its own, once there is somebody on it", () => {
|
||||||
|
assert.ok(acting, ".actionActing rule not found");
|
||||||
|
assert.match(acting[1], /flex:\s*0 1 auto/,
|
||||||
|
"the pill on its own should not claim a line");
|
||||||
|
const withActor = CSS.match(/\.actionActing:has\(\.actionWho\) \{([^}]*)\}/);
|
||||||
|
assert.ok(withActor, "nothing gives the acting row its own line");
|
||||||
|
assert.match(withActor[1], /flex:\s*1 1 100%/,
|
||||||
|
"without a full basis it shares whatever line the wrap left it");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and only then — a run-out has nobody to act and cannot spare the line", () => {
|
||||||
|
// While a board is running out there is nobody to act, so this row is the undo
|
||||||
|
// pill alone. Claiming a line for it is 22px off the felt on the twelve-handed
|
||||||
|
// 360x640 table where the pucks are already at their 44px floor, and the sweep
|
||||||
|
// caught that as a seat's "ALL IN" going back under the blind badge next door.
|
||||||
|
assert.doesNotMatch(acting[1], /flex:\s*1 1 100%/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and it shrinks rather than wrapping", () => {
|
||||||
|
// Left wrapping, this row reproduced the fault one level down: the undo pill is
|
||||||
|
// a fixed width, so a long name pushed it onto a line of its own and the block
|
||||||
|
// was 84px for some players and 66 for others.
|
||||||
|
assert.match(acting[1], /flex-wrap:\s*nowrap/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("the name is what gives way", () => {
|
||||||
|
// `min-width` on a flex item defaults to `auto` — "never smaller than your own
|
||||||
|
// content" — which is why the ellipsis this was written with never once appeared.
|
||||||
|
assert.ok(who && name, "the actor should be a name and an amount, not one string");
|
||||||
|
assert.match(who[1], /min-width:\s*0/);
|
||||||
|
assert.match(name[1], /min-width:\s*0/);
|
||||||
|
assert.match(name[1], /text-overflow:\s*ellipsis/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and the stack is what does not", () => {
|
||||||
|
// The same rule the seat pucks keep: a number read wrong is a worse failure than
|
||||||
|
// a name read short, so the stack keeps every digit.
|
||||||
|
assert.ok(stack, ".actionWhoStack rule not found");
|
||||||
|
assert.match(stack[1], /flex:\s*none/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("except in the rail, which is a column and has no line to shrink into", () => {
|
||||||
|
// 190px wide: every one of these readings is on its own line there regardless,
|
||||||
|
// and squeezing a name into a fifth of the column would be shrinking to fit a
|
||||||
|
// row that isn't there.
|
||||||
|
const rules = [...CSS.matchAll(/\.actionActing \{([^}]*)\}/g)].map(m => m[1]);
|
||||||
|
assert.equal(rules.length, 2,
|
||||||
|
"one rule for the bar and one for the rail — no more, no fewer");
|
||||||
|
assert.ok(rules.some(body => /flex-wrap:\s*wrap\s*;/.test(body)),
|
||||||
|
"the rail should say what it wants instead");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe("how much of the bottom is spoken for", () => {
|
describe("how much of the bottom is spoken for", () => {
|
||||||
// feltRoomFor asks one question — what is under the felt — and gets two different
|
// feltRoomFor asks one question — what is under the felt — and gets two different
|
||||||
// answers depending on the layout. Under the bar it is the bar. Under the rail it is
|
// answers depending on the layout. Under the bar it is the bar. Under the rail it is
|
||||||
|
|||||||
Reference in New Issue
Block a user