diff --git a/apps/calendar-web/src/common/utils/grid/grid.util.ts b/apps/calendar-web/src/common/utils/grid/grid.util.ts index 77feb3590..90f973bb7 100644 --- a/apps/calendar-web/src/common/utils/grid/grid.util.ts +++ b/apps/calendar-web/src/common/utils/grid/grid.util.ts @@ -63,14 +63,37 @@ export const SCROLL_TO_NOW_BUFFER_PX = 150; const SCROLL_TO_NOW_TOLERANCE_PX = 2; +/** When the now line is visible and scroll is within this window, "t" pulses. */ +export const SCROLL_TO_NOW_PULSE_NEAR_MINUTES = 5; + export const getScrollToNowTop = (clientHeight: number) => - getCurrentMinute() * getMinuteHeight(clientHeight) - SCROLL_TO_NOW_BUFFER_PX; + Math.max( + 0, + getCurrentMinute() * getMinuteHeight(clientHeight) - + SCROLL_TO_NOW_BUFFER_PX, + ); -export const isTimedGridScrolledToNow = (timedGrid: HTMLElement) => { +export const getScrollToNowDelta = (timedGrid: HTMLElement) => { const targetTop = getScrollToNowTop(timedGrid.clientHeight); - return ( - Math.abs(timedGrid.scrollTop - targetTop) <= SCROLL_TO_NOW_TOLERANCE_PX - ); + return Math.abs(timedGrid.scrollTop - targetTop); +}; + +export const isTimedGridScrolledToNow = (timedGrid: HTMLElement) => { + return getScrollToNowDelta(timedGrid) <= SCROLL_TO_NOW_TOLERANCE_PX; +}; + +export const isTimedGridNearScrollToNow = (timedGrid: HTMLElement) => { + const nearPx = + getMinuteHeight(timedGrid.clientHeight) * SCROLL_TO_NOW_PULSE_NEAR_MINUTES; + return getScrollToNowDelta(timedGrid) <= nearPx; +}; + +/** Whether the live now line sits inside the timed grid's visible scroll area. */ +export const isNowLineInTimedGridViewport = (timedGrid: HTMLElement) => { + const nowLineY = getCurrentMinute() * getMinuteHeight(timedGrid.clientHeight); + const viewTop = timedGrid.scrollTop; + const viewBottom = viewTop + timedGrid.clientHeight; + return nowLineY >= viewTop && nowLineY <= viewBottom; }; /** Returns true when the grid scroll position changed. */ diff --git a/apps/calendar-web/src/components/SelectView/SelectView.tsx b/apps/calendar-web/src/components/SelectView/SelectView.tsx index 4b9ec76d1..47de58fef 100644 --- a/apps/calendar-web/src/components/SelectView/SelectView.tsx +++ b/apps/calendar-web/src/components/SelectView/SelectView.tsx @@ -20,6 +20,7 @@ import { Z_INDEX_FLOATING_MENU } from "@web/common/constants/web.constants"; import { ShortcutKeys } from "@web/components/Shortcuts/ShortcutKeys"; import { selectNowCueActive, + selectNowCuePulseGeneration, useNowCueStore, } from "@web/grid/now-cue/now-cue.store"; import { useFloatingLayer } from "@web/shortcuts/floating-layer"; @@ -52,6 +53,7 @@ export const SelectView = ({ const listRef = useRef>([]); useFloatingLayer("viewSelect", isOpen); const pulseDayLabel = useNowCueStore(selectNowCueActive) && emphasizeDayLabel; + const pulseGeneration = useNowCueStore(selectNowCuePulseGeneration); const getCurrentView = (): "Day" | "Week" | "Life" => { const pathname = location.pathname; @@ -190,6 +192,7 @@ export const SelectView = ({ pulseDayLabel && "c-now-cue-label-pulse motion-reduce:transition-none", )} + key={emphasizeDayLabel ? pulseGeneration : undefined} > {label} diff --git a/apps/calendar-web/src/grid/components/TimedGrid.tsx b/apps/calendar-web/src/grid/components/TimedGrid.tsx index c0a201f16..31682d466 100644 --- a/apps/calendar-web/src/grid/components/TimedGrid.tsx +++ b/apps/calendar-web/src/grid/components/TimedGrid.tsx @@ -25,6 +25,7 @@ import { import { useGridMarginLeft } from "@web/grid/grid-margin"; import { selectNowCueActive, + selectNowCuePulseGeneration, useNowCueStore, } from "@web/grid/now-cue/now-cue.store"; import { type GridVisibleDate } from "@web/grid/types/grid.types"; @@ -173,19 +174,18 @@ const CalendarNowLine = ({ useMinuteTick(); const percentOfDay = getCurrentPercentOfDay(effectiveTimeZone); const pulseNowLine = useNowCueStore(selectNowCueActive); + const pulseGeneration = useNowCueStore(selectNowCuePulseGeneration); return (