From c6c986ade20a49225734564b2ecace410b32cc3c Mon Sep 17 00:00:00 2001 From: Reece Date: Tue, 28 Oct 2025 18:09:55 +0000 Subject: [PATCH] Clean up --- .../components/pageEditor/DragDropGrid.tsx | 11 ++- .../core/components/pageEditor/PageEditor.tsx | 40 ++------- .../core/components/viewer/EmbedPdfViewer.tsx | 39 ++------- frontend/src/core/hooks/useWheelZoom.ts | 82 +++++++++++++++++++ frontend/src/core/styles/zIndex.ts | 4 + 5 files changed, 107 insertions(+), 69 deletions(-) create mode 100644 frontend/src/core/hooks/useWheelZoom.ts diff --git a/frontend/src/core/components/pageEditor/DragDropGrid.tsx b/frontend/src/core/components/pageEditor/DragDropGrid.tsx index c7f7a84b4..62e4f6f71 100644 --- a/frontend/src/core/components/pageEditor/DragDropGrid.tsx +++ b/frontend/src/core/components/pageEditor/DragDropGrid.tsx @@ -2,6 +2,11 @@ import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react' import { Box } from '@mantine/core'; import { useVirtualizer } from '@tanstack/react-virtual'; import { GRID_CONSTANTS } from './constants'; +import { + Z_INDEX_SELECTION_BOX, + Z_INDEX_DROP_INDICATOR, + Z_INDEX_DRAG_BADGE, +} from '../../styles/zIndex'; import { DndContext, DragEndEvent, @@ -564,7 +569,7 @@ const DragDropGrid = ({ border: '2px dashed #3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', pointerEvents: 'none' as const, - zIndex: 1000, + zIndex: Z_INDEX_SELECTION_BOX, } : null; // Calculate drop indicator position @@ -592,7 +597,7 @@ const DragDropGrid = ({ height: `${height}px`, backgroundColor: 'rgba(96, 165, 250, 0.8)', borderRadius: '2px', - zIndex: 1001, + zIndex: Z_INDEX_DROP_INDICATOR, pointerEvents: 'none' as const, }; }, [hoveredItemId, dropSide, activeId, itemGap, zoomLevel]); @@ -738,7 +743,7 @@ const DragDropGrid = ({ fontSize: '14px', fontWeight: 'bold', boxShadow: '0 2px 8px rgba(0,0,0,0.2)', - zIndex: 1001 + zIndex: Z_INDEX_DRAG_BADGE }} > {boxSelectedPageIds.length} diff --git a/frontend/src/core/components/pageEditor/PageEditor.tsx b/frontend/src/core/components/pageEditor/PageEditor.tsx index 632fbfdf4..c017c845a 100644 --- a/frontend/src/core/components/pageEditor/PageEditor.tsx +++ b/frontend/src/core/components/pageEditor/PageEditor.tsx @@ -31,6 +31,7 @@ import { usePageEditorState } from './hooks/usePageEditorState'; import { parseSelection } from "@app/utils/bulkselection/parseSelection"; import { usePageEditorRightRailButtons } from "@app/components/pageEditor/pageEditorRightRailButtons"; import { useFileColorMap } from "@app/components/pageEditor/hooks/useFileColorMap"; +import { useWheelZoom } from "@app/hooks/useWheelZoom"; export interface PageEditorProps { onFunctionsReady?: (functions: PageEditorFunctions) => void; @@ -981,39 +982,12 @@ const PageEditor = ({ selectionMode, selectedPageIds, splitPositions, displayDocument?.pages.length, closePdf ]); - // Handle scroll wheel zoom with accumulator for smooth trackpad pinch - useEffect(() => { - let accumulator = 0; - - const handleWheel = (event: WheelEvent) => { - // Check if Ctrl (Windows/Linux) or Cmd (Mac) is pressed - if (event.ctrlKey || event.metaKey) { - event.preventDefault(); - event.stopPropagation(); - - accumulator += event.deltaY; - const threshold = 10; - - if (accumulator <= -threshold) { - // Accumulated scroll up - zoom in - zoomIn(); - accumulator = 0; - } else if (accumulator >= threshold) { - // Accumulated scroll down - zoom out - zoomOut(); - accumulator = 0; - } - } - }; - - const container = containerRef.current; - if (container) { - container.addEventListener('wheel', handleWheel, { passive: false }); - return () => { - container.removeEventListener('wheel', handleWheel); - }; - } - }, [zoomIn, zoomOut]); + useWheelZoom({ + ref: containerRef, + onZoomIn: zoomIn, + onZoomOut: zoomOut, + enabled: !!displayDocument, + }); // Handle keyboard zoom shortcuts useEffect(() => { diff --git a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx index f85058a73..01ac57d04 100644 --- a/frontend/src/core/components/viewer/EmbedPdfViewer.tsx +++ b/frontend/src/core/components/viewer/EmbedPdfViewer.tsx @@ -14,6 +14,7 @@ import { createStirlingFilesAndStubs } from '@app/services/fileStubHelpers'; import NavigationWarningModal from '@app/components/shared/NavigationWarningModal'; import { isStirlingFile } from '@app/types/fileContext'; import { useViewerRightRailButtons } from '@app/components/viewer/useViewerRightRailButtons'; +import { useWheelZoom } from '@app/hooks/useWheelZoom'; export interface EmbedPdfViewerProps { sidebarsVisible: boolean; @@ -124,39 +125,11 @@ const EmbedPdfViewerContent = ({ } }, [previewFile, fileWithUrl]); - // Handle scroll wheel zoom with accumulator for smooth trackpad pinch - useEffect(() => { - let accumulator = 0; - - const handleWheel = (event: WheelEvent) => { - // Check if Ctrl (Windows/Linux) or Cmd (Mac) is pressed - if (event.ctrlKey || event.metaKey) { - event.preventDefault(); - event.stopPropagation(); - - accumulator += event.deltaY; - const threshold = 10; - - if (accumulator <= -threshold) { - // Accumulated scroll up - zoom in - zoomActions.zoomIn(); - accumulator = 0; - } else if (accumulator >= threshold) { - // Accumulated scroll down - zoom out - zoomActions.zoomOut(); - accumulator = 0; - } - } - }; - - const viewerElement = viewerRef.current; - if (viewerElement) { - viewerElement.addEventListener('wheel', handleWheel, { passive: false }); - return () => { - viewerElement.removeEventListener('wheel', handleWheel); - }; - } - }, [zoomActions]); + useWheelZoom({ + ref: viewerRef, + onZoomIn: zoomActions.zoomIn, + onZoomOut: zoomActions.zoomOut, + }); // Handle keyboard zoom shortcuts useEffect(() => { diff --git a/frontend/src/core/hooks/useWheelZoom.ts b/frontend/src/core/hooks/useWheelZoom.ts new file mode 100644 index 000000000..a999566f8 --- /dev/null +++ b/frontend/src/core/hooks/useWheelZoom.ts @@ -0,0 +1,82 @@ +import { RefObject, useEffect } from 'react'; + +interface UseWheelZoomOptions { + /** + * Element the wheel listener should be bound to. + */ + ref: RefObject; + /** + * Callback executed when the hook decides to zoom in. + */ + onZoomIn: () => void; + /** + * Callback executed when the hook decides to zoom out. + */ + onZoomOut: () => void; + /** + * Whether the wheel listener should be active. + */ + enabled?: boolean; + /** + * How much delta needs to accumulate before a zoom action is triggered. + * Defaults to 10 which matches the previous implementations. + */ + threshold?: number; + /** + * Whether a Ctrl/Cmd modifier is required for zooming. Defaults to true so + * we only react to pinch gestures and intentional ctrl+wheel zooming. + */ + requireModifierKey?: boolean; +} + +/** + * Shared hook for handling wheel-based zoom across components. + * It normalises accumulated delta behaviour, prevents default scrolling when + * zoom is triggered, and keeps the handler detached when disabled. + */ +export function useWheelZoom({ + ref, + onZoomIn, + onZoomOut, + enabled = true, + threshold = 10, + requireModifierKey = true, +}: UseWheelZoomOptions) { + useEffect(() => { + if (!enabled) { + return; + } + + const element = ref.current; + if (!element) { + return; + } + + let accumulator = 0; + + const handleWheel = (event: WheelEvent) => { + const hasModifier = event.ctrlKey || event.metaKey; + if (requireModifierKey && !hasModifier) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + accumulator += event.deltaY; + + if (accumulator <= -threshold) { + onZoomIn(); + accumulator = 0; + } else if (accumulator >= threshold) { + onZoomOut(); + accumulator = 0; + } + }; + + element.addEventListener('wheel', handleWheel, { passive: false }); + return () => { + element.removeEventListener('wheel', handleWheel); + }; + }, [ref, onZoomIn, onZoomOut, enabled, threshold, requireModifierKey]); +} diff --git a/frontend/src/core/styles/zIndex.ts b/frontend/src/core/styles/zIndex.ts index c2b02660f..620700503 100644 --- a/frontend/src/core/styles/zIndex.ts +++ b/frontend/src/core/styles/zIndex.ts @@ -10,5 +10,9 @@ export const Z_INDEX_OVER_FILE_MANAGER_MODAL = 1300; export const Z_INDEX_AUTOMATE_MODAL = 1100; +export const Z_INDEX_SELECTION_BOX = 1000; +export const Z_INDEX_DROP_INDICATOR = 1001; +export const Z_INDEX_DRAG_BADGE = 1001; +