diff --git a/frontend/src/components/page-header/page-header.ts b/frontend/src/components/page-header/page-header.ts index a441b19..d67fed1 100644 --- a/frontend/src/components/page-header/page-header.ts +++ b/frontend/src/components/page-header/page-header.ts @@ -162,6 +162,15 @@ export class PageHeader extends LitElement { @state() private collapsed: ReadonlySet = new Set(); + /** + * Whether the count has been given up. Derived, like `collapsed`. + * + * It is the last thing to yield and the only thing here that is + * neither an identity nor an action — see `measureFit`. + */ + @state() + private countCollapsed = false; + @state() private menuOpen = false; @@ -531,12 +540,6 @@ export class PageHeader extends LitElement { if (!header) return; - if (this.actions.length === 0) { - this.commitCollapsed(new Set()); - - return; - } - const buttons = new Map(); for (const el of this.renderRoot.querySelectorAll( @@ -549,6 +552,7 @@ export class PageHeader extends LitElement { const more = this.moreButton; const title = this.renderRoot.querySelector('h1'); + const count = this.renderRoot.querySelector('.count'); /** * Nothing is clipped — which is not the same as the header not @@ -570,6 +574,8 @@ export class PageHeader extends LitElement { if (more) more.hidden = true; + if (count) count.hidden = false; + const collapsed = new Set(); if (!fits()) { @@ -586,7 +592,42 @@ export class PageHeader extends LitElement { } } - this.commitCollapsed(collapsed); + this.commitCollapsed(collapsed, this.collapseCount(count, fits)); + } + + /** + * The last thing to give way, after every action is in the menu and + * the title has already run out. + * + * There are four things competing for this row and three of them + * cannot go. The **title** yields first and is allowed to ellipsis + * away entirely at 320px, because the navigation also says which + * page you are on. The **sort** control and the **actions** are + * each the only place they are said, so an action collapses into + * the menu rather than disappearing and the sort control stays. + * That leaves the **count**, which is the one purely informational + * item on the row — an empty page says so in its empty state, and a + * full one is being looked at. + * + * It became reachable rather than theoretical with #57: below 600px + * the header also carries the phone's search button, and on + * Playlists at 320px that is 43px more than the row has. Measured + * there: title 0, count 50, sort 143, search 40, "More actions" 38, + * five 12px gaps and 32px of gutters — 363 in 320, with the More + * button ending 27px past the edge. Something has to go, and this + * is the only candidate that is not an action. + * + * @returns whether the count was given up. + */ + private collapseCount( + count: HTMLElement | null, + fits: () => boolean, + ): boolean { + if (count === null || fits()) return false; + + count.hidden = true; + + return true; } /** Lowest priority first; ties broken from the right. */ @@ -601,7 +642,9 @@ export class PageHeader extends LitElement { .map(({ action }) => action); } - private commitCollapsed(next: Set): void { + private commitCollapsed(next: Set, countHidden: boolean): void { + this.countCollapsed = countHidden; + const same = next.size === this.collapsed.size && [...next].every((id) => this.collapsed.has(id)); @@ -769,7 +812,16 @@ export class PageHeader extends LitElement { const noun = this.count === 1 ? this.countNoun : plural; - return html``; }