148 lines
3.4 KiB
TypeScript
148 lines
3.4 KiB
TypeScript
import type { FolkShape } from "./folk-shape";
|
|
|
|
interface StoredDimensions {
|
|
x: number;
|
|
y: number;
|
|
width: number;
|
|
height: number;
|
|
rotation: number;
|
|
}
|
|
|
|
// Store original dimensions for each maximized shape
|
|
const originalDimensions = new WeakMap<FolkShape, StoredDimensions>();
|
|
|
|
/**
|
|
* Check if a shape is currently maximized
|
|
*/
|
|
export function isMaximized(shape: FolkShape): boolean {
|
|
return originalDimensions.has(shape);
|
|
}
|
|
|
|
/**
|
|
* Maximize a shape to fill the viewport
|
|
* @param shape - The shape to maximize
|
|
* @param padding - Padding from viewport edges (default 40px)
|
|
* @param animate - Whether to animate the transition (default true)
|
|
*/
|
|
export function maximizeShape(
|
|
shape: FolkShape,
|
|
padding = 40,
|
|
animate = true
|
|
): void {
|
|
if (isMaximized(shape)) {
|
|
// Already maximized, restore instead
|
|
restoreShape(shape, animate);
|
|
return;
|
|
}
|
|
|
|
// Store original dimensions
|
|
originalDimensions.set(shape, {
|
|
x: shape.x,
|
|
y: shape.y,
|
|
width: shape.width,
|
|
height: shape.height,
|
|
rotation: shape.rotation,
|
|
});
|
|
|
|
// Calculate viewport dimensions
|
|
const viewportWidth = window.innerWidth - padding * 2;
|
|
const viewportHeight = window.innerHeight - padding * 2;
|
|
|
|
// Get canvas element to account for any transforms
|
|
const canvas = shape.closest("#canvas") as HTMLElement | null;
|
|
const canvasRect = canvas?.getBoundingClientRect();
|
|
const scrollX = window.scrollX || 0;
|
|
const scrollY = window.scrollY || 0;
|
|
|
|
// Calculate centered position
|
|
const newX = padding + scrollX - (canvasRect?.left || 0);
|
|
const newY = padding + scrollY - (canvasRect?.top || 0);
|
|
|
|
if (animate) {
|
|
shape.style.transition = "all 0.3s ease-out";
|
|
}
|
|
|
|
// Apply maximized dimensions
|
|
shape.x = newX;
|
|
shape.y = newY;
|
|
shape.width = viewportWidth;
|
|
shape.height = viewportHeight;
|
|
shape.rotation = 0;
|
|
|
|
// Add maximized state
|
|
shape.setAttribute("data-maximized", "true");
|
|
|
|
// Remove transition after animation
|
|
if (animate) {
|
|
setTimeout(() => {
|
|
shape.style.transition = "";
|
|
}, 300);
|
|
}
|
|
|
|
// Dispatch event
|
|
shape.dispatchEvent(
|
|
new CustomEvent("maximize", {
|
|
detail: { maximized: true },
|
|
bubbles: true,
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Restore a maximized shape to its original dimensions
|
|
* @param shape - The shape to restore
|
|
* @param animate - Whether to animate the transition (default true)
|
|
*/
|
|
export function restoreShape(shape: FolkShape, animate = true): void {
|
|
const original = originalDimensions.get(shape);
|
|
if (!original) return;
|
|
|
|
if (animate) {
|
|
shape.style.transition = "all 0.3s ease-out";
|
|
}
|
|
|
|
// Restore original dimensions
|
|
shape.x = original.x;
|
|
shape.y = original.y;
|
|
shape.width = original.width;
|
|
shape.height = original.height;
|
|
shape.rotation = original.rotation;
|
|
|
|
// Remove maximized state
|
|
shape.removeAttribute("data-maximized");
|
|
originalDimensions.delete(shape);
|
|
|
|
// Remove transition after animation
|
|
if (animate) {
|
|
setTimeout(() => {
|
|
shape.style.transition = "";
|
|
}, 300);
|
|
}
|
|
|
|
// Dispatch event
|
|
shape.dispatchEvent(
|
|
new CustomEvent("maximize", {
|
|
detail: { maximized: false },
|
|
bubbles: true,
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Toggle maximize state of a shape
|
|
* @param shape - The shape to toggle
|
|
* @param padding - Padding from viewport edges (default 40px)
|
|
* @param animate - Whether to animate the transition (default true)
|
|
*/
|
|
export function toggleMaximize(
|
|
shape: FolkShape,
|
|
padding = 40,
|
|
animate = true
|
|
): void {
|
|
if (isMaximized(shape)) {
|
|
restoreShape(shape, animate);
|
|
} else {
|
|
maximizeShape(shape, padding, animate);
|
|
}
|
|
}
|