wip, resize, debugging

This commit is contained in:
Janus C. H. Knudsen 2025-10-08 00:58:38 +02:00
parent e2cf4d1e04
commit 8b8a1e3127
7 changed files with 289 additions and 9 deletions

View file

@ -1,10 +1,30 @@
import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents';
import { calendarConfig } from '../core/CalendarConfig';
export class ResizeHandleManager {
private resizeZoneHeight = 15; // Must match CSS ::after height
private cachedEvents: HTMLElement[] = [];
// Resize state
private isResizing = false;
private resizingElement: HTMLElement | null = null;
private initialHeight = 0;
private initialMouseY = 0;
private targetHeight = 0;
private currentHeight = 0;
private animationFrameId: number | null = null;
// Snap configuration
private snapIntervalMinutes = 15;
private hourHeightPx: number;
constructor() {
const gridSettings = calendarConfig.getGridSettings();
this.hourHeightPx = gridSettings.hourHeight;
this.snapIntervalMinutes = gridSettings.snapInterval;
}
public initialize(): void {
this.refreshEventCache();
this.setupEventListeners();
@ -17,16 +37,33 @@ export class ResizeHandleManager {
}
private setupEventListeners(): void {
// Hover detection (only when not resizing and mouse button is up)
document.addEventListener('mousemove', (e: MouseEvent) => {
this.handleGlobalMouseMove(e);
if (!this.isResizing) {
// Only check for resize zones when mouse button is up
if (e.buttons === 0) {
this.handleGlobalMouseMove(e);
}
} else {
this.handleMouseMove(e);
}
});
// Resize mouse handling
document.addEventListener('mousedown', (e: MouseEvent) => {
this.handleMouseDown(e);
});
document.addEventListener('mouseup', (e: MouseEvent) => {
this.handleMouseUp(e);
});
// Cache refresh
eventBus.on(CoreEvents.GRID_RENDERED, () => this.refreshEventCache());
eventBus.on(CoreEvents.EVENTS_RENDERED, () => this.refreshEventCache());
eventBus.on(CoreEvents.EVENT_CREATED, () => this.refreshEventCache());
eventBus.on(CoreEvents.EVENT_UPDATED, () => this.refreshEventCache());
eventBus.on(CoreEvents.EVENT_DELETED, () => this.refreshEventCache());
eventBus.on('drag:end', () => this.refreshEventCache());
}
private handleGlobalMouseMove(e: MouseEvent): void {
@ -34,6 +71,11 @@ export class ResizeHandleManager {
const events = this.cachedEvents;
events.forEach(eventElement => {
// Skip the element we're currently resizing
if (this.resizingElement === eventElement) {
return;
}
const rect = eventElement.getBoundingClientRect();
const mouseY = e.clientY;
const mouseX = e.clientX;
@ -73,4 +115,121 @@ export class ResizeHandleManager {
}
eventElement.removeAttribute('data-resize-hover');
}
private handleMouseDown(e: MouseEvent): void {
const target = e.target as HTMLElement;
const eventElement = target.closest<HTMLElement>('swp-event[data-resize-hover="true"]');
if (!eventElement) return;
// Check if click is in bottom resize zone
const rect = eventElement.getBoundingClientRect();
const distanceFromBottom = rect.bottom - e.clientY;
if (distanceFromBottom >= 0 && distanceFromBottom <= this.resizeZoneHeight) {
// START RESIZE
e.stopPropagation(); // Prevent DragDropManager from handling
e.preventDefault();
this.isResizing = true;
this.resizingElement = eventElement;
this.initialHeight = eventElement.offsetHeight;
this.initialMouseY = e.clientY;
// Set high z-index on event-group if exists, otherwise on event itself
const eventGroup = eventElement.closest<HTMLElement>('swp-event-group');
if (eventGroup) {
eventGroup.style.zIndex = '1000';
} else {
eventElement.style.zIndex = '1000';
}
console.log('🔄 Resize started', this.initialHeight);
}
}
private handleMouseMove(e: MouseEvent): void {
if (!this.isResizing || !this.resizingElement) return;
const deltaY = e.clientY - this.initialMouseY;
const rawHeight = this.initialHeight + deltaY;
// Apply minimum height
this.targetHeight = Math.max(30, rawHeight);
// Start animation loop if not already running
if (this.animationFrameId === null) {
this.currentHeight = this.resizingElement.offsetHeight;
this.animate();
}
}
private animate(): void {
if (!this.isResizing || !this.resizingElement) {
this.animationFrameId = null;
return;
}
// Smooth interpolation towards target
const diff = this.targetHeight - this.currentHeight;
const step = diff * 0.3; // 30% of distance per frame
// Update if difference is significant
if (Math.abs(diff) > 0.5) {
this.currentHeight += step;
const swpEvent = this.resizingElement as any;
if (swpEvent.updateHeight) {
swpEvent.updateHeight(this.currentHeight);
}
this.animationFrameId = requestAnimationFrame(() => this.animate());
} else {
// Close enough - snap to target
this.currentHeight = this.targetHeight;
const swpEvent = this.resizingElement as any;
if (swpEvent.updateHeight) {
swpEvent.updateHeight(this.currentHeight);
}
this.animationFrameId = null;
}
}
private handleMouseUp(e: MouseEvent): void {
if (!this.isResizing || !this.resizingElement) return;
// Cancel animation
if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
// Snap to grid on mouse up
const snapDistancePx = (this.snapIntervalMinutes / 60) * this.hourHeightPx;
const currentHeight = this.resizingElement.offsetHeight;
const snappedHeight = Math.round(currentHeight / snapDistancePx) * snapDistancePx;
const finalHeight = Math.max(30, snappedHeight);
const swpEvent = this.resizingElement as any;
if (swpEvent.updateHeight) {
swpEvent.updateHeight(finalHeight);
}
console.log('✅ Resize ended', finalHeight);
// Clear z-index on event-group if exists, otherwise on event itself
const eventGroup = this.resizingElement.closest<HTMLElement>('swp-event-group');
if (eventGroup) {
eventGroup.style.zIndex = '';
} else {
this.resizingElement.style.zIndex = '';
}
// Cleanup state
this.isResizing = false;
this.resizingElement = null;
// Refresh cache for future operations
this.refreshEventCache();
}
}