rspace-online/lib/maximize.ts

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);
}
}