fix(web): keep season controls on one line
This commit is contained in:
+24
-9
@@ -3628,10 +3628,20 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
disclose.click();
|
||||
});
|
||||
|
||||
// season 0 never counts toward tracking or totals (§4.1, §4.2); say so
|
||||
// season 0 never counts toward tracking or totals (§4.1, §4.2); say so.
|
||||
// Both forms are rendered and CSS picks one: narrow viewports show the
|
||||
// SxxEyy-style tag the rest of the app already uses, so the row's controls
|
||||
// keep their place instead of wrapping under it.
|
||||
const name = document.createElement("span");
|
||||
name.className = "season-name";
|
||||
name.textContent = season.number === 0 ? "specials · s00" : `season ${PAD_TWO(season.number)}`;
|
||||
const nameLong = document.createElement("span");
|
||||
nameLong.className = "season-name-long";
|
||||
nameLong.textContent =
|
||||
season.number === 0 ? "specials · s00" : `season ${PAD_TWO(season.number)}`;
|
||||
const nameShort = document.createElement("span");
|
||||
nameShort.className = "season-name-short";
|
||||
nameShort.textContent = seasonTag(season.number);
|
||||
name.append(nameLong, nameShort);
|
||||
|
||||
const track = document.createElement("button");
|
||||
track.type = "button";
|
||||
@@ -3658,8 +3668,13 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
});
|
||||
});
|
||||
|
||||
const space = document.createElement("span");
|
||||
space.className = "season-space";
|
||||
// everything that describes the season shares one shrinkable box; the
|
||||
// controls sit outside it, so a long flag chip wraps within the head
|
||||
// rather than pushing search and delete onto a line of their own.
|
||||
const head = document.createElement("span");
|
||||
head.className = "season-head";
|
||||
const actions = document.createElement("span");
|
||||
actions.className = "season-actions";
|
||||
|
||||
const deckBtn = document.createElement("button");
|
||||
deckBtn.type = "button";
|
||||
@@ -3689,7 +3704,7 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
});
|
||||
});
|
||||
|
||||
line.append(disclose, name, track, seasonCountsChip(season));
|
||||
head.append(name, track, seasonCountsChip(season));
|
||||
// #227: the season the operator's report is about read `0/10` with
|
||||
// nothing saying a pack had been grabbed, downloaded in full and thrown
|
||||
// out at import. The counts chip beside this one is exactly the number
|
||||
@@ -3699,7 +3714,7 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
const kind = blacklistClass(failure.reason);
|
||||
const when = failure.failed_at === null ? "" : ` ${formatSweepAge(failure.failed_at)}`;
|
||||
const told = `a pack for this season downloaded in full and failed at import${when} — ${failure.release} · ${blacklistAdvice(failure.reason)}`;
|
||||
line.append(
|
||||
head.append(
|
||||
chip(`import failed · ${blacklistReasonLabel(failure.reason)}`, (span) => {
|
||||
span.dataset.flag = "import-failed";
|
||||
span.dataset.blacklist = kind;
|
||||
@@ -3711,13 +3726,12 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
// The season-level twin of the episode flag above: gone upstream while
|
||||
// files under it remained.
|
||||
if (season.vanished) {
|
||||
line.append(
|
||||
head.append(
|
||||
chip("vanished", (span) => {
|
||||
span.dataset.flag = "vanished";
|
||||
}),
|
||||
);
|
||||
}
|
||||
line.append(space, deckBtn);
|
||||
// #174: files go, episodes stop being wanted, the season stays listed.
|
||||
// Only offered with files on disk — intent alone is the tracked toggle.
|
||||
let clear: HTMLButtonElement | null = null;
|
||||
@@ -3746,7 +3760,8 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
|
||||
);
|
||||
}
|
||||
// rows without a delete still reserve its slot, so search keeps its column
|
||||
line.append(space, deckBtn, clear ?? placeholderSlot());
|
||||
actions.append(deckBtn, clear ?? placeholderSlot());
|
||||
line.append(disclose, head, actions);
|
||||
item.append(line);
|
||||
|
||||
const episodes = document.createElement("ul");
|
||||
|
||||
+44
-10
@@ -1516,16 +1516,34 @@ body {
|
||||
border-bottom: 1px solid oklch(from var(--line) l c h / 45%);
|
||||
}
|
||||
|
||||
/* the line never wraps: the head shrinks and wraps inside itself, so the
|
||||
controls keep the right edge on every viewport */
|
||||
.season-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--space-2) var(--space-3);
|
||||
padding: var(--space-2) var(--space-1);
|
||||
cursor: pointer;
|
||||
transition: background 150ms var(--ease-out);
|
||||
}
|
||||
|
||||
.season-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
gap: var(--space-2) var(--space-3);
|
||||
}
|
||||
|
||||
.season-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: none;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
/* a row under the pointer is visibly the row the click will land on —
|
||||
the same lift a search result row gets */
|
||||
.season-line:hover,
|
||||
@@ -1568,6 +1586,7 @@ body {
|
||||
}
|
||||
|
||||
.season-name {
|
||||
flex: none;
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 700;
|
||||
@@ -1576,16 +1595,19 @@ body {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* one name, two lengths — the wide one reads as prose, the narrow one is the
|
||||
SxxEyy tag used everywhere else. display:none keeps the unused one out of
|
||||
the accessibility tree, so the row is never announced twice. */
|
||||
.season-name-short {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* the tracked toggle reads as the rule it is; pressed = on */
|
||||
.season-line .control,
|
||||
.ep-actions .control {
|
||||
padding: 0 var(--space-3);
|
||||
}
|
||||
|
||||
.season-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.episodes {
|
||||
margin: 0;
|
||||
padding: 0 0 var(--space-2) var(--space-8);
|
||||
@@ -1968,8 +1990,10 @@ body {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* no side gutter on a phone: the width belongs to the content, and the
|
||||
hero reaches both edges the way the rail already does */
|
||||
.deck {
|
||||
padding: var(--space-6) var(--space-4) var(--space-8);
|
||||
padding: var(--space-6) 0 var(--space-8);
|
||||
}
|
||||
|
||||
.row-chips {
|
||||
@@ -1977,6 +2001,20 @@ body {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
/* the season name shrinks to its tag so the head keeps fitting beside the
|
||||
controls; the long form has nowhere to go on a phone */
|
||||
.season-name-long {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.season-name-short {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.episodes {
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
|
||||
/* an episode row wraps: identity first, chips full-width, actions right */
|
||||
.ep-chips {
|
||||
margin-left: 0;
|
||||
@@ -1987,10 +2025,6 @@ body {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.episodes {
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
|
||||
.releases-id {
|
||||
order: -1;
|
||||
flex-basis: 100%;
|
||||
|
||||
Reference in New Issue
Block a user