aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-06-05 13:51:09 +0000
committerFuwn <[email protected]>2026-06-05 13:51:09 +0000
commitde0d3bbf10755330a9e24e5e4c2a99d6ff06de5b (patch)
tree13aefb03cb6b2e8c7b3a3ef8abd604169478313f /src
parentfeat(schedule): add sub/dub toggle to the schedule page (diff)
downloaddue.moe-de0d3bbf10755330a9e24e5e4c2a99d6ff06de5b.tar.xz
due.moe-de0d3bbf10755330a9e24e5e4c2a99d6ff06de5b.zip
fix(schedule): use masonry columns so day panels collapse cleanly
The grid layout kept fixed row heights, so collapsing a day left a gap. Distribute days round-robin into flex columns (preserving left-to-right order) so each column packs and reflows independently on collapse.
Diffstat (limited to 'src')
-rw-r--r--src/lib/Schedule/Days.svelte76
-rw-r--r--src/routes/schedule/+page.svelte16
2 files changed, 65 insertions, 27 deletions
diff --git a/src/lib/Schedule/Days.svelte b/src/lib/Schedule/Days.svelte
index 754d3f61..7fe09d4e 100644
--- a/src/lib/Schedule/Days.svelte
+++ b/src/lib/Schedule/Days.svelte
@@ -14,7 +14,7 @@ import { parseOrDefault } from "$lib/Utility/parameters";
import settings from "$stores/settings";
import CoverBypass from "./CoverBypass.svelte";
import "$lib/List/covers.css";
-import { onMount } from "svelte";
+import { onDestroy, onMount } from "svelte";
import RateLimitedError from "$lib/Error/RateLimited.svelte";
import ParallaxImage from "$lib/Image/ParallaxImage.svelte";
import Message from "$lib/Loading/Message.svelte";
@@ -65,7 +65,20 @@ const selectTrack = (track: AirType) => {
}
};
+// Two masonry columns above the $bp-md (800px) breakpoint, one below.
+let columnCount = 1;
+let columnQuery: MediaQueryList | undefined;
+
+const updateColumnCount = () => {
+ columnCount = columnQuery?.matches ? 1 : 2;
+};
+
onMount(async () => {
+ columnQuery = window.matchMedia("(max-width: 800px)");
+
+ updateColumnCount();
+ columnQuery.addEventListener("change", updateColumnCount);
+
if (user === undefined || $identity.id === -2)
mediaListPromise = Promise.resolve([]);
else {
@@ -84,6 +97,10 @@ onMount(async () => {
}
});
+onDestroy(() => {
+ columnQuery?.removeEventListener("change", updateColumnCount);
+});
+
const WEEKDAYS = [
"Sunday",
"Monday",
@@ -134,6 +151,26 @@ const scheduleByDay = (entries: AiringEntry[]) => {
return ordered;
};
+// Round-robin the ordered days across columns so reading stays left-to-right
+// (e.g. Friday, then Saturday to its right), while each column still packs and
+// reflows independently when a day is collapsed.
+const distributeDays = (
+ daysByWeekday: { [key: string]: AiringEntry[] },
+ columns: number,
+) => {
+ const distributed: {
+ day: string;
+ scheduleEntry: AiringEntry[];
+ dayIndex: number;
+ }[][] = Array.from({ length: columns }, () => []);
+
+ Object.entries(daysByWeekday).forEach(([day, scheduleEntry], dayIndex) => {
+ distributed[dayIndex % columns].push({ day, scheduleEntry, dayIndex });
+ });
+
+ return distributed;
+};
+
const associateMedia = (
media: (Media | undefined)[],
title: string,
@@ -186,12 +223,15 @@ const episode = (media: Media, weekday: string) => {
<Skeleton grid={true} count={7} height="15em" width="49.5%" />
{:then mediaList}
- {#each Object.entries(scheduleByDay(schedule[source])) as [day, scheduleEntry], dayIndex}
- <details
- open
- class="list"
- class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })}
- >
+ <div class="masonry">
+ {#each distributeDays(scheduleByDay(schedule[source]), columnCount) as column}
+ <div class="masonry-column">
+ {#each column as { day, scheduleEntry, dayIndex } (dayIndex)}
+ <details
+ open
+ class="list"
+ class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })}
+ >
<summary>{day}</summary>
{#if !$settings.displayScheduleListMode && !forceListMode}
@@ -275,7 +315,10 @@ const episode = (media: Media, weekday: string) => {
</ol>
{/if}
</details>
- {/each}
+ {/each}
+ </div>
+ {/each}
+ </div>
{:catch}
<RateLimitedError type="Media lists" loginSessionError={false} card list={false} />
{/await}
@@ -284,12 +327,25 @@ const episode = (media: Media, weekday: string) => {
@use "breakpoints" as *;
.track-toggle {
- grid-column: 1 / -1;
display: flex;
gap: 0.5em;
margin-bottom: 0.5em;
}
+ .masonry {
+ display: flex;
+ gap: 0.5em;
+ align-items: flex-start;
+ }
+
+ .masonry-column {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+ }
+
.countdown {
white-space: nowrap;
float: right;
@@ -304,8 +360,6 @@ const episode = (media: Media, weekday: string) => {
.list {
overflow-y: auto;
- break-inside: avoid-column;
- page-break-inside: avoid;
}
@media (max-width: $bp-md) {
diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte
index da26e4e3..ee40ad4d 100644
--- a/src/routes/schedule/+page.svelte
+++ b/src/routes/schedule/+page.svelte
@@ -71,19 +71,3 @@ onMount(async () => {
<RateLimitedError type="Media" loginSessionError={false} card list={false} />
{/await}
{/if}
-
-<style lang="scss">
- @use "breakpoints" as *;
-
- .schedule-container {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(35%, 1fr));
- gap: 0.5em;
- }
-
- @media (max-width: $bp-md) {
- .schedule-container {
- grid-template-columns: 1fr !important;
- }
- }
-</style>