fix(player): the YouTube player keeps its size on a short window and when maximised
Two layout bugs since the player grew a side panel, both reported from prod. Maximised (F) on a wide window, the video was a thin line across the screen: the side-panel layout aligns the stage `self-start`, and Chrome applies a flex item's self-alignment to an absolutely positioned box too, so the fixed stage took its content's height — 0 px. The maximised stage now stretches between its insets whatever the layout asks of it. On a short window under 1280 px (a laptop at 100%, a 1080p screen at 175%) only the side strips were left: the card narrows so the chrome under the video fits, but that chrome grows as the card narrows (its text wraps), and the loop ran the card down to 2 px. The video now never goes narrower than 36rem to make room; past that the card scrolls. Measured before/after (localdev, real YouTube iframe): 1097×520 open 0×0 → 610×343; 1920×911 maximised 1920×0 → 1920×911; 1440×760 and 1366×650 maximised fill the window; leaving maximise restores the card. Held by an e2e test at both sizes (red on the old code: 0 px wide, and 911 ≠ 0), green on Chrome, WebKit and iPad.
This commit is contained in:
@@ -465,7 +465,9 @@ describe("PlayerModal — fits the window's height", () => {
|
||||
<PlayerModal video={A} queue={[A, B]} startIndex={0} onClose={() => {}} onState={() => {}} />,
|
||||
);
|
||||
const row = document.querySelector<HTMLElement>(".player-stage")!.parentElement!.parentElement!;
|
||||
expect(row.style.maxWidth).toBe("min(63rem, calc((100dvh - 3rem - 160px) * 16 / 9 + 7rem))");
|
||||
expect(row.style.maxWidth).toBe(
|
||||
"min(63rem, calc(max(36rem, (100dvh - 3rem - 160px) * 16 / 9) + 7rem))",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -510,7 +512,7 @@ describe("PlayerModal — the side panel on a wide window", () => {
|
||||
expect(screen.queryByRole("button", { name: "player.description" })).toBeNull();
|
||||
// The row's width: the video at the window's height, plus the panel and the side strips.
|
||||
expect(card.parentElement!.style.maxWidth).toBe(
|
||||
"calc((100dvh - 3rem - 0px) * 16 / 9 + 20rem + 7rem)",
|
||||
"calc(max(36rem, (100dvh - 3rem - 0px) * 16 / 9) + 20rem + 7rem)",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ function canScrollBy(el: HTMLElement | null, dir: -1 | 1): boolean {
|
||||
// a rem width, so it grows with the text size; the video takes what is left.
|
||||
const WIDE_PLAYER_PX = 1280;
|
||||
const SIDE_PANEL = "20rem";
|
||||
// The narrowest the video gets to make room for the chrome under it (see the card's maxWidth).
|
||||
const MIN_VIDEO = "36rem";
|
||||
|
||||
// The meta line's items each lead with a "·" that parts them from what stands before: the channel name
|
||||
// on its line. In the wide panel the meta has a line of its own, where the first "·" would lead nothing.
|
||||
@@ -611,6 +613,10 @@ export default function PlayerModal({
|
||||
onLoadVideo: loadVideo,
|
||||
};
|
||||
|
||||
// The card's width budget (its maxWidth, below): the video's, and the side strips' gutters.
|
||||
const videoWidth = `max(${MIN_VIDEO}, (100dvh - 3rem - ${below}px) * 16 / 9)`;
|
||||
const gutters = hasQueue ? " + 7rem" : "";
|
||||
|
||||
// Portaled to <body>, like every other full-screen overlay here. A fixed, full-viewport modal
|
||||
// must not render inside the page scroller: once that scroller is a stacking context (its
|
||||
// edge-fade mask makes it one) this z-overlay is scoped inside it, and the nav rail (z-rail) and the
|
||||
@@ -636,12 +642,13 @@ export default function PlayerModal({
|
||||
// The design width (63rem with the side strips, else 56rem), narrowed so the 16:9 video plus the
|
||||
// chrome under it fits the window's height less the backdrop's padding (`p-6` = 3rem).
|
||||
// Wide: no design cap — the video is as wide as the window's height allows, plus the side panel.
|
||||
// Never narrower than MIN_VIDEO: the chrome under the video grows as the card narrows (its text
|
||||
// wraps), so on a short window fitting it was a loop that ran the card down to 2 px (a laptop
|
||||
// at 100%, a 1080p screen at 175%). Past the floor the card scrolls instead.
|
||||
style={{
|
||||
maxWidth: wide
|
||||
? `calc((100dvh - 3rem - ${below}px) * 16 / 9 + ${SIDE_PANEL}${hasQueue ? " + 7rem" : ""})`
|
||||
: `min(${hasQueue ? "63rem" : "56rem"}, calc((100dvh - 3rem - ${below}px) * 16 / 9${
|
||||
hasQueue ? " + 7rem" : ""
|
||||
}))`,
|
||||
? `calc(${videoWidth} + ${SIDE_PANEL}${gutters})`
|
||||
: `min(${hasQueue ? "63rem" : "56rem"}, calc(${videoWidth}${gutters}))`,
|
||||
}}
|
||||
>
|
||||
{hasQueue && (
|
||||
|
||||
Reference in New Issue
Block a user