From 5e35496eee465f9c14bf5d675db350d3e8dddeac Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Thu, 23 Jul 2026 21:49:56 -0600 Subject: [PATCH] refactor(web): narrow draft reads for drag grid helpers Add useDraftDragMotion so edge navigation and smart scroll depend on isDragging / draft presence / timed-ness instead of full DraftContext. Co-authored-by: Cursor --- .../Draft/context/useDraftDragMotion.ts | 16 ++++++++++++++++ .../Week/hooks/grid/useDragEdgeNavigation.ts | 17 +++++++---------- .../Week/hooks/grid/useDragEventSmartScroll.ts | 18 +++++++++--------- 3 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 packages/web/src/views/Week/components/Draft/context/useDraftDragMotion.ts diff --git a/packages/web/src/views/Week/components/Draft/context/useDraftDragMotion.ts b/packages/web/src/views/Week/components/Draft/context/useDraftDragMotion.ts new file mode 100644 index 000000000..41342591f --- /dev/null +++ b/packages/web/src/views/Week/components/Draft/context/useDraftDragMotion.ts @@ -0,0 +1,16 @@ +import { useDraftContext } from "./useDraftContext"; + +/** + * Narrow Week draft reads for mid-drag grid helpers (edge navigation and + * smart scroll). Prefer this over `useDraftContext` when actions/setters + * and the full local draft are not needed. + */ +export const useDraftDragMotion = () => { + const { state } = useDraftContext(); + + return { + hasDraft: state.draft !== null, + isDragging: state.isDragging, + isTimedDraft: state.draft?.values.schedule.kind === "timed", + }; +}; diff --git a/packages/web/src/views/Week/hooks/grid/useDragEdgeNavigation.ts b/packages/web/src/views/Week/hooks/grid/useDragEdgeNavigation.ts index 9bc08effe..43ef6b535 100644 --- a/packages/web/src/views/Week/hooks/grid/useDragEdgeNavigation.ts +++ b/packages/web/src/views/Week/hooks/grid/useDragEdgeNavigation.ts @@ -1,5 +1,5 @@ import { type MutableRefObject, useEffect, useRef } from "react"; -import { useDraftContext } from "@web/views/Week/components/Draft/context/useDraftContext"; +import { useDraftDragMotion } from "@web/views/Week/components/Draft/context/useDraftDragMotion"; import { createWeekEdgeNavigationController, WEEK_EDGE_NAVIGATION_THRESHOLD_PX, @@ -15,18 +15,15 @@ export const useDragEdgeNavigation = ( mainGridRef: MutableRefObject, weekProps: WeekProps, ) => { - const { state: draftState } = useDraftContext(); - const isDragging = draftState.isDragging; - const currentDraft = draftState.draft; - const hasCurrentDraft = Boolean(currentDraft); + const { hasDraft, isDragging } = useDraftDragMotion(); const controllerRef = useRef(createWeekEdgeNavigationController()); - const currentDraftRef = useRef(currentDraft); const frameRef = useRef(null); + const hasDraftRef = useRef(hasDraft); const isDraggingRef = useRef(isDragging); const pointerRef = useRef(null); const weekUtilRef = useRef(weekProps.util); - currentDraftRef.current = currentDraft; + hasDraftRef.current = hasDraft; isDraggingRef.current = isDragging; weekUtilRef.current = weekProps.util; @@ -55,7 +52,7 @@ export const useDragEdgeNavigation = ( if ( !isDraggingRef.current || - !currentDraftRef.current || + !hasDraftRef.current || !mainGridRef.current || !pointerRef.current ) { @@ -89,7 +86,7 @@ export const useDragEdgeNavigation = ( } }; - if (!isDragging || !hasCurrentDraft) { + if (!isDragging || !hasDraft) { resetDraftEdgeNavigation(); return; } @@ -106,7 +103,7 @@ export const useDragEdgeNavigation = ( window.removeEventListener("mousemove", updatePointer); resetDraftEdgeNavigation(); }; - }, [hasCurrentDraft, isDragging, mainGridRef]); + }, [hasDraft, isDragging, mainGridRef]); useEffect(() => { return () => { diff --git a/packages/web/src/views/Week/hooks/grid/useDragEventSmartScroll.ts b/packages/web/src/views/Week/hooks/grid/useDragEventSmartScroll.ts index d91733aae..080b54ac3 100644 --- a/packages/web/src/views/Week/hooks/grid/useDragEventSmartScroll.ts +++ b/packages/web/src/views/Week/hooks/grid/useDragEventSmartScroll.ts @@ -1,5 +1,5 @@ import { type MutableRefObject, useEffect, useRef, useState } from "react"; -import { useDraftContext } from "@web/views/Week/components/Draft/context/useDraftContext"; +import { useDraftDragMotion } from "@web/views/Week/components/Draft/context/useDraftDragMotion"; const SCROLL_SPEED = 10; const EDGE_THRESHOLD = 50; @@ -7,13 +7,13 @@ const EDGE_THRESHOLD = 50; export const useDragEventSmartScroll = ( mainGridRef: MutableRefObject, ) => { - const { state } = useDraftContext(); + const { isDragging, isTimedDraft } = useDraftDragMotion(); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); const scrollRef = useRef(null); useEffect(() => { - if (!state.isDragging) return; - if (state.draft?.values.schedule.kind !== "timed") return; + if (!isDragging) return; + if (!isTimedDraft) return; const updateMousePosition = (event: MouseEvent) => { setMousePosition({ x: event.clientX, y: event.clientY }); @@ -24,16 +24,16 @@ export const useDragEventSmartScroll = ( return () => { window.removeEventListener("mousemove", updateMousePosition); }; - }, [state.draft?.values.schedule.kind, state.isDragging]); + }, [isDragging, isTimedDraft]); useEffect(() => { if (!mainGridRef.current) return; const container = mainGridRef.current; const scrollIfNeeded = () => { - if (!state.isDragging) return; + if (!isDragging) return; if (!container) return; - if (state.draft?.values.schedule.kind !== "timed") return; + if (!isTimedDraft) return; const containerRect = container.getBoundingClientRect(); const { top, bottom } = { @@ -73,10 +73,10 @@ export const useDragEventSmartScroll = ( } }; }, [ - state.isDragging, + isDragging, + isTimedDraft, mousePosition.x, mousePosition.y, - state.draft?.values.schedule.kind, mousePosition, mainGridRef.current, ]);