Allows dynamic drag clone replacement
Introduces a polymorphic `createClone` method on base event elements to customize clone generation. Adds a `replaceClone` delegate to drag event payloads, enabling subscribers to dynamically swap the active dragged clone. This supports scenarios like converting a standard event clone to an all-day event clone when dragging to the all-day header.
This commit is contained in:
parent
125cd678a3
commit
5fae433afb
5 changed files with 641 additions and 31 deletions
|
|
@ -7,7 +7,7 @@ import { DateService } from '../utils/DateService';
|
|||
/**
|
||||
* Base class for event elements
|
||||
*/
|
||||
abstract class BaseSwpEventElement extends HTMLElement {
|
||||
export abstract class BaseSwpEventElement extends HTMLElement {
|
||||
protected dateService: DateService;
|
||||
|
||||
constructor() {
|
||||
|
|
@ -16,6 +16,16 @@ abstract class BaseSwpEventElement extends HTMLElement {
|
|||
this.dateService = new DateService(timezone);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Abstract Methods
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Create a clone for drag operations
|
||||
* Must be implemented by subclasses
|
||||
*/
|
||||
public abstract createClone(): HTMLElement;
|
||||
|
||||
// ============================================
|
||||
// Common Getters/Setters
|
||||
// ============================================
|
||||
|
|
@ -312,6 +322,18 @@ export class SwpAllDayEventElement extends BaseSwpEventElement {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone for drag operations
|
||||
*/
|
||||
public createClone(): SwpAllDayEventElement {
|
||||
const clone = this.cloneNode(true) as SwpAllDayEventElement;
|
||||
|
||||
// Apply "clone-" prefix to ID
|
||||
clone.dataset.eventId = `clone-${this.eventId}`;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply CSS grid positioning
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -318,13 +318,20 @@ export class AllDayManager {
|
|||
let allDayContainer = this.getAllDayContainer();
|
||||
if (!allDayContainer) return;
|
||||
|
||||
// Create SwpAllDayEventElement from CalendarEvent
|
||||
const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent);
|
||||
|
||||
// Apply grid positioning
|
||||
allDayElement.style.gridRow = '1';
|
||||
allDayElement.style.gridColumn = payload.targetColumn.index.toString();
|
||||
|
||||
|
||||
// Remove old swp-event clone
|
||||
payload.draggedClone.remove();
|
||||
|
||||
// Call delegate to update DragDropManager's draggedClone reference
|
||||
payload.replaceClone(allDayElement);
|
||||
|
||||
// Append to container
|
||||
allDayContainer.appendChild(allDayElement);
|
||||
|
||||
ColumnDetectionUtils.updateColumnBoundsCache();
|
||||
|
|
@ -372,9 +379,9 @@ export class AllDayManager {
|
|||
|
||||
private handleDragEnd(dragEndEvent: DragEndEventPayload): void {
|
||||
|
||||
const getEventDurationDays = (start: string|undefined, end: string|undefined): number => {
|
||||
|
||||
if(!start || !end)
|
||||
const getEventDurationDays = (start: string | undefined, end: string | undefined): number => {
|
||||
|
||||
if (!start || !end)
|
||||
throw new Error('Undefined start or end - date');
|
||||
|
||||
const startDate = new Date(start);
|
||||
|
|
@ -396,7 +403,6 @@ export class AllDayManager {
|
|||
dragEndEvent.draggedClone.dataset.eventId = dragEndEvent.draggedClone.dataset.eventId?.replace('clone-', '');
|
||||
dragEndEvent.originalElement.dataset.eventId += '_';
|
||||
|
||||
// 3. Create temporary array with existing events + the dropped event
|
||||
let eventId = dragEndEvent.draggedClone.dataset.eventId;
|
||||
let eventDate = dragEndEvent.finalPosition.column?.date;
|
||||
let eventType = dragEndEvent.draggedClone.dataset.type;
|
||||
|
|
@ -404,21 +410,16 @@ export class AllDayManager {
|
|||
if (eventDate == null || eventId == null || eventType == null)
|
||||
return;
|
||||
|
||||
|
||||
// Calculate original event duration
|
||||
|
||||
|
||||
|
||||
const durationDays = getEventDurationDays(dragEndEvent.draggedClone.dataset.start, dragEndEvent.draggedClone.dataset.end);
|
||||
|
||||
|
||||
// Get original dates to preserve time
|
||||
const originalStartDate = new Date(dragEndEvent.draggedClone.dataset.start!);
|
||||
const originalEndDate = new Date(dragEndEvent.draggedClone.dataset.end!);
|
||||
|
||||
|
||||
// Create new start date with the new day but preserve original time
|
||||
const newStartDate = new Date(eventDate);
|
||||
newStartDate.setHours(originalStartDate.getHours(), originalStartDate.getMinutes(), originalStartDate.getSeconds(), originalStartDate.getMilliseconds());
|
||||
|
||||
|
||||
// Create new end date with the new day + duration, preserving original end time
|
||||
const newEndDate = new Date(eventDate);
|
||||
newEndDate.setDate(newEndDate.getDate() + durationDays);
|
||||
|
|
@ -464,6 +465,8 @@ export class AllDayManager {
|
|||
element.style.gridRow = layout.row.toString();
|
||||
element.style.gridColumn = `${layout.startColumn} / ${layout.endColumn + 1}`;
|
||||
|
||||
element.classList.remove('max-event-overflow-hide');
|
||||
element.classList.remove('max-event-overflow-show');
|
||||
|
||||
if (layout.row > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS)
|
||||
if (!this.isExpanded)
|
||||
|
|
@ -486,11 +489,8 @@ export class AllDayManager {
|
|||
dragEndEvent.draggedClone.style.cursor = '';
|
||||
dragEndEvent.draggedClone.style.opacity = '';
|
||||
|
||||
// 7. Restore original element opacity
|
||||
//dragEndEvent.originalElement.remove(); //TODO: this should be an event that only fade and remove if confirmed dragdrop
|
||||
this.fadeOutAndRemove(dragEndEvent.originalElement);
|
||||
|
||||
// 8. Check if height adjustment is needed
|
||||
this.checkAndAnimateAllDayHeight();
|
||||
|
||||
}
|
||||
|
|
@ -505,7 +505,7 @@ export class AllDayManager {
|
|||
let chevron = headerSpacer.querySelector('.allday-chevron') as HTMLElement;
|
||||
|
||||
if (show && !chevron) {
|
||||
// Create chevron button
|
||||
|
||||
chevron = document.createElement('button');
|
||||
chevron.className = 'allday-chevron collapsed';
|
||||
chevron.innerHTML = `
|
||||
|
|
@ -515,13 +515,16 @@ export class AllDayManager {
|
|||
`;
|
||||
chevron.onclick = () => this.toggleExpanded();
|
||||
headerSpacer.appendChild(chevron);
|
||||
|
||||
} else if (!show && chevron) {
|
||||
// Remove chevron button
|
||||
|
||||
chevron.remove();
|
||||
|
||||
} else if (chevron) {
|
||||
// Update chevron state
|
||||
|
||||
chevron.classList.toggle('collapsed', !this.isExpanded);
|
||||
chevron.classList.toggle('expanded', this.isExpanded);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -532,12 +535,15 @@ export class AllDayManager {
|
|||
this.isExpanded = !this.isExpanded;
|
||||
this.checkAndAnimateAllDayHeight();
|
||||
|
||||
let elements = document.querySelectorAll('swp-allday-container swp-event.max-event-overflow-hide, swp-allday-container swp-event.max-event-overflow-show');
|
||||
const elements = document.querySelectorAll('swp-allday-container swp-allday-event.max-event-overflow-hide, swp-allday-container swp-allday-event.max-event-overflow-show');
|
||||
|
||||
elements.forEach((element) => {
|
||||
if (element.classList.contains('max-event-overflow-hide')) {
|
||||
if (this.isExpanded) {
|
||||
// ALTID vis når expanded=true
|
||||
element.classList.remove('max-event-overflow-hide');
|
||||
element.classList.add('max-event-overflow-show');
|
||||
} else if (element.classList.contains('max-event-overflow-show')) {
|
||||
} else {
|
||||
// ALTID skjul når expanded=false
|
||||
element.classList.remove('max-event-overflow-show');
|
||||
element.classList.add('max-event-overflow-hide');
|
||||
}
|
||||
|
|
@ -582,7 +588,7 @@ export class AllDayManager {
|
|||
existingIndicator.innerHTML = `<span>+${overflowCount + 1} more</span>`;
|
||||
} else {
|
||||
// Create new overflow indicator element
|
||||
let overflowElement = document.createElement('swp-event');
|
||||
let overflowElement = document.createElement('swp-allday-event');
|
||||
overflowElement.className = 'max-event-indicator';
|
||||
overflowElement.setAttribute('data-column', columnBounds.index.toString());
|
||||
overflowElement.style.gridRow = ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS.toString();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { IEventBus } from '../types/CalendarTypes';
|
|||
import { calendarConfig } from '../core/CalendarConfig';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { ColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||
import { SwpEventElement, BaseSwpEventElement } from '../elements/SwpEventElement';
|
||||
import {
|
||||
DragStartEventPayload,
|
||||
DragMoveEventPayload,
|
||||
|
|
@ -192,9 +192,9 @@ export class DragDropManager {
|
|||
// Detect current column
|
||||
this.currentColumnBounds = ColumnDetectionUtils.getColumnBounds(currentPosition);
|
||||
|
||||
// Cast to SwpEventElement and create clone
|
||||
const originalSwpEvent = this.draggedElement as SwpEventElement;
|
||||
this.draggedClone = originalSwpEvent.createClone();
|
||||
// Cast to BaseSwpEventElement and create clone (works for both SwpEventElement and SwpAllDayEventElement)
|
||||
const originalElement = this.draggedElement as BaseSwpEventElement;
|
||||
this.draggedClone = originalElement.createClone();
|
||||
|
||||
const dragStartPayload: DragStartEventPayload = {
|
||||
draggedElement: this.draggedElement,
|
||||
|
|
@ -499,15 +499,17 @@ export class DragDropManager {
|
|||
|
||||
// Extract CalendarEvent from the dragged clone
|
||||
const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone);
|
||||
|
||||
const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent);
|
||||
|
||||
const dragMouseEnterPayload: DragMouseEnterHeaderEventPayload = {
|
||||
targetColumn: targetColumn,
|
||||
mousePosition: position,
|
||||
originalElement: this.draggedElement,
|
||||
draggedClone: this.draggedClone,
|
||||
calendarEvent: calendarEvent
|
||||
calendarEvent: calendarEvent,
|
||||
// Delegate pattern - allows AllDayManager to replace the clone
|
||||
replaceClone: (newClone: HTMLElement) => {
|
||||
this.draggedClone = newClone;
|
||||
}
|
||||
};
|
||||
this.eventBus.emit('drag:mouseenter-header', dragMouseEnterPayload);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ export interface DragMouseEnterHeaderEventPayload {
|
|||
originalElement: HTMLElement | null;
|
||||
draggedClone: HTMLElement;
|
||||
calendarEvent: CalendarEvent;
|
||||
// Delegate pattern - allows subscriber to replace the dragged clone
|
||||
replaceClone: (newClone: HTMLElement) => void;
|
||||
}
|
||||
|
||||
// Drag mouse leave header event payload
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue