Inline, combine functions, lint

This commit is contained in:
midzelis 2025-09-06 16:16:17 +00:00
parent 3ffa7c9d29
commit d14c997ed3
3 changed files with 44 additions and 40 deletions

View file

@ -53,12 +53,16 @@
const VIEWPORT_MULTIPLIER = 2; // Used to determine if timeline is "small" const VIEWPORT_MULTIPLIER = 2; // Used to determine if timeline is "small"
let isInLeadOutSection = $state(false);
// The percentage of scroll through the month that is currently intersecting the top boundary of the viewport. // The percentage of scroll through the month that is currently intersecting the top boundary of the viewport.
// Note: There may be multiple months visible within the viewport at any given time. // Note: There may be multiple months visible within the viewport at any given time.
let viewportTopMonthScrollPercent = $state(0); let viewportTopMonthScrollPercent = $state(0);
// The timeline month intersecting the top position of the viewport
let viewportTopMonth: TimelineYearMonth | undefined = $state(undefined); let viewportTopMonth: TimelineYearMonth | undefined = $state(undefined);
// Overall scroll percentage through the entire timeline (0-1)
let timelineScrollPercent: number = $state(0); let timelineScrollPercent: number = $state(0);
// Indicates whether the viewport is currently in the lead-out section (after all months)
let isInLeadOutSection = $state(false);
// Width of the scrubber component in pixels, used to adjust timeline margins
let scrubberWidth: number = $state(0); let scrubberWidth: number = $state(0);
// note: don't throttle, debounce, or otherwise make this function async - it causes flicker // note: don't throttle, debounce, or otherwise make this function async - it causes flicker
@ -66,16 +70,10 @@
const handleTimelineScroll = () => { const handleTimelineScroll = () => {
isInLeadOutSection = false; isInLeadOutSection = false;
// Handle small timeline edge case: scroll limited due to size of content // Handle edge cases: small timeline (limited scroll) or lead-in area scrolling
if (isSmallTimeline()) {
handleSmallTimelineScroll();
return;
}
// Handle scrolling of the lead-in area
const top = timelineManager.visibleWindow.top; const top = timelineManager.visibleWindow.top;
if (top < timelineManager.topSectionHeight) { if (isSmallTimeline() || top < timelineManager.topSectionHeight) {
handleLeadInScroll(); calculateTimelineScrollPercent();
return; return;
} }
@ -83,29 +81,6 @@
handleMonthScroll(); handleMonthScroll();
}; };
const isSmallTimeline = () => {
return timelineManager.timelineHeight < timelineManager.viewportHeight * VIEWPORT_MULTIPLIER;
};
const resetScrubberMonth = () => {
viewportTopMonth = undefined;
viewportTopMonthScrollPercent = 0;
};
const calculateTimelineScrollPercent = () => {
const maxScroll = timelineManager.getMaxScroll();
timelineScrollPercent = Math.min(1, timelineManager.visibleWindow.top / maxScroll);
resetScrubberMonth();
};
const handleSmallTimelineScroll = () => {
calculateTimelineScrollPercent();
};
const handleLeadInScroll = () => {
calculateTimelineScrollPercent();
};
const handleMonthScroll = () => { const handleMonthScroll = () => {
const scrollPosition = timelineManager.visibleWindow.top; const scrollPosition = timelineManager.visibleWindow.top;
const months = timelineManager.months; const months = timelineManager.months;
@ -118,12 +93,24 @@
viewportTopMonth = searchResult.month; viewportTopMonth = searchResult.month;
viewportTopMonthScrollPercent = searchResult.monthScrollPercent; viewportTopMonthScrollPercent = searchResult.monthScrollPercent;
isInLeadOutSection = false; isInLeadOutSection = false;
} else { return;
// We're in lead-out section
isInLeadOutSection = true;
timelineScrollPercent = 1;
resetScrubberMonth();
} }
// We're in lead-out section
isInLeadOutSection = true;
timelineScrollPercent = 1;
resetScrubberMonth();
};
const resetScrubberMonth = () => {
viewportTopMonth = undefined;
viewportTopMonthScrollPercent = 0;
};
const calculateTimelineScrollPercent = () => {
const maxScroll = timelineManager.getMaxScroll();
timelineScrollPercent = Math.min(1, timelineManager.visibleWindow.top / maxScroll);
resetScrubberMonth();
}; };
const handleOverallPercentScroll = (percent: number, scrollTo?: (offset: number) => void) => { const handleOverallPercentScroll = (percent: number, scrollTo?: (offset: number) => void) => {
@ -138,6 +125,10 @@
); );
}; };
const isSmallTimeline = () => {
return timelineManager.timelineHeight < timelineManager.viewportHeight * VIEWPORT_MULTIPLIER;
};
// note: don't throttle, debounce, or otherwise make this function async - it causes flicker // note: don't throttle, debounce, or otherwise make this function async - it causes flicker
// this function scrolls the timeline to the specified month group and offset, based on scrubber interaction // this function scrolls the timeline to the specified month group and offset, based on scrubber interaction
const onScrub: ScrubberListener = (scrubberData) => { const onScrub: ScrubberListener = (scrubberData) => {

View file

@ -11,18 +11,31 @@
import { fade, fly } from 'svelte/transition'; import { fade, fly } from 'svelte/transition';
interface Props { interface Props {
/** Offset from the top of the timeline (e.g., for headers) */
timelineTopOffset?: number; timelineTopOffset?: number;
/** Offset from the bottom of the timeline (e.g., for footers) */
timelineBottomOffset?: number; timelineBottomOffset?: number;
/** Total height of the scrubber component */
height?: number; height?: number;
/** Timeline manager instance that controls the timeline state */
timelineManager: TimelineManager; timelineManager: TimelineManager;
/** Overall scroll percentage through the entire timeline (0-1), used when no specific month is targeted */
timelineScrollPercent?: number; timelineScrollPercent?: number;
/** The percentage of scroll through the month that is currently intersecting the top boundary of the viewport */
viewportTopMonthScrollPercent?: number; viewportTopMonthScrollPercent?: number;
/** The year/month of the timeline month at the top of the viewport */
viewportTopMonth?: TimelineYearMonth; viewportTopMonth?: TimelineYearMonth;
/** Indicates whether the viewport is currently in the lead-out section (after all months) */
isInLeadOutSection?: boolean; isInLeadOutSection?: boolean;
/** Width of the scrubber component in pixels (bindable for parent component margin adjustments) */
scrubberWidth?: number; scrubberWidth?: number;
/** Callback fired when user interacts with the scrubber to navigate */
onScrub?: ScrubberListener; onScrub?: ScrubberListener;
/** Callback fired when keyboard events occur on the scrubber */
onScrubKeyDown?: (event: KeyboardEvent, element: HTMLElement) => void; onScrubKeyDown?: (event: KeyboardEvent, element: HTMLElement) => void;
/** Callback fired when scrubbing starts */
startScrub?: ScrubberListener; startScrub?: ScrubberListener;
/** Callback fired when scrubbing stops */
stopScrub?: ScrubberListener; stopScrub?: ScrubberListener;
} }

View file

@ -274,7 +274,7 @@ export function findMonthAtScrollPosition(
} }
// Check if we're after the last month // Check if we're after the last month
const lastMonth = months[months.length - 1]; const lastMonth = months.at(-1)!;
const lastMonthBottom = (lastMonth.top + lastMonth.height) * maxScrollPercent; const lastMonthBottom = (lastMonth.top + lastMonth.height) * maxScrollPercent;
if (scrollPosition >= lastMonthBottom - SUBPIXEL_TOLERANCE) { if (scrollPosition >= lastMonthBottom - SUBPIXEL_TOLERANCE) {
return null; return null;
@ -293,7 +293,7 @@ export function findMonthAtScrollPosition(
if (scrollPosition >= monthTop - SUBPIXEL_TOLERANCE && scrollPosition < monthBottom - SUBPIXEL_TOLERANCE) { if (scrollPosition >= monthTop - SUBPIXEL_TOLERANCE && scrollPosition < monthBottom - SUBPIXEL_TOLERANCE) {
// Found the month containing the scroll position // Found the month containing the scroll position
const distanceIntoMonth = scrollPosition - monthTop; const distanceIntoMonth = scrollPosition - monthTop;
let monthScrollPercent = Math.max(0, distanceIntoMonth / (month.height * maxScrollPercent)); const monthScrollPercent = Math.max(0, distanceIntoMonth / (month.height * maxScrollPercent));
// Handle month boundary edge case // Handle month boundary edge case
if (monthScrollPercent > NEAR_END_THRESHOLD && mid < months.length - 1) { if (monthScrollPercent > NEAR_END_THRESHOLD && mid < months.length - 1) {