Refactors event element handling with web components
Introduces web components for event elements, separating timed and all-day events into distinct components for better organization and reusability. This change also simplifies event rendering and drag-and-drop operations by leveraging the properties and lifecycle methods of web components.
This commit is contained in:
parent
1a47214831
commit
a9d6d14c93
6 changed files with 253 additions and 274 deletions
|
|
@ -2,166 +2,243 @@ import { CalendarEvent } from '../types/CalendarTypes';
|
|||
import { calendarConfig } from '../core/CalendarConfig';
|
||||
import { TimeFormatter } from '../utils/TimeFormatter';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { EventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { DateService } from '../utils/DateService';
|
||||
|
||||
/**
|
||||
* Abstract base class for event DOM elements
|
||||
* Base class for event elements
|
||||
*/
|
||||
export abstract class BaseEventElement {
|
||||
protected element: HTMLElement;
|
||||
protected event: CalendarEvent;
|
||||
abstract class BaseSwpEventElement extends HTMLElement {
|
||||
protected dateService: DateService;
|
||||
|
||||
protected constructor(event: CalendarEvent) {
|
||||
this.event = event;
|
||||
constructor() {
|
||||
super();
|
||||
const timezone = calendarConfig.getTimezone?.();
|
||||
this.dateService = new DateService(timezone);
|
||||
this.element = this.createElement();
|
||||
this.setDataAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the underlying DOM element
|
||||
*/
|
||||
protected abstract createElement(): HTMLElement;
|
||||
// ============================================
|
||||
// Common Getters/Setters
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Set standard data attributes on the element
|
||||
*/
|
||||
protected setDataAttributes(): void {
|
||||
this.element.dataset.eventId = this.event.id;
|
||||
this.element.dataset.title = this.event.title;
|
||||
this.element.dataset.start = this.dateService.toUTC(this.event.start);
|
||||
this.element.dataset.end = this.dateService.toUTC(this.event.end);
|
||||
this.element.dataset.type = this.event.type;
|
||||
this.element.dataset.duration = this.event.metadata?.duration?.toString() || '60';
|
||||
get eventId(): string {
|
||||
return this.dataset.eventId || '';
|
||||
}
|
||||
set eventId(value: string) {
|
||||
this.dataset.eventId = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DOM element
|
||||
*/
|
||||
public getElement(): HTMLElement {
|
||||
return this.element;
|
||||
get start(): Date {
|
||||
return new Date(this.dataset.start || '');
|
||||
}
|
||||
set start(value: Date) {
|
||||
this.dataset.start = this.dateService.toUTC(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time for display using TimeFormatter
|
||||
*/
|
||||
protected formatTime(date: Date): string {
|
||||
return TimeFormatter.formatTime(date);
|
||||
get end(): Date {
|
||||
return new Date(this.dataset.end || '');
|
||||
}
|
||||
set end(value: Date) {
|
||||
this.dataset.end = this.dateService.toUTC(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate event position for timed events using PositionUtils
|
||||
*/
|
||||
protected calculateEventPosition(): { top: number; height: number } {
|
||||
return PositionUtils.calculateEventPosition(this.event.start, this.event.end);
|
||||
get title(): string {
|
||||
return this.dataset.title || '';
|
||||
}
|
||||
set title(value: string) {
|
||||
this.dataset.title = value;
|
||||
}
|
||||
|
||||
get type(): string {
|
||||
return this.dataset.type || 'work';
|
||||
}
|
||||
set type(value: string) {
|
||||
this.dataset.type = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timed event element (swp-event)
|
||||
* Web Component for timed calendar events (Light DOM)
|
||||
*/
|
||||
export class SwpEventElement extends BaseEventElement {
|
||||
private constructor(event: CalendarEvent) {
|
||||
super(event);
|
||||
this.createInnerStructure();
|
||||
this.applyPositioning();
|
||||
}
|
||||
export class SwpEventElement extends BaseSwpEventElement {
|
||||
|
||||
protected createElement(): HTMLElement {
|
||||
return document.createElement('swp-event');
|
||||
/**
|
||||
* Observed attributes - changes trigger attributeChangedCallback
|
||||
*/
|
||||
static get observedAttributes() {
|
||||
return ['data-start', 'data-end', 'data-title', 'data-type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create inner HTML structure
|
||||
* Called when element is added to DOM
|
||||
*/
|
||||
private createInnerStructure(): void {
|
||||
const timeRange = TimeFormatter.formatTimeRange(this.event.start, this.event.end);
|
||||
const durationMinutes = (this.event.end.getTime() - this.event.start.getTime()) / (1000 * 60);
|
||||
connectedCallback() {
|
||||
if (!this.hasChildNodes()) {
|
||||
this.render();
|
||||
}
|
||||
this.applyPositioning();
|
||||
}
|
||||
|
||||
this.element.innerHTML = `
|
||||
/**
|
||||
* Called when observed attribute changes
|
||||
*/
|
||||
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
||||
if (oldValue !== newValue && this.isConnected) {
|
||||
this.updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Public Methods
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Update event position during drag
|
||||
* @param columnDate - The date of the column
|
||||
* @param snappedY - The Y position in pixels
|
||||
*/
|
||||
public updatePosition(columnDate: Date, snappedY: number): void {
|
||||
// 1. Update visual position
|
||||
this.style.top = `${snappedY + 1}px`;
|
||||
|
||||
// 2. Calculate new timestamps
|
||||
const { startMinutes, endMinutes } = this.calculateTimesFromPosition(snappedY);
|
||||
|
||||
// 3. Update data attributes (triggers attributeChangedCallback)
|
||||
const startDate = this.dateService.createDateAtTime(columnDate, startMinutes);
|
||||
let endDate = this.dateService.createDateAtTime(columnDate, endMinutes);
|
||||
|
||||
// Handle cross-midnight events
|
||||
if (endMinutes >= 1440) {
|
||||
const extraDays = Math.floor(endMinutes / 1440);
|
||||
endDate = this.dateService.addDays(endDate, extraDays);
|
||||
}
|
||||
|
||||
this.start = startDate;
|
||||
this.end = endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone for drag operations
|
||||
*/
|
||||
public createClone(): SwpEventElement {
|
||||
const clone = this.cloneNode(true) as SwpEventElement;
|
||||
|
||||
// Apply "clone-" prefix to ID
|
||||
clone.dataset.eventId = `clone-${this.eventId}`;
|
||||
|
||||
// Cache original duration
|
||||
const timeEl = this.querySelector('swp-event-time');
|
||||
if (timeEl) {
|
||||
const duration = timeEl.getAttribute('data-duration');
|
||||
if (duration) {
|
||||
clone.dataset.originalDuration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
// Set height from original
|
||||
clone.style.height = this.style.height || `${this.getBoundingClientRect().height}px`;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Private Methods
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Render inner HTML structure
|
||||
*/
|
||||
private render(): void {
|
||||
const start = this.start;
|
||||
const end = this.end;
|
||||
const timeRange = TimeFormatter.formatTimeRange(start, end);
|
||||
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
|
||||
|
||||
this.innerHTML = `
|
||||
<swp-event-time data-duration="${durationMinutes}">${timeRange}</swp-event-time>
|
||||
<swp-event-title>${this.event.title}</swp-event-title>
|
||||
<swp-event-title>${this.title}</swp-event-title>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply positioning styles
|
||||
* Update time display when attributes change
|
||||
*/
|
||||
private updateDisplay(): void {
|
||||
const timeEl = this.querySelector('swp-event-time');
|
||||
const titleEl = this.querySelector('swp-event-title');
|
||||
|
||||
if (timeEl && this.dataset.start && this.dataset.end) {
|
||||
const start = new Date(this.dataset.start);
|
||||
const end = new Date(this.dataset.end);
|
||||
const timeRange = TimeFormatter.formatTimeRange(start, end);
|
||||
timeEl.textContent = timeRange;
|
||||
|
||||
// Update duration attribute
|
||||
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
|
||||
timeEl.setAttribute('data-duration', durationMinutes.toString());
|
||||
}
|
||||
|
||||
if (titleEl && this.dataset.title) {
|
||||
titleEl.textContent = this.dataset.title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply initial positioning based on start/end times
|
||||
*/
|
||||
private applyPositioning(): void {
|
||||
const position = this.calculateEventPosition();
|
||||
this.element.style.top = `${position.top + 1}px`;
|
||||
this.element.style.height = `${position.height - 3}px`;
|
||||
this.element.style.left = '2px';
|
||||
this.element.style.right = '2px';
|
||||
const position = PositionUtils.calculateEventPosition(this.start, this.end);
|
||||
this.style.top = `${position.top + 1}px`;
|
||||
this.style.height = `${position.height - 3}px`;
|
||||
this.style.left = '2px';
|
||||
this.style.right = '2px';
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a SwpEventElement from a CalendarEvent
|
||||
* Calculate start/end minutes from Y position
|
||||
*/
|
||||
private calculateTimesFromPosition(snappedY: number): { startMinutes: number; endMinutes: number } {
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const { hourHeight, dayStartHour, snapInterval } = gridSettings;
|
||||
|
||||
// Get original duration
|
||||
const originalDuration = parseInt(
|
||||
this.dataset.originalDuration ||
|
||||
this.dataset.duration ||
|
||||
'60'
|
||||
);
|
||||
|
||||
// Calculate snapped start minutes
|
||||
const minutesFromGridStart = (snappedY / hourHeight) * 60;
|
||||
const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart;
|
||||
const snappedStartMinutes = Math.round(actualStartMinutes / snapInterval) * snapInterval;
|
||||
|
||||
// Calculate end minutes
|
||||
const endMinutes = snappedStartMinutes + originalDuration;
|
||||
|
||||
return { startMinutes: snappedStartMinutes, endMinutes };
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Static Factory Methods
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Create SwpEventElement from CalendarEvent
|
||||
*/
|
||||
public static fromCalendarEvent(event: CalendarEvent): SwpEventElement {
|
||||
return new SwpEventElement(event);
|
||||
}
|
||||
const element = document.createElement('swp-event') as SwpEventElement;
|
||||
const timezone = calendarConfig.getTimezone?.();
|
||||
const dateService = new DateService(timezone);
|
||||
|
||||
/**
|
||||
* Create a clone of this SwpEventElement with "clone-" prefix
|
||||
*/
|
||||
public createClone(): SwpEventElement {
|
||||
// Clone the underlying DOM element
|
||||
const clonedElement = this.element.cloneNode(true) as HTMLElement;
|
||||
element.dataset.eventId = event.id;
|
||||
element.dataset.title = event.title;
|
||||
element.dataset.start = dateService.toUTC(event.start);
|
||||
element.dataset.end = dateService.toUTC(event.end);
|
||||
element.dataset.type = event.type;
|
||||
element.dataset.duration = event.metadata?.duration?.toString() || '60';
|
||||
|
||||
// Create new SwpEventElement instance from the cloned DOM
|
||||
const clonedSwpEvent = SwpEventElement.fromExistingElement(clonedElement);
|
||||
|
||||
// Apply "clone-" prefix to ID
|
||||
clonedSwpEvent.updateEventId(`clone-${this.event.id}`);
|
||||
|
||||
// Cache original duration for drag operations
|
||||
const originalDuration = this.getOriginalEventDuration();
|
||||
clonedSwpEvent.element.dataset.originalDuration = originalDuration.toString();
|
||||
|
||||
// Set height from original element
|
||||
clonedSwpEvent.element.style.height = this.element.style.height || `${this.element.getBoundingClientRect().height}px`;
|
||||
|
||||
return clonedSwpEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create SwpEventElement from existing DOM element
|
||||
*/
|
||||
public static fromExistingElement(element: HTMLElement): SwpEventElement {
|
||||
// Extract CalendarEvent data from DOM element
|
||||
const event = this.extractCalendarEventFromElement(element);
|
||||
|
||||
// Create new instance but replace the created element with the existing one
|
||||
const swpEvent = new SwpEventElement(event);
|
||||
swpEvent.element = element;
|
||||
|
||||
return swpEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the event ID in both the CalendarEvent and DOM element
|
||||
*/
|
||||
private updateEventId(newId: string): void {
|
||||
this.event.id = newId;
|
||||
this.element.dataset.eventId = newId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract original event duration from DOM element
|
||||
*/
|
||||
private getOriginalEventDuration(): number {
|
||||
const timeElement = this.element.querySelector('swp-event-time');
|
||||
if (timeElement) {
|
||||
const duration = timeElement.getAttribute('data-duration');
|
||||
if (duration) {
|
||||
return parseInt(duration);
|
||||
}
|
||||
}
|
||||
return 60; // Fallback
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,7 +263,6 @@ export class SwpEventElement extends BaseEventElement {
|
|||
* Factory method to convert an all-day HTML element to a timed SwpEventElement
|
||||
*/
|
||||
public static fromAllDayElement(allDayElement: HTMLElement): SwpEventElement {
|
||||
// Extract data from all-day element's dataset
|
||||
const eventId = allDayElement.dataset.eventId || '';
|
||||
const title = allDayElement.dataset.title || allDayElement.textContent || 'Untitled';
|
||||
const type = allDayElement.dataset.type || 'work';
|
||||
|
|
@ -198,11 +274,9 @@ export class SwpEventElement extends BaseEventElement {
|
|||
throw new Error('All-day element missing start/end dates');
|
||||
}
|
||||
|
||||
// Parse dates and set reasonable 1-hour duration for timed event
|
||||
const originalStart = new Date(startStr);
|
||||
const duration = durationStr ? parseInt(durationStr) : 60; // Default 1 hour
|
||||
const duration = durationStr ? parseInt(durationStr) : 60;
|
||||
|
||||
// For conversion, use current time or a reasonable default (9 AM)
|
||||
const now = new Date();
|
||||
const startDate = new Date(originalStart);
|
||||
startDate.setHours(now.getHours() || 9, now.getMinutes() || 0, 0, 0);
|
||||
|
|
@ -210,7 +284,6 @@ export class SwpEventElement extends BaseEventElement {
|
|||
const endDate = new Date(startDate);
|
||||
endDate.setMinutes(endDate.getMinutes() + duration);
|
||||
|
||||
// Create CalendarEvent object
|
||||
const calendarEvent: CalendarEvent = {
|
||||
id: eventId,
|
||||
title: title,
|
||||
|
|
@ -224,48 +297,49 @@ export class SwpEventElement extends BaseEventElement {
|
|||
}
|
||||
};
|
||||
|
||||
return new SwpEventElement(calendarEvent);
|
||||
return SwpEventElement.fromCalendarEvent(calendarEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All-day event element (now using unified swp-event tag)
|
||||
* Web Component for all-day calendar events
|
||||
*/
|
||||
export class SwpAllDayEventElement extends BaseEventElement {
|
||||
export class SwpAllDayEventElement extends BaseSwpEventElement {
|
||||
|
||||
constructor(event: CalendarEvent) {
|
||||
super(event);
|
||||
this.setAllDayAttributes();
|
||||
this.createInnerStructure();
|
||||
// this.applyGridPositioning();
|
||||
}
|
||||
|
||||
protected createElement(): HTMLElement {
|
||||
return document.createElement('swp-event');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all-day specific attributes
|
||||
*/
|
||||
private setAllDayAttributes(): void {
|
||||
this.element.dataset.allday = "true";
|
||||
this.element.dataset.start = this.dateService.toUTC(this.event.start);
|
||||
this.element.dataset.end = this.dateService.toUTC(this.event.end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create inner structure (just text content for all-day events)
|
||||
*/
|
||||
private createInnerStructure(): void {
|
||||
this.element.textContent = this.event.title;
|
||||
connectedCallback() {
|
||||
if (!this.textContent) {
|
||||
this.textContent = this.dataset.title || 'Untitled';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply CSS grid positioning
|
||||
*/
|
||||
public applyGridPositioning(layout: EventLayout): void {
|
||||
const gridArea = `${layout.row} / ${layout.startColumn} / ${layout.row + 1} / ${layout.endColumn + 1}`;
|
||||
this.element.style.gridArea = gridArea;
|
||||
public applyGridPositioning(row: number, startColumn: number, endColumn: number): void {
|
||||
const gridArea = `${row} / ${startColumn} / ${row + 1} / ${endColumn + 1}`;
|
||||
this.style.gridArea = gridArea;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Create from CalendarEvent
|
||||
*/
|
||||
public static fromCalendarEvent(event: CalendarEvent): SwpAllDayEventElement {
|
||||
const element = document.createElement('swp-allday-event') as SwpAllDayEventElement;
|
||||
const timezone = calendarConfig.getTimezone?.();
|
||||
const dateService = new DateService(timezone);
|
||||
|
||||
element.dataset.eventId = event.id;
|
||||
element.dataset.title = event.title;
|
||||
element.dataset.start = dateService.toUTC(event.start);
|
||||
element.dataset.end = dateService.toUTC(event.end);
|
||||
element.dataset.type = event.type;
|
||||
element.dataset.allDay = 'true';
|
||||
element.textContent = event.title;
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
// Register custom elements
|
||||
customElements.define('swp-event', SwpEventElement);
|
||||
customElements.define('swp-allday-event', SwpAllDayEventElement);
|
||||
|
|
@ -185,12 +185,9 @@ export class DragDropManager {
|
|||
// Detect current column
|
||||
this.currentColumnBounds = ColumnDetectionUtils.getColumnBounds(currentPosition);
|
||||
|
||||
// Create SwpEventElement from existing DOM element and clone it
|
||||
const originalSwpEvent = SwpEventElement.fromExistingElement(this.draggedElement);
|
||||
const clonedSwpEvent = originalSwpEvent.createClone();
|
||||
|
||||
// Get the cloned DOM element
|
||||
this.draggedClone = clonedSwpEvent.getElement();
|
||||
// Cast to SwpEventElement and create clone
|
||||
const originalSwpEvent = this.draggedElement as SwpEventElement;
|
||||
this.draggedClone = originalSwpEvent.createClone();
|
||||
|
||||
const dragStartPayload: DragStartEventPayload = {
|
||||
draggedElement: this.draggedElement,
|
||||
|
|
|
|||
|
|
@ -76,10 +76,10 @@ export class AllDayEventRenderer {
|
|||
const container = this.getContainer();
|
||||
if (!container) return null;
|
||||
|
||||
let dayEvent = new SwpAllDayEventElement(event);
|
||||
dayEvent.applyGridPositioning(layout);
|
||||
const dayEvent = SwpAllDayEventElement.fromCalendarEvent(event);
|
||||
dayEvent.applyGridPositioning(layout.row, layout.startColumn, layout.endColumn);
|
||||
|
||||
container.appendChild(dayEvent.getElement());
|
||||
container.appendChild(dayEvent);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,86 +43,6 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update clone timestamp based on new position
|
||||
*/
|
||||
private updateCloneTimestamp(payload: DragMoveEventPayload): void {
|
||||
if (payload.draggedClone.dataset.allDay === "true" || !payload.columnBounds) return;
|
||||
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const { hourHeight, dayStartHour, snapInterval } = gridSettings;
|
||||
|
||||
if (!payload.draggedClone.dataset.originalDuration) {
|
||||
throw new DOMException("missing clone.dataset.originalDuration");
|
||||
}
|
||||
|
||||
// Calculate snapped start minutes
|
||||
const minutesFromGridStart = (payload.snappedY / hourHeight) * 60;
|
||||
const snappedStartMinutes = this.calculateSnappedMinutes(
|
||||
minutesFromGridStart, dayStartHour, snapInterval
|
||||
);
|
||||
|
||||
// Calculate end minutes
|
||||
const originalDuration = parseInt(payload.draggedClone.dataset.originalDuration);
|
||||
const endTotalMinutes = snappedStartMinutes + originalDuration;
|
||||
|
||||
// Update UI
|
||||
this.updateTimeDisplay(payload.draggedClone, snappedStartMinutes, endTotalMinutes);
|
||||
|
||||
// Update data attributes
|
||||
this.updateDateTimeAttributes(
|
||||
payload.draggedClone,
|
||||
new Date(payload.columnBounds.date),
|
||||
snappedStartMinutes,
|
||||
endTotalMinutes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate snapped minutes from grid start
|
||||
*/
|
||||
private calculateSnappedMinutes(minutesFromGridStart: number, dayStartHour: number, snapInterval: number): number {
|
||||
const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart;
|
||||
return Math.round(actualStartMinutes / snapInterval) * snapInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update time display in the UI
|
||||
*/
|
||||
private updateTimeDisplay(element: HTMLElement, startMinutes: number, endMinutes: number): void {
|
||||
const timeElement = element.querySelector('swp-event-time');
|
||||
if (!timeElement) return;
|
||||
|
||||
const startTime = this.formatTimeFromMinutes(startMinutes);
|
||||
const endTime = this.formatTimeFromMinutes(endMinutes);
|
||||
timeElement.textContent = `${startTime} - ${endTime}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data-start and data-end attributes with ISO timestamps
|
||||
*/
|
||||
private updateDateTimeAttributes(element: HTMLElement, columnDate: Date, startMinutes: number, endMinutes: number): void {
|
||||
const startDate = this.dateService.createDateAtTime(columnDate, startMinutes);
|
||||
|
||||
let endDate = this.dateService.createDateAtTime(columnDate, endMinutes);
|
||||
|
||||
// Handle cross-midnight events
|
||||
if (endMinutes >= 1440) {
|
||||
const extraDays = Math.floor(endMinutes / 1440);
|
||||
endDate = this.dateService.addDays(endDate, extraDays);
|
||||
}
|
||||
|
||||
// Convert to UTC before storing as ISO string
|
||||
element.dataset.start = this.dateService.toUTC(startDate);
|
||||
element.dataset.end = this.dateService.toUTC(endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format minutes since midnight to time string
|
||||
*/
|
||||
private formatTimeFromMinutes(totalMinutes: number): string {
|
||||
return this.dateService.minutesToTime(totalMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drag start event
|
||||
|
|
@ -155,15 +75,12 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
* Handle drag move event
|
||||
*/
|
||||
public handleDragMove(payload: DragMoveEventPayload): void {
|
||||
if (!this.draggedClone) return;
|
||||
|
||||
// Update position - snappedY is already the event top position
|
||||
// Add +1px to match the initial positioning offset from SwpEventElement
|
||||
this.draggedClone.style.top = (payload.snappedY + 1) + 'px';
|
||||
|
||||
// Update timestamp display
|
||||
this.updateCloneTimestamp(payload);
|
||||
if (!this.draggedClone || !payload.columnBounds) return;
|
||||
|
||||
// Delegate to SwpEventElement to update position and timestamps
|
||||
const swpEvent = this.draggedClone as SwpEventElement;
|
||||
const columnDate = new Date(payload.columnBounds.date);
|
||||
swpEvent.updatePosition(columnDate, payload.snappedY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -191,16 +108,9 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
|
||||
// Recalculate timestamps with new column date
|
||||
const currentTop = parseFloat(this.draggedClone.style.top) || 0;
|
||||
const mockPayload: DragMoveEventPayload = {
|
||||
draggedElement: dragColumnChangeEvent.originalElement,
|
||||
draggedClone: this.draggedClone,
|
||||
mousePosition: dragColumnChangeEvent.mousePosition,
|
||||
mouseOffset: { x: 0, y: 0 },
|
||||
columnBounds: dragColumnChangeEvent.newColumn,
|
||||
snappedY: currentTop
|
||||
};
|
||||
|
||||
this.updateCloneTimestamp(mockPayload);
|
||||
const swpEvent = this.draggedClone as SwpEventElement;
|
||||
const columnDate = new Date(dragColumnChangeEvent.newColumn.date);
|
||||
swpEvent.updatePosition(columnDate, currentTop);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,8 +182,7 @@ export class DateEventRenderer implements EventRendererStrategy {
|
|||
}
|
||||
|
||||
private renderEvent(event: CalendarEvent): HTMLElement {
|
||||
const swpEvent = SwpEventElement.fromCalendarEvent(event);
|
||||
return swpEvent.getElement();
|
||||
return SwpEventElement.fromCalendarEvent(event);
|
||||
}
|
||||
|
||||
protected calculateEventPosition(event: CalendarEvent): { top: number; height: number } {
|
||||
|
|
|
|||
|
|
@ -247,8 +247,7 @@ export class EventRenderingService {
|
|||
|
||||
|
||||
// Use SwpEventElement factory to create day event from all-day event
|
||||
const dayEventElement = SwpEventElement.fromAllDayElement(allDayClone as HTMLElement);
|
||||
const dayElement = dayEventElement.getElement();
|
||||
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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue