Drop the phone's top bar; search becomes a button and a modal #167
@@ -162,6 +162,15 @@ export class PageHeader extends LitElement {
|
||||
@state()
|
||||
private collapsed: ReadonlySet<string> = 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<string, HTMLElement>();
|
||||
|
||||
for (const el of this.renderRoot.querySelectorAll<HTMLElement>(
|
||||
@@ -549,6 +552,7 @@ export class PageHeader extends LitElement {
|
||||
|
||||
const more = this.moreButton;
|
||||
const title = this.renderRoot.querySelector('h1');
|
||||
const count = this.renderRoot.querySelector<HTMLElement>('.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<string>();
|
||||
|
||||
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<string>): void {
|
||||
private commitCollapsed(next: Set<string>, 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`<span class="count" data-testid="page-count"
|
||||
// Rendered whether or not it fits, and hidden with an
|
||||
// attribute -- the same shape the action buttons use, and for
|
||||
// the same reason: `measureFit` starts every pass from
|
||||
// all-visible, so it needs a node to un-hide. Returning
|
||||
// `nothing` here would take the count away for the rest of the
|
||||
// session the first time a 320px window appeared.
|
||||
return html`<span
|
||||
class="count"
|
||||
data-testid="page-count"
|
||||
?hidden=${this.countCollapsed}
|
||||
>${this.count.toLocaleString()} ${noun}</span
|
||||
>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user