From b5f6e0269f72895e854449edf83d9967b9fcb811 Mon Sep 17 00:00:00 2001 From: snmln Date: Mon, 30 Sep 2024 15:34:34 -0400 Subject: [PATCH 1/4] implementing centering functionality --- .../components/timeline/timeline-controls.tsx | 343 +++++++++++++----- 1 file changed, 243 insertions(+), 100 deletions(-) diff --git a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx index 819c76969..431b18b25 100644 --- a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx +++ b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx @@ -1,6 +1,6 @@ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useCallback } from 'react'; import styled, { css } from 'styled-components'; -import { useAtom } from 'jotai'; +import { useAtom, useAtomValue } from 'jotai'; import { endOfYear, format, startOfYear } from 'date-fns'; import { scaleTime, ScaleTime } from 'd3'; @@ -9,14 +9,19 @@ import { Toolbar, ToolbarGroup, VerticalDivider } from '@devseed-ui/toolbar'; import { isEqual } from 'lodash'; import { View } from 'react-calendar/dist/cjs/shared/types'; -import { - TimeDensity, -} from './../../types.d.ts'; +import { TimeDensity } from './../../types.d.ts'; import { DateAxis } from './date-axis'; import { TimelineZoomControls } from './timeline-zoom-controls'; import { TimelineDatePicker } from './timeline-datepicker'; import { TimelineHead } from './timeline'; -import { TemporalExtent } from './timeline-utils.js'; +import { TemporalExtent } from './timeline-utils'; + +import { + timelineWidthAtom, + zoomTransformAtom +} from '$components/exploration/atoms/timeline'; + +import { useScales } from '$components/exploration/hooks/scales-hooks'; import { selectedCompareDateAtom, selectedDateAtom, @@ -27,6 +32,11 @@ import { CollecticonCalendarMinus } from '$components/common/icons/calendar-minu import { CollecticonCalendarPlus } from '$components/common/icons/calendar-plus'; import { TipToolbarIconButton } from '$components/common/tip-button'; import useAois from '$components/common/map/controls/hooks/use-aois'; +import { useOnTOIZoom } from '$components/exploration/hooks/use-toi-zoom'; +import { + RIGHT_AXIS_SPACE, + HEADER_COLUMN_WIDTH +} from '$components/exploration/constants'; const TimelineControlsSelf = styled.div` width: 100%; @@ -75,7 +85,6 @@ interface TimelineControlsProps { minMaxTemporalExtent: TemporalExtent; } - export function getInitialScale(width) { const now = new Date(); return scaleTime() @@ -83,9 +92,13 @@ export function getInitialScale(width) { .range([0, width]); } -export function TimelineDateAxis(props: Omit) { +export function TimelineDateAxis( + props: Omit< + TimelineControlsProps, + 'onZoom' | 'timeDensity' | 'timelineLabelsFormat' | 'minMaxTemporalExtent' + > +) { const { xScaled, width } = props; - const initialScale = useMemo(() => { return getInitialScale(width); }, [width]); @@ -117,14 +130,15 @@ interface PlayheadProps { const TimelinePlayheadBase = styled.div` background-color: ${TIMELINE_PLAYHEAD_COLOR_PRIMARY}; color: ${TIMELINE_PLAYHEAD_COLOR_TEXT}; - padding: ${glsp(0.15)} ${glsp(0.30)}; + padding: ${glsp(0.15)} ${glsp(0.3)}; border-radius: ${themeVal('shape.rounded')}; font-size: 0.75rem; position: relative; width: max-content; font-weight: ${themeVal('type.base.regular')}; - &::after, &::before { + &::after, + &::before { content: ''; position: absolute; bottom: 1px; @@ -136,7 +150,8 @@ const TimelinePlayheadBase = styled.div` `; const PlayheadArrow = css` - &::after, &::before { + &::after, + &::before { content: ''; position: absolute; bottom: 1px; @@ -150,30 +165,44 @@ const PlayheadArrow = css` const LeftPlayheadArrow = css` ${PlayheadArrow} &::after { - border-right: 8px solid ${props => props.secondary ? TIMELINE_PLAYHEAD_COLOR_PRIMARY : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; + border-right: 8px solid + ${(props) => + props.secondary + ? TIMELINE_PLAYHEAD_COLOR_PRIMARY + : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; } `; const RightPlayheadArrow = css` ${PlayheadArrow} &::before { - border-left: 8px solid ${props => props.secondary ? TIMELINE_PLAYHEAD_COLOR_PRIMARY : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; + border-left: 8px solid + ${(props) => + props.secondary + ? TIMELINE_PLAYHEAD_COLOR_PRIMARY + : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; } `; const TimelinePlayheadLeftIndicator = styled(TimelinePlayheadBase)` - background-color: ${props => props.secondary ? TIMELINE_PLAYHEAD_COLOR_PRIMARY : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; + background-color: ${(props) => + props.secondary + ? TIMELINE_PLAYHEAD_COLOR_PRIMARY + : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; ${LeftPlayheadArrow} &::after { - left: ${props => props.secondary ? '-28%' : '-8%'}; + left: ${(props) => (props.secondary ? '-28%' : '-8%')}; } `; const TimelinePlayheadRightIndicator = styled(TimelinePlayheadBase)` - background-color: ${props => props.secondary ? TIMELINE_PLAYHEAD_COLOR_PRIMARY : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; + background-color: ${(props) => + props.secondary + ? TIMELINE_PLAYHEAD_COLOR_PRIMARY + : TIMELINE_PLAYHEAD_COLOR_SECONDARY}; ${RightPlayheadArrow} &::before { - right: ${props => props.secondary ? '-28%' : '-8%'}; + right: ${(props) => (props.secondary ? '-28%' : '-8%')}; } `; @@ -193,45 +222,65 @@ const TimelineHeadRightIndicators = styled(TimelineHeadIndicatorsBase)` flex-direction: row-reverse; `; -export const TimelineHeadIndicators = memo(({ outOfViewHeads, timelineLabelsFormat }: { outOfViewHeads: TimelineHead[], timelineLabelsFormat: string }) => { - // Filter the out-of-view heads to get those that are out to the left - const leftHeads = outOfViewHeads.filter(head => head.outDirection === 'left'); - // Filter the out-of-view heads to get those that are out to the right - const rightHeads = outOfViewHeads.filter(head => head.outDirection === 'right'); - - return ( - <> - {/* If there are any heads out to the left, render the left indicators */} - {leftHeads.length > 0 && ( - - - {format(leftHeads[0].date, timelineLabelsFormat)} - - {leftHeads.length > 1 && - - +{leftHeads.length - 1} - } - - )} - {/* If there are any heads out to the right, render the right indicators */} - {rightHeads.length > 0 && ( - - - {format(rightHeads[rightHeads.length - 1].date, timelineLabelsFormat)} - - {rightHeads.length > 1 && - - +{rightHeads.length - 1} - } - - )} - - ); -}, (prevProps, nextProps) => { - // React.memo does a shallow comparison of props, so we need to supply - // a custom comparison function to compare the outOfViewHead objects - return isEqual(prevProps.outOfViewHeads, nextProps.outOfViewHeads); -}); +export const TimelineHeadIndicators = memo( + ({ + outOfViewHeads, + timelineLabelsFormat + }: { + outOfViewHeads: TimelineHead[]; + timelineLabelsFormat: string; + }) => { + // Filter the out-of-view heads to get those that are out to the left + const leftHeads = outOfViewHeads.filter( + (head) => head.outDirection === 'left' + ); + // Filter the out-of-view heads to get those that are out to the right + const rightHeads = outOfViewHeads.filter( + (head) => head.outDirection === 'right' + ); + + return ( + <> + {/* If there are any heads out to the left, render the left indicators */} + {leftHeads.length > 0 && ( + + + {format(leftHeads[0].date, timelineLabelsFormat)} + + {leftHeads.length > 1 && ( + + +{leftHeads.length - 1} + + )} + + )} + {/* If there are any heads out to the right, render the right indicators */} + {rightHeads.length > 0 && ( + + + + {format( + rightHeads[rightHeads.length - 1].date, + timelineLabelsFormat + )} + + + {rightHeads.length > 1 && ( + + +{rightHeads.length - 1} + + )} + + )} + + ); + }, + (prevProps, nextProps) => { + // React.memo does a shallow comparison of props, so we need to supply + // a custom comparison function to compare the outOfViewHead objects + return isEqual(prevProps.outOfViewHeads, nextProps.outOfViewHeads); + } +); TimelineHeadIndicators.displayName = 'TimelineHeadIndicators'; @@ -255,9 +304,18 @@ const getCalendarView = (timeDensity: TimeDensity): View => { }; export function TimelineControls(props: TimelineControlsProps) { - const { xScaled, width, outOfViewHeads, onZoom, timeDensity, timelineLabelsFormat, minMaxTemporalExtent } = props; + const { + xScaled, + width, + outOfViewHeads, + onZoom, + timeDensity, + timelineLabelsFormat, + minMaxTemporalExtent + } = props; const [selectedDay, setSelectedDay] = useAtom(selectedDateAtom); + const [selectedCompareDay, setSelectedCompareDay] = useAtom( selectedCompareDateAtom ); @@ -265,16 +323,81 @@ export function TimelineControls(props: TimelineControlsProps) { const { features } = useAois(); // Scale to use when there are no datasets with data (loading or error) - const initialScale = useMemo(() => getInitialScale(width) ,[width]); + const initialScale = useMemo(() => getInitialScale(width), [width]); - const calendarView = useMemo(() => getCalendarView(timeDensity), [timeDensity]); + const calendarView = useMemo( + () => getCalendarView(timeDensity), + [timeDensity] + ); + //Center to selected point + const { onTOIZoom } = useOnTOIZoom(); + const timelineWidth = useAtomValue(timelineWidthAtom); + const { k } = useAtomValue(zoomTransformAtom); + + const { main } = useScales(); + const centerTimelineOnSelections = useCallback( + (newDate) => { + if (!timelineWidth || !main) return; + + //setting most recent date value depending on interaction + const newSelectedDay = newDate.selectedDay ?? selectedDay; + const newSelectedCompareDay = + newDate.selectedCompareDay ?? selectedCompareDay; + const newSelectedStartInterval = newDate.start ?? selectedInterval?.start; + const newSelectedEndInterval = newDate.end ?? selectedInterval?.end; + + //Settign visible areas to base calculations + const visibleTimeline = + timelineWidth - RIGHT_AXIS_SPACE - HEADER_COLUMN_WIDTH; + const widthToFit = visibleTimeline * 0.9; + const timelineCenter = visibleTimeline * 0.5; + const startPoint = 0; + let new_k; + let new_x; + if (newSelectedDay) { + new_k = k; + new_x = startPoint - new_k * main(newSelectedDay) + timelineCenter; + } + if (newSelectedCompareDay) { + if (newSelectedDay < newSelectedCompareDay) { + new_k = + widthToFit / (main(newSelectedCompareDay) - main(newSelectedDay)); + new_x = startPoint - new_k * main(newSelectedDay); + } else { + new_k = + widthToFit / (main(newSelectedDay) - main(newSelectedCompareDay)); + new_x = startPoint - new_k * main(newSelectedCompareDay); + } + } + + if (newSelectedStartInterval && newSelectedEndInterval) { + new_k = + widthToFit / + (main(newSelectedEndInterval) - main(newSelectedStartInterval)); + new_x = startPoint - new_k * main(newSelectedStartInterval); + } + return onTOIZoom(new_x, new_k); + }, + [ + selectedDay, + selectedInterval, + selectedCompareDay, + k, + main, + timelineWidth, + onTOIZoom + ] + ); return ( - + {outOfViewHeads && outOfViewHeads.length > 0 && ( - + )} @@ -301,8 +424,10 @@ export function TimelineControls(props: TimelineControlsProps) { const newDate = xScaled.invert( nextX > max ? currentX - DAY_SIZE_MAX : nextX ); - setSelectedCompareDay(newDate); + centerTimelineOnSelections({ + selectedCompareDay: newDate + }); }} > { if (!d) return; setSelectedDay(new Date(d)); + centerTimelineOnSelections({ selectedDay: new Date(d) }); }} disabled={!xScaled} - tipContent={selectedCompareDay ? 'Date shown on left map ' : 'Date shown on map'} + tipContent={ + selectedCompareDay + ? 'Date shown on left map ' + : 'Date shown on map' + } dataTourId='date-picker-a' calendarView={calendarView} triggerLabelFormat={timelineLabelsFormat} @@ -379,6 +510,7 @@ export function TimelineControls(props: TimelineControlsProps) { ...selectedInterval, end: new Date(d) }); + centerTimelineOnSelections({ end: new Date(d) }); }} disabled={!xScaled} tipContent='End date for analysis' @@ -390,48 +522,59 @@ export function TimelineControls(props: TimelineControlsProps) { ) : ( <> - { - if (!d) return; - setSelectedDay(new Date(d)); - }} - disabled={!xScaled} - tipContent={selectedCompareDay ? 'Date shown on left map ' : 'Date shown on map'} - dataTourId='date-picker-a' - calendarView={calendarView} - triggerLabelFormat={timelineLabelsFormat} - /> - {selectedCompareDay && ( - <> - - { - if (!d) return; - setSelectedCompareDay(new Date(d)); - }} - disabled={!xScaled} - tipContent='Date shown on right map' - dataTourId='date-picker-b' - calendarView={calendarView} - triggerLabelFormat={timelineLabelsFormat} - /> - - )} + {/* DEFAULT DATE PICKER */} + + { + if (!d) return; + + setSelectedDay(new Date(d)); + centerTimelineOnSelections({ selectedDay: new Date(d) }); + }} + disabled={!xScaled} + tipContent={ + selectedCompareDay + ? 'Date shown on left map ' + : 'Date shown on map' + } + dataTourId='date-picker-a' + calendarView={calendarView} + triggerLabelFormat={timelineLabelsFormat} + /> + {selectedCompareDay && ( + <> + + { + if (!d) return; + setSelectedCompareDay(new Date(d)); + centerTimelineOnSelections({ + selectedCompareDay: new Date(d) + }); + }} + disabled={!xScaled} + tipContent='Date shown on right map' + dataTourId='date-picker-b' + calendarView={calendarView} + triggerLabelFormat={timelineLabelsFormat} + /> + + )} )} - + From cf16e495b32e784326ed9b53d03e635c9fc8d8a6 Mon Sep 17 00:00:00 2001 From: snmln Date: Wed, 2 Oct 2024 12:20:53 -0400 Subject: [PATCH 2/4] implementing review feedback --- .../components/timeline/timeline-controls.tsx | 85 +++++++++++++------ 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx index 431b18b25..eef4572d7 100644 --- a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx +++ b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx @@ -332,57 +332,94 @@ export function TimelineControls(props: TimelineControlsProps) { //Center to selected point const { onTOIZoom } = useOnTOIZoom(); const timelineWidth = useAtomValue(timelineWidthAtom); - const { k } = useAtomValue(zoomTransformAtom); - + const { k: currentZoomTransformRatio } = useAtomValue(zoomTransformAtom); const { main } = useScales(); + const visualBufferSizing = 0.9; + const startPoint = 0; + + const calculateNewTOIZoom = ( + dateStart, + dateEnd, + widthToFit + ): { zTransform: number; panPosition: number } => { + const zTransform = widthToFit / (dateEnd - dateStart); + const panPosition = startPoint - zTransform * dateStart; + return { zTransform, panPosition }; + }; + const centerTimelineOnSelections = useCallback( (newDate) => { if (!timelineWidth || !main) return; + //defining width of visible area after confirming we have a timeline width + const widthToFit = + (timelineWidth - RIGHT_AXIS_SPACE - HEADER_COLUMN_WIDTH) * + visualBufferSizing; + //setting most recent date value depending on interaction const newSelectedDay = newDate.selectedDay ?? selectedDay; const newSelectedCompareDay = newDate.selectedCompareDay ?? selectedCompareDay; const newSelectedStartInterval = newDate.start ?? selectedInterval?.start; + const newSelectedEndInterval = newDate.end ?? selectedInterval?.end; + let newZoomArgs: { zTransform: number; panPosition: number } = { + zTransform: 0, + panPosition: 0 + }; + const calcNewSelectedDay = main(newSelectedDay); + const calcNewSelectedCompareDay = main(newSelectedCompareDay); + const calcNewSelectedEndInterval = main(newSelectedEndInterval); + const calcNewSelectedStartInterval = main(newSelectedStartInterval); - //Settign visible areas to base calculations - const visibleTimeline = - timelineWidth - RIGHT_AXIS_SPACE - HEADER_COLUMN_WIDTH; - const widthToFit = visibleTimeline * 0.9; - const timelineCenter = visibleTimeline * 0.5; - const startPoint = 0; - let new_k; - let new_x; if (newSelectedDay) { - new_k = k; - new_x = startPoint - new_k * main(newSelectedDay) + timelineCenter; + const halfOfCurrentWidth = 0.5; + const timelineCenter = widthToFit * halfOfCurrentWidth; + + newZoomArgs.zTransform = currentZoomTransformRatio; + newZoomArgs.panPosition = + startPoint - + newZoomArgs.zTransform * calcNewSelectedDay + + timelineCenter; } if (newSelectedCompareDay) { if (newSelectedDay < newSelectedCompareDay) { - new_k = - widthToFit / (main(newSelectedCompareDay) - main(newSelectedDay)); - new_x = startPoint - new_k * main(newSelectedDay); + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedDay, + calcNewSelectedCompareDay, + widthToFit + ); } else { - new_k = - widthToFit / (main(newSelectedDay) - main(newSelectedCompareDay)); - new_x = startPoint - new_k * main(newSelectedCompareDay); + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedCompareDay, + calcNewSelectedDay, + widthToFit + ); } } if (newSelectedStartInterval && newSelectedEndInterval) { - new_k = - widthToFit / - (main(newSelectedEndInterval) - main(newSelectedStartInterval)); - new_x = startPoint - new_k * main(newSelectedStartInterval); + if (newSelectedStartInterval > newSelectedEndInterval) { + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedEndInterval, + calcNewSelectedStartInterval, + widthToFit + ); + } else { + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedStartInterval, + calcNewSelectedEndInterval, + widthToFit + ); + } } - return onTOIZoom(new_x, new_k); + return onTOIZoom(newZoomArgs.panPosition, newZoomArgs.zTransform); }, [ selectedDay, selectedInterval, selectedCompareDay, - k, + currentZoomTransformRatio, main, timelineWidth, onTOIZoom From 641d174cb720d517acffb4665be38dc17dd3e97e Mon Sep 17 00:00:00 2001 From: snmln Date: Thu, 3 Oct 2024 09:40:40 -0400 Subject: [PATCH 3/4] adding types and dateFallback --- .../components/timeline/timeline-controls.tsx | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx index eef4572d7..bf58fa62a 100644 --- a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx +++ b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx @@ -338,9 +338,9 @@ export function TimelineControls(props: TimelineControlsProps) { const startPoint = 0; const calculateNewTOIZoom = ( - dateStart, - dateEnd, - widthToFit + dateStart: number, + dateEnd: number, + widthToFit: number ): { zTransform: number; panPosition: number } => { const zTransform = widthToFit / (dateEnd - dateStart); const panPosition = startPoint - zTransform * dateStart; @@ -348,7 +348,12 @@ export function TimelineControls(props: TimelineControlsProps) { }; const centerTimelineOnSelections = useCallback( - (newDate) => { + (newDate: { + selectedDay?: Date | null; + start?: Date | null; + end?: Date | null; + selectedCompareDay?: Date | null; + }) => { if (!timelineWidth || !main) return; //defining width of visible area after confirming we have a timeline width @@ -356,13 +361,18 @@ export function TimelineControls(props: TimelineControlsProps) { (timelineWidth - RIGHT_AXIS_SPACE - HEADER_COLUMN_WIDTH) * visualBufferSizing; + //Setting fallback incase no values are provided at any layer + const dateFallBack = new Date(); + //setting most recent date value depending on interaction - const newSelectedDay = newDate.selectedDay ?? selectedDay; + const newSelectedDay = newDate.selectedDay ?? selectedDay ?? dateFallBack; const newSelectedCompareDay = - newDate.selectedCompareDay ?? selectedCompareDay; - const newSelectedStartInterval = newDate.start ?? selectedInterval?.start; + newDate.selectedCompareDay ?? selectedCompareDay ?? dateFallBack; + const newSelectedStartInterval = + newDate.start ?? selectedInterval?.start ?? dateFallBack; + const newSelectedEndInterval = + newDate.end ?? selectedInterval?.end ?? dateFallBack; - const newSelectedEndInterval = newDate.end ?? selectedInterval?.end; let newZoomArgs: { zTransform: number; panPosition: number } = { zTransform: 0, panPosition: 0 From d5ec2249267277b70d7d7d109873f61b6ccf1bc5 Mon Sep 17 00:00:00 2001 From: snmln Date: Thu, 3 Oct 2024 10:14:06 -0400 Subject: [PATCH 4/4] removing datefallback and moving around main calculations --- .../components/timeline/timeline-controls.tsx | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx index bf58fa62a..a13224262 100644 --- a/app/scripts/components/exploration/components/timeline/timeline-controls.tsx +++ b/app/scripts/components/exploration/components/timeline/timeline-controls.tsx @@ -361,28 +361,21 @@ export function TimelineControls(props: TimelineControlsProps) { (timelineWidth - RIGHT_AXIS_SPACE - HEADER_COLUMN_WIDTH) * visualBufferSizing; - //Setting fallback incase no values are provided at any layer - const dateFallBack = new Date(); - //setting most recent date value depending on interaction - const newSelectedDay = newDate.selectedDay ?? selectedDay ?? dateFallBack; + const newSelectedDay = newDate.selectedDay ?? selectedDay; const newSelectedCompareDay = - newDate.selectedCompareDay ?? selectedCompareDay ?? dateFallBack; - const newSelectedStartInterval = - newDate.start ?? selectedInterval?.start ?? dateFallBack; - const newSelectedEndInterval = - newDate.end ?? selectedInterval?.end ?? dateFallBack; + newDate.selectedCompareDay ?? selectedCompareDay; + const newSelectedStartInterval = newDate.start ?? selectedInterval?.start; + const newSelectedEndInterval = newDate.end ?? selectedInterval?.end; let newZoomArgs: { zTransform: number; panPosition: number } = { zTransform: 0, panPosition: 0 }; - const calcNewSelectedDay = main(newSelectedDay); - const calcNewSelectedCompareDay = main(newSelectedCompareDay); - const calcNewSelectedEndInterval = main(newSelectedEndInterval); - const calcNewSelectedStartInterval = main(newSelectedStartInterval); if (newSelectedDay) { + const calcNewSelectedDay = main(newSelectedDay); + const halfOfCurrentWidth = 0.5; const timelineCenter = widthToFit * halfOfCurrentWidth; @@ -391,24 +384,28 @@ export function TimelineControls(props: TimelineControlsProps) { startPoint - newZoomArgs.zTransform * calcNewSelectedDay + timelineCenter; - } - if (newSelectedCompareDay) { - if (newSelectedDay < newSelectedCompareDay) { - newZoomArgs = calculateNewTOIZoom( - calcNewSelectedDay, - calcNewSelectedCompareDay, - widthToFit - ); - } else { - newZoomArgs = calculateNewTOIZoom( - calcNewSelectedCompareDay, - calcNewSelectedDay, - widthToFit - ); + + if (newSelectedCompareDay) { + const calcNewSelectedCompareDay = main(newSelectedCompareDay); + + if (newSelectedDay < newSelectedCompareDay) { + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedDay, + calcNewSelectedCompareDay, + widthToFit + ); + } else { + newZoomArgs = calculateNewTOIZoom( + calcNewSelectedCompareDay, + calcNewSelectedDay, + widthToFit + ); + } } } - if (newSelectedStartInterval && newSelectedEndInterval) { + const calcNewSelectedEndInterval = main(newSelectedEndInterval); + const calcNewSelectedStartInterval = main(newSelectedStartInterval); if (newSelectedStartInterval > newSelectedEndInterval) { newZoomArgs = calculateNewTOIZoom( calcNewSelectedEndInterval,