Enables all-day event conversion on column hover
Allows users to convert all-day events to timed events by dragging them over a day column. This implementation adds logic to the DragDropManager to detect when an all-day event is dragged over a column. It then emits a new event, 'drag:mouseenter-column', carrying the event data and target column information. The event rendering service handles this event.
This commit is contained in:
parent
7a79297854
commit
78ca23c07a
5 changed files with 69 additions and 125 deletions
|
|
@ -289,46 +289,6 @@ export class SwpEventElement extends BaseSwpEventElement {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method to convert an all-day HTML element to a timed SwpEventElement
|
|
||||||
*/
|
|
||||||
public static fromAllDayElement(allDayElement: HTMLElement): SwpEventElement {
|
|
||||||
const eventId = allDayElement.dataset.eventId || '';
|
|
||||||
const title = allDayElement.dataset.title || allDayElement.textContent || 'Untitled';
|
|
||||||
const type = allDayElement.dataset.type || 'work';
|
|
||||||
const startStr = allDayElement.dataset.start;
|
|
||||||
const endStr = allDayElement.dataset.end;
|
|
||||||
const durationStr = allDayElement.dataset.duration;
|
|
||||||
|
|
||||||
if (!startStr || !endStr) {
|
|
||||||
throw new Error('All-day element missing start/end dates');
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalStart = new Date(startStr);
|
|
||||||
const duration = durationStr ? parseInt(durationStr) : 60;
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const startDate = new Date(originalStart);
|
|
||||||
startDate.setHours(now.getHours() || 9, now.getMinutes() || 0, 0, 0);
|
|
||||||
|
|
||||||
const endDate = new Date(startDate);
|
|
||||||
endDate.setMinutes(endDate.getMinutes() + duration);
|
|
||||||
|
|
||||||
const calendarEvent: CalendarEvent = {
|
|
||||||
id: eventId,
|
|
||||||
title: title,
|
|
||||||
start: startDate,
|
|
||||||
end: endDate,
|
|
||||||
type: type,
|
|
||||||
allDay: false,
|
|
||||||
syncStatus: 'synced',
|
|
||||||
metadata: {
|
|
||||||
duration: duration.toString()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return SwpEventElement.fromCalendarEvent(calendarEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
DragEndEventPayload,
|
DragEndEventPayload,
|
||||||
DragMouseEnterHeaderEventPayload,
|
DragMouseEnterHeaderEventPayload,
|
||||||
DragMouseLeaveHeaderEventPayload,
|
DragMouseLeaveHeaderEventPayload,
|
||||||
|
DragMouseEnterColumnEventPayload,
|
||||||
DragColumnChangeEventPayload
|
DragColumnChangeEventPayload
|
||||||
} from '../types/EventTypes';
|
} from '../types/EventTypes';
|
||||||
import { MousePosition } from '../types/DragDropTypes';
|
import { MousePosition } from '../types/DragDropTypes';
|
||||||
|
|
@ -116,6 +117,8 @@ export class DragDropManager {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (target.closest('swp-calendar-header')) {
|
if (target.closest('swp-calendar-header')) {
|
||||||
this.handleHeaderMouseEnter(e as MouseEvent);
|
this.handleHeaderMouseEnter(e as MouseEvent);
|
||||||
|
} else if (target.closest('swp-day-column')) {
|
||||||
|
this.handleColumnMouseEnter(e as MouseEvent);
|
||||||
} else if (target.closest('swp-event')) {
|
} else if (target.closest('swp-event')) {
|
||||||
// Entered an event - activate hover tracking and set color
|
// Entered an event - activate hover tracking and set color
|
||||||
const eventElement = target.closest<HTMLElement>('swp-event');
|
const eventElement = target.closest<HTMLElement>('swp-event');
|
||||||
|
|
@ -652,6 +655,46 @@ export class DragDropManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mouse enter on day column - for converting all-day to timed events
|
||||||
|
*/
|
||||||
|
private handleColumnMouseEnter(event: MouseEvent): void {
|
||||||
|
// Only handle if we're dragging an all-day event
|
||||||
|
if (!this.isDragStarted || !this.draggedClone || !this.draggedClone.hasAttribute('data-allday')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎯 DragDropManager: Mouse entered day column');
|
||||||
|
|
||||||
|
const position: MousePosition = { x: event.clientX, y: event.clientY };
|
||||||
|
const targetColumn = ColumnDetectionUtils.getColumnBounds(position);
|
||||||
|
|
||||||
|
if (!targetColumn) {
|
||||||
|
console.warn("No column detected when entering day column");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate snapped Y position
|
||||||
|
const snappedY = this.calculateSnapPosition(position.y, targetColumn);
|
||||||
|
|
||||||
|
// Extract CalendarEvent from the dragged clone
|
||||||
|
const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone);
|
||||||
|
|
||||||
|
const dragMouseEnterPayload: DragMouseEnterColumnEventPayload = {
|
||||||
|
targetColumn: targetColumn,
|
||||||
|
mousePosition: position,
|
||||||
|
snappedY: snappedY,
|
||||||
|
originalElement: this.draggedElement,
|
||||||
|
draggedClone: this.draggedClone,
|
||||||
|
calendarEvent: calendarEvent,
|
||||||
|
// Delegate pattern - allows EventRenderer to replace the clone
|
||||||
|
replaceClone: (newClone: HTMLElement) => {
|
||||||
|
this.draggedClone = newClone;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.eventBus.emit('drag:mouseenter-column', dragMouseEnterPayload);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle mouse leave from calendar header - simplified using native events
|
* Handle mouse leave from calendar header - simplified using native events
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||||
* Separates event handling from rendering concerns
|
* Separates event handling from rendering concerns
|
||||||
*/
|
*/
|
||||||
export class HeaderManager {
|
export class HeaderManager {
|
||||||
|
|
||||||
// Event listeners for drag events
|
// Event listeners for drag events
|
||||||
private dragMouseEnterHeaderListener: ((event: Event) => void) | null = null;
|
private dragMouseEnterHeaderListener: ((event: Event) => void) | null = null;
|
||||||
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
private dragMouseLeaveHeaderListener: ((event: Event) => void) | null = null;
|
||||||
|
|
@ -20,7 +20,7 @@ export class HeaderManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Bind methods for event listeners
|
// Bind methods for event listeners
|
||||||
this.setupHeaderDragListeners = this.setupHeaderDragListeners.bind(this);
|
this.setupHeaderDragListeners = this.setupHeaderDragListeners.bind(this);
|
||||||
|
|
||||||
// Listen for navigation events to update header
|
// Listen for navigation events to update header
|
||||||
this.setupNavigationListener();
|
this.setupNavigationListener();
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ export class HeaderManager {
|
||||||
* Get cached calendar header element
|
* Get cached calendar header element
|
||||||
*/
|
*/
|
||||||
private getCalendarHeader(): HTMLElement | null {
|
private getCalendarHeader(): HTMLElement | null {
|
||||||
return document.querySelector('swp-calendar-header');
|
return document.querySelector('swp-calendar-header');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,42 +48,35 @@ export class HeaderManager {
|
||||||
// Create and store event listeners
|
// Create and store event listeners
|
||||||
this.dragMouseEnterHeaderListener = (event: Event) => {
|
this.dragMouseEnterHeaderListener = (event: Event) => {
|
||||||
const { targetColumn: targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
|
const { targetColumn: targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
|
||||||
|
|
||||||
console.log('🎯 HeaderManager: Received drag:mouseenter-header', {
|
console.log('🎯 HeaderManager: Received drag:mouseenter-header', {
|
||||||
targetDate,
|
targetDate,
|
||||||
originalElement: !!originalElement,
|
originalElement: !!originalElement,
|
||||||
cloneElement: !!cloneElement
|
cloneElement: !!cloneElement
|
||||||
});
|
});
|
||||||
|
|
||||||
if (targetDate) {
|
if (targetDate) {
|
||||||
const calendarType = calendarConfig.getCalendarMode();
|
const calendarType = calendarConfig.getCalendarMode();
|
||||||
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
||||||
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
||||||
|
|
||||||
console.log('🚪 HeaderManager: Received drag:mouseleave-header', {
|
console.log('🚪 HeaderManager: Received drag:mouseleave-header', {
|
||||||
targetDate,
|
targetDate,
|
||||||
originalElement: !!originalElement,
|
originalElement: !!originalElement,
|
||||||
cloneElement: !!cloneElement
|
cloneElement: !!cloneElement
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.emit('header:mouseleave', {
|
|
||||||
element: this.getCalendarHeader(),
|
|
||||||
targetDate,
|
|
||||||
originalElement,
|
|
||||||
cloneElement
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen for drag events from DragDropManager
|
// Listen for drag events from DragDropManager
|
||||||
eventBus.on('drag:mouseenter-header', this.dragMouseEnterHeaderListener);
|
eventBus.on('drag:mouseenter-header', this.dragMouseEnterHeaderListener);
|
||||||
eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener);
|
eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener);
|
||||||
|
|
||||||
console.log('✅ HeaderManager: Drag event listeners attached');
|
console.log('✅ HeaderManager: Drag event listeners attached');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,13 +110,13 @@ export class HeaderManager {
|
||||||
const calendarHeader = this.getOrCreateCalendarHeader();
|
const calendarHeader = this.getOrCreateCalendarHeader();
|
||||||
if (!calendarHeader) return;
|
if (!calendarHeader) return;
|
||||||
|
|
||||||
// Clear existing content
|
// Clear existing content
|
||||||
calendarHeader.innerHTML = '';
|
calendarHeader.innerHTML = '';
|
||||||
|
|
||||||
// Render new header content
|
// Render new header content
|
||||||
const calendarType = calendarConfig.getCalendarMode();
|
const calendarType = calendarConfig.getCalendarMode();
|
||||||
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
||||||
|
|
||||||
const context: HeaderRenderContext = {
|
const context: HeaderRenderContext = {
|
||||||
currentWeek: currentDate,
|
currentWeek: currentDate,
|
||||||
config: calendarConfig,
|
config: calendarConfig,
|
||||||
|
|
@ -147,12 +140,12 @@ export class HeaderManager {
|
||||||
*/
|
*/
|
||||||
private getOrCreateCalendarHeader(): HTMLElement | null {
|
private getOrCreateCalendarHeader(): HTMLElement | null {
|
||||||
const calendarHeader = this.getCalendarHeader();
|
const calendarHeader = this.getCalendarHeader();
|
||||||
|
|
||||||
if (!calendarHeader) {
|
if (!calendarHeader) {
|
||||||
console.warn('HeaderManager: Calendar header not found - should always exist now!');
|
console.warn('HeaderManager: Calendar header not found - should always exist now!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return calendarHeader;
|
return calendarHeader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,17 +88,6 @@ export class EventRenderingService {
|
||||||
// Handle all drag events and delegate to appropriate renderer
|
// Handle all drag events and delegate to appropriate renderer
|
||||||
this.setupDragEventListeners();
|
this.setupDragEventListeners();
|
||||||
|
|
||||||
// Listen for conversion from all-day event to time event
|
|
||||||
this.eventBus.on('drag:convert-to-time_event', (event: Event) => {
|
|
||||||
const { draggedElement, mousePosition, column } = (event as CustomEvent).detail;
|
|
||||||
console.log('🔄 EventRendererManager: Received drag:convert-to-time_event', {
|
|
||||||
draggedElement: draggedElement?.dataset.eventId,
|
|
||||||
mousePosition,
|
|
||||||
column
|
|
||||||
});
|
|
||||||
this.handleConvertToTimeEvent(draggedElement, mousePosition, column);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -301,60 +290,7 @@ export class EventRenderingService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle conversion from all-day event to time event
|
|
||||||
*/
|
|
||||||
private handleConvertToTimeEvent(draggedElement: HTMLElement, mousePosition: { x: number; y: number }, column: string): void {
|
|
||||||
// Use the provided draggedElement directly
|
|
||||||
const allDayClone = draggedElement;
|
|
||||||
const draggedEventId = draggedElement?.dataset.eventId?.replace('clone-', '') || '';
|
|
||||||
|
|
||||||
|
|
||||||
// Use SwpEventElement factory to create day event from all-day event
|
|
||||||
const dayElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement);
|
|
||||||
|
|
||||||
// Remove the all-day clone - it's no longer needed since we're converting to day event
|
|
||||||
allDayClone.remove();
|
|
||||||
|
|
||||||
// Set clone ID
|
|
||||||
dayElement.dataset.eventId = `clone-${draggedEventId}`;
|
|
||||||
|
|
||||||
// Find target column
|
|
||||||
const columnElement = document.querySelector(`swp-day-column[data-date="${column}"]`);
|
|
||||||
if (!columnElement) {
|
|
||||||
console.warn('EventRendererManager: Target column not found', { column });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find events layer in the column
|
|
||||||
const eventsLayer = columnElement.querySelector('swp-events-layer');
|
|
||||||
if (!eventsLayer) {
|
|
||||||
console.warn('EventRendererManager: Events layer not found in column');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to events layer
|
|
||||||
eventsLayer.appendChild(dayElement);
|
|
||||||
|
|
||||||
// Position based on mouse Y coordinate
|
|
||||||
const columnRect = columnElement.getBoundingClientRect();
|
|
||||||
const relativeY = Math.max(0, mousePosition.y - columnRect.top);
|
|
||||||
dayElement.style.top = `${relativeY}px`;
|
|
||||||
|
|
||||||
// Set drag styling
|
|
||||||
dayElement.style.zIndex = '1000';
|
|
||||||
dayElement.style.cursor = 'grabbing';
|
|
||||||
dayElement.style.opacity = '';
|
|
||||||
dayElement.style.transform = '';
|
|
||||||
|
|
||||||
console.log('✅ EventRendererManager: Converted all-day event to time event', {
|
|
||||||
draggedEventId,
|
|
||||||
column,
|
|
||||||
mousePosition,
|
|
||||||
relativeY
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-render affected columns after drag to recalculate stacking/grouping
|
* Re-render affected columns after drag to recalculate stacking/grouping
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,18 @@ export interface DragMouseLeaveHeaderEventPayload {
|
||||||
draggedClone: HTMLElement| null;
|
draggedClone: HTMLElement| null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drag mouse enter column event payload
|
||||||
|
export interface DragMouseEnterColumnEventPayload {
|
||||||
|
targetColumn: ColumnBounds;
|
||||||
|
mousePosition: MousePosition;
|
||||||
|
snappedY: number;
|
||||||
|
originalElement: HTMLElement | null;
|
||||||
|
draggedClone: HTMLElement;
|
||||||
|
calendarEvent: CalendarEvent;
|
||||||
|
// Delegate pattern - allows subscriber to replace the dragged clone
|
||||||
|
replaceClone: (newClone: HTMLElement) => void;
|
||||||
|
}
|
||||||
|
|
||||||
// Drag column change event payload
|
// Drag column change event payload
|
||||||
export interface DragColumnChangeEventPayload {
|
export interface DragColumnChangeEventPayload {
|
||||||
originalElement: HTMLElement;
|
originalElement: HTMLElement;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue