mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
fix(web): improve scrubber behavior on scroll-limited timelines (#22917)
Improves scroll indicator positioning when scrubbing through timelines with limited scrollable content (e.g., small albums). When a timeline's scrollable height is less than 50% of the viewport height, the scroll position is now properly distributed across the entire scrubber height, making the indicator more responsive and accurate. Changes: - Add `limitedScroll` state to detect scroll-constrained timelines (threshold: 50%) - Introduce `ViewportTopMonth` type to handle lead-in/lead-out sections - Calculate `totalViewerHeight` including top/bottom sections for accurate positioning - Refactor scrubber to treat lead-in and lead-out as distinct scroll segments - Update scroll position calculations to use relative percentages on constrained timelines
This commit is contained in:
parent
9b5855f848
commit
f1e03d0022
6 changed files with 120 additions and 127 deletions
|
|
@ -48,7 +48,9 @@ export class TimelineManager {
|
|||
isInitialized = $state(false);
|
||||
months: MonthGroup[] = $state([]);
|
||||
topSectionHeight = $state(0);
|
||||
timelineHeight = $derived(this.months.reduce((accumulator, b) => accumulator + b.height, 0) + this.topSectionHeight);
|
||||
bottomSectionHeight = $state(60);
|
||||
assetsHeight = $derived(this.months.reduce((accumulator, b) => accumulator + b.height, 0));
|
||||
totalViewerHeight = $derived(this.topSectionHeight + this.assetsHeight + this.bottomSectionHeight);
|
||||
assetCount = $derived(this.months.reduce((accumulator, b) => accumulator + b.assetsCount, 0));
|
||||
|
||||
albumAssets: Set<string> = new SvelteSet();
|
||||
|
|
@ -62,6 +64,7 @@ export class TimelineManager {
|
|||
top: this.#scrollTop,
|
||||
bottom: this.#scrollTop + this.viewportHeight,
|
||||
}));
|
||||
limitedScroll = $derived(this.maxScrollPercent < 0.5);
|
||||
|
||||
initTask = new CancellableTask(
|
||||
() => {
|
||||
|
|
@ -383,7 +386,9 @@ export class TimelineManager {
|
|||
updateGeometry(this, month, { invalidateHeight: changedWidth });
|
||||
}
|
||||
this.updateIntersections();
|
||||
this.#createScrubberMonths();
|
||||
if (changedWidth) {
|
||||
this.#createScrubberMonths();
|
||||
}
|
||||
}
|
||||
|
||||
#createScrubberMonths() {
|
||||
|
|
@ -394,7 +399,7 @@ export class TimelineManager {
|
|||
title: month.monthGroupTitle,
|
||||
height: month.height,
|
||||
}));
|
||||
this.scrubberTimelineHeight = this.timelineHeight;
|
||||
this.scrubberTimelineHeight = this.totalViewerHeight;
|
||||
}
|
||||
|
||||
createLayoutOptions() {
|
||||
|
|
@ -408,6 +413,16 @@ export class TimelineManager {
|
|||
};
|
||||
}
|
||||
|
||||
get maxScrollPercent() {
|
||||
const totalHeight = this.totalViewerHeight;
|
||||
const max = (totalHeight - this.viewportHeight) / totalHeight;
|
||||
return max;
|
||||
}
|
||||
|
||||
get maxScroll() {
|
||||
return this.totalViewerHeight - this.viewportHeight;
|
||||
}
|
||||
|
||||
async loadMonthGroup(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise<void> {
|
||||
let cancelable = true;
|
||||
if (options) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue