import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; /** * Panel position stored as percentages (0-100) of viewport dimensions * This ensures panels stay within bounds when screen size changes */ interface PanelPosition { xPercent: number; // 0-100, percentage from left edge yPercent: number; // 0-100, percentage from top edge } interface VisualizationState { editorPosition: PanelPosition | null; setEditorPosition: (position: PanelPosition) => void; resetEditorPosition: () => void; } export const useVisualizationStore = create()( immer((set) => ({ editorPosition: null, setEditorPosition: (position) => set((state) => { state.editorPosition = position; }), resetEditorPosition: () => set((state) => { state.editorPosition = null; }), })) ); /** * Utility functions to convert between pixels and percentages */ export const positionUtils = { /** * Convert pixel position to percentage position */ pixelsToPercent: (pixelX: number, pixelY: number, panelWidth: number, panelHeight: number): PanelPosition => { const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // Calculate percentage, ensuring panel stays within bounds const maxXPercent = ((viewportWidth - panelWidth) / viewportWidth) * 100; const maxYPercent = ((viewportHeight - panelHeight) / viewportHeight) * 100; const xPercent = Math.max(0, Math.min((pixelX / viewportWidth) * 100, maxXPercent)); const yPercent = Math.max(0, Math.min((pixelY / viewportHeight) * 100, maxYPercent)); return { xPercent, yPercent }; }, /** * Convert percentage position to pixel position */ percentToPixels: (position: PanelPosition, panelWidth: number, panelHeight: number): { x: number; y: number } => { const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // Calculate pixel position let x = (position.xPercent / 100) * viewportWidth; let y = (position.yPercent / 100) * viewportHeight; // Ensure panel stays within bounds const maxX = viewportWidth - panelWidth - 8; const maxY = viewportHeight - panelHeight - 8; x = Math.max(0, Math.min(x, maxX)); y = Math.max(0, Math.min(y, maxY)); return { x, y }; }, /** * Get default position (top-right corner with some margin) */ getDefaultPosition: (panelWidth: number): PanelPosition => { const viewportWidth = window.innerWidth; const defaultX = Math.max(0, viewportWidth - panelWidth - 40); const defaultY = 80; return positionUtils.pixelsToPercent(defaultX, defaultY, panelWidth, 300); }, };