86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
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<VisualizationState>()(
|
|
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);
|
|
},
|
|
};
|