Improves drag and drop functionality
Enhances drag and drop behavior by introducing free positioning during auto-scroll and snapping to grid intervals. - Introduces a `calculateFreePosition` method to allow events to follow the mouse exactly during auto-scroll. - Modifies the drag move event to emit the snapped position during normal drag behavior. - Updates event rendering to use grid settings for snap intervals. - Updates grid styles to configure CSS variables dynamically.
This commit is contained in:
parent
b4d758b6d9
commit
7a1c776bc1
4 changed files with 50 additions and 25 deletions
|
|
@ -47,7 +47,7 @@ export class DragDropManager {
|
|||
|
||||
// Snap configuration
|
||||
private snapIntervalMinutes = 15; // Default 15 minutes
|
||||
private hourHeightPx = 60; // From CSS --hour-height
|
||||
private hourHeightPx: number; // Will be set from config
|
||||
|
||||
// Event listener references for proper cleanup
|
||||
private boundHandlers = {
|
||||
|
|
@ -66,6 +66,7 @@ export class DragDropManager {
|
|||
// Get config values
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
this.hourHeightPx = gridSettings.hourHeight;
|
||||
this.snapIntervalMinutes = gridSettings.snapInterval;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
|
@ -164,14 +165,14 @@ export class DragDropManager {
|
|||
const currentPosition: Position = { x: event.clientX, y: event.clientY };
|
||||
const deltaY = Math.abs(currentPosition.y - this.lastLoggedPosition.y);
|
||||
|
||||
// Check for snap interval vertical movement
|
||||
// Check for snap interval vertical movement (normal drag behavior)
|
||||
if (deltaY >= this.snapDistancePx) {
|
||||
this.lastLoggedPosition = currentPosition;
|
||||
|
||||
// Consolidated position calculations
|
||||
// Consolidated position calculations with snapping for normal drag
|
||||
const positionData = this.calculateDragPosition(currentPosition);
|
||||
|
||||
// Emit drag move event with consolidated data
|
||||
// Emit drag move event with snapped position (normal behavior)
|
||||
this.eventBus.emit('drag:move', {
|
||||
eventId: this.draggedEventId,
|
||||
mousePosition: currentPosition,
|
||||
|
|
@ -240,7 +241,24 @@ export class DragDropManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Optimized snap position calculation with caching
|
||||
* Calculate free position (follows mouse exactly)
|
||||
*/
|
||||
private calculateFreePosition(mouseY: number, column: string | null = null): number {
|
||||
const targetColumn = column || this.currentColumn;
|
||||
|
||||
// Use cached column element if available
|
||||
const columnElement = this.getCachedColumnElement(targetColumn);
|
||||
if (!columnElement) return mouseY;
|
||||
|
||||
const columnRect = columnElement.getBoundingClientRect();
|
||||
const relativeY = mouseY - columnRect.top - this.mouseOffset.y;
|
||||
|
||||
// Return free position (no snapping)
|
||||
return Math.max(0, relativeY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized snap position calculation with caching (used only on drop)
|
||||
*/
|
||||
private calculateSnapPosition(mouseY: number, column: string | null = null): number {
|
||||
const targetColumn = column || this.currentColumn;
|
||||
|
|
@ -371,14 +389,13 @@ export class DragDropManager {
|
|||
const columnElement = this.getCachedColumnElement(this.currentColumn);
|
||||
if (columnElement) {
|
||||
const columnRect = columnElement.getBoundingClientRect();
|
||||
// Calculate position relative to column, accounting for scroll movement
|
||||
// Calculate free position relative to column, accounting for scroll movement (no snapping during scroll)
|
||||
const relativeY = this.currentMouseY - columnRect.top - this.mouseOffset.y;
|
||||
const snappedY = Math.round(relativeY / this.snapDistancePx) * this.snapDistancePx;
|
||||
const finalSnappedY = Math.max(0, snappedY);
|
||||
const freeY = Math.max(0, relativeY);
|
||||
|
||||
this.eventBus.emit('drag:auto-scroll', {
|
||||
eventId: this.draggedEventId,
|
||||
snappedY: finalSnappedY,
|
||||
snappedY: freeY, // Actually free position during scroll
|
||||
scrollTop: this.cachedElements.scrollContainer.scrollTop
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { calendarConfig } from '../core/CalendarConfig';
|
|||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { ResourceCalendarData, CalendarView } from '../types/CalendarTypes';
|
||||
import { GridRenderer } from '../renderers/GridRenderer';
|
||||
import { GridStyleManager } from '../renderers/GridStyleManager';
|
||||
import { DateCalculator } from '../utils/DateCalculator';
|
||||
|
||||
/**
|
||||
|
|
@ -19,11 +20,13 @@ export class GridManager {
|
|||
private resourceData: ResourceCalendarData | null = null;
|
||||
private currentView: CalendarView = 'week';
|
||||
private gridRenderer: GridRenderer;
|
||||
private styleManager: GridStyleManager;
|
||||
private eventCleanup: (() => void)[] = [];
|
||||
|
||||
constructor() {
|
||||
// Initialize GridRenderer with config
|
||||
// Initialize GridRenderer and StyleManager with config
|
||||
this.gridRenderer = new GridRenderer();
|
||||
this.styleManager = new GridStyleManager();
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +88,9 @@ export class GridManager {
|
|||
return;
|
||||
}
|
||||
|
||||
// Update CSS variables first
|
||||
this.styleManager.updateGridStyles(this.resourceData);
|
||||
|
||||
// Delegate to GridRenderer with current view context
|
||||
this.gridRenderer.renderGrid(
|
||||
this.container,
|
||||
|
|
|
|||
|
|
@ -159,25 +159,26 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
|||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const hourHeight = gridSettings.hourHeight;
|
||||
const dayStartHour = gridSettings.dayStartHour;
|
||||
const snapInterval = 15; // TODO: Get from config
|
||||
const snapInterval = gridSettings.snapInterval;
|
||||
|
||||
// Calculate total minutes from top
|
||||
const totalMinutesFromTop = (snappedY / hourHeight) * 60;
|
||||
const startTotalMinutes = Math.max(
|
||||
dayStartHour * 60,
|
||||
Math.round((dayStartHour * 60 + totalMinutesFromTop) / snapInterval) * snapInterval
|
||||
);
|
||||
// Calculate minutes from grid start (not from midnight)
|
||||
const minutesFromGridStart = (snappedY / hourHeight) * 60;
|
||||
|
||||
// Add dayStartHour offset to get actual time
|
||||
const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart;
|
||||
|
||||
// Snap to interval
|
||||
const snappedStartMinutes = Math.round(actualStartMinutes / snapInterval) * snapInterval;
|
||||
|
||||
// Use cached original duration (no recalculation)
|
||||
const cachedDuration = parseInt(clone.dataset.originalDuration || '60');
|
||||
const endTotalMinutes = startTotalMinutes + cachedDuration;
|
||||
const endTotalMinutes = snappedStartMinutes + cachedDuration;
|
||||
|
||||
// Update display
|
||||
const timeElement = clone.querySelector('swp-event-time');
|
||||
if (timeElement) {
|
||||
const newTimeText = `${this.formatTime(startTotalMinutes)} - ${this.formatTime(endTotalMinutes)}`;
|
||||
const newTimeText = `${this.formatTime(snappedStartMinutes)} - ${this.formatTime(endTotalMinutes)}`;
|
||||
timeElement.textContent = newTimeText;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -619,19 +620,20 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
|||
const dayStartHour = gridSettings.dayStartHour;
|
||||
const hourHeight = gridSettings.hourHeight;
|
||||
|
||||
// Calculate minutes from visible day start
|
||||
// Calculate minutes from midnight
|
||||
const startMinutes = startDate.getHours() * 60 + startDate.getMinutes();
|
||||
const endMinutes = endDate.getHours() * 60 + endDate.getMinutes();
|
||||
const dayStartMinutes = dayStartHour * 60;
|
||||
|
||||
// Calculate top position (subtract day start to align with time axis)
|
||||
// Calculate top position relative to visible grid start
|
||||
// If dayStartHour=6 and event starts at 09:00 (540 min), then:
|
||||
// top = ((540 - 360) / 60) * hourHeight = 3 * hourHeight (3 hours from grid start)
|
||||
const top = ((startMinutes - dayStartMinutes) / 60) * hourHeight;
|
||||
|
||||
// Calculate height
|
||||
// Calculate height based on event duration
|
||||
const durationMinutes = endMinutes - startMinutes;
|
||||
const height = (durationMinutes / 60) * hourHeight;
|
||||
|
||||
|
||||
return { top, height };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ swp-hour-marker {
|
|||
swp-hour-marker::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
top: -1px;
|
||||
left: 50px;
|
||||
width: calc(100vw - 60px); /* Full viewport width minus time-axis width */
|
||||
height: 1px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue