From efcb9cf6d25fd898a942d3f1863d58f8d7df4801 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 31 Dec 2025 10:13:55 +0330 Subject: [PATCH] zoom control --- src/pages/editor/components/EditorCanvas.tsx | 15 ++- src/pages/editor/components/ZoomControls.tsx | 99 ++++++++++++++++++++ src/pages/editor/store/editorStore.ts | 16 ++++ 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/pages/editor/components/ZoomControls.tsx diff --git a/src/pages/editor/components/EditorCanvas.tsx b/src/pages/editor/components/EditorCanvas.tsx index 6b91b4d..cd93a03 100644 --- a/src/pages/editor/components/EditorCanvas.tsx +++ b/src/pages/editor/components/EditorCanvas.tsx @@ -6,6 +6,7 @@ import { useDrawingHandlers } from "./canvas/useDrawingHandlers"; import { useStageSize } from "./canvas/useStageSize"; import ObjectRenderer from "./canvas/ObjectRenderer"; import CellEditor from "@/components/CellEditor"; +import ZoomControls from "./ZoomControls"; const EditorCanvas = () => { const stageRef = useRef(null); @@ -26,6 +27,7 @@ const EditorCanvas = () => { applyTableResize, setStageRef, setLayerRef, + zoom, } = useEditorStore(); const { transformerRef, handleStageMouseDown, handleStageMouseMove, handleStageMouseUp } = useDrawingHandlers(); @@ -150,15 +152,17 @@ const EditorCanvas = () => { }; }, [selectedObjectId, objects, transformerRef, applyTableResize, updateObject]); + const finalScale = stageSize.scale * zoom; + return ( -
+
{ /> ); })()} +
); }; diff --git a/src/pages/editor/components/ZoomControls.tsx b/src/pages/editor/components/ZoomControls.tsx new file mode 100644 index 0000000..069a615 --- /dev/null +++ b/src/pages/editor/components/ZoomControls.tsx @@ -0,0 +1,99 @@ +import { useState, useRef, useEffect, useCallback } from "react"; +import { Add, Minus, Grid8 } from "iconsax-react"; +import { useEditorStore } from "../store/editorStore"; + +const ZoomControls = () => { + const { zoom, setZoom, zoomIn, zoomOut } = useEditorStore(); + const sliderRef = useRef(null); + const [isDragging, setIsDragging] = useState(false); + + const MIN_ZOOM = 0.5; + const MAX_ZOOM = 2; + const SLIDER_WIDTH = 80; + + const sliderPosition = ((zoom - MIN_ZOOM) / (MAX_ZOOM - MIN_ZOOM)) * SLIDER_WIDTH; + + const updateZoomFromPosition = useCallback((clientX: number) => { + if (!sliderRef.current) return; + + const trackElement = sliderRef.current.querySelector('.slider-track') as HTMLElement; + if (!trackElement) return; + + const rect = trackElement.getBoundingClientRect(); + const x = clientX - rect.left; + const clampedX = Math.max(0, Math.min(SLIDER_WIDTH, x)); + const newZoom = MIN_ZOOM + (clampedX / SLIDER_WIDTH) * (MAX_ZOOM - MIN_ZOOM); + setZoom(newZoom); + }, [setZoom]); + + const handleSliderMouseDown = (e: React.MouseEvent) => { + setIsDragging(true); + updateZoomFromPosition(e.clientX); + }; + + const handleMouseMove = useCallback((e: MouseEvent) => { + if (isDragging) { + updateZoomFromPosition(e.clientX); + } + }, [isDragging, updateZoomFromPosition]); + + const handleMouseUp = useCallback(() => { + setIsDragging(false); + }, []); + + useEffect(() => { + if (isDragging) { + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + return () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + } + }, [isDragging, handleMouseMove, handleMouseUp]); + + return ( +
+ + + + +
+
+
+
+
+ + + +
1/1
+
+ ); +}; + +export default ZoomControls; + diff --git a/src/pages/editor/store/editorStore.ts b/src/pages/editor/store/editorStore.ts index cb35a20..27728ea 100644 --- a/src/pages/editor/store/editorStore.ts +++ b/src/pages/editor/store/editorStore.ts @@ -88,6 +88,10 @@ type EditorStoreType = { setStageRef: (ref: React.RefObject) => void; layerRef: React.RefObject | null; setLayerRef: (ref: React.RefObject) => void; + zoom: number; + setZoom: (zoom: number) => void; + zoomIn: () => void; + zoomOut: () => void; }; const createTableCellId = (row: number, col: number) => `cell-${row}-${col}`; @@ -350,4 +354,16 @@ export const useEditorStore = create((set, get) => ({ setStageRef: (ref) => set({ stageRef: ref }), layerRef: null, setLayerRef: (ref) => set({ layerRef: ref }), + zoom: 1, + setZoom: (zoom) => set({ zoom }), + zoomIn: () => { + const state = get(); + const newZoom = Math.min(state.zoom + 0.1, 2); + set({ zoom: newZoom }); + }, + zoomOut: () => { + const state = get(); + const newZoom = Math.max(state.zoom - 0.1, 0.5); + set({ zoom: newZoom }); + }, }));