Improves drag and drop functionality
Refactors drag and drop logic to use the dragged clone consistently, fixing issues with event handling and element manipulation during drag operations. Also includes a fix where the original element is removed after a drag is completed. Adds column bounds cache update after drag operations for improved column detection.
This commit is contained in:
parent
83e01f9cb7
commit
5417a2b6b1
7 changed files with 50 additions and 45 deletions
|
|
@ -74,7 +74,7 @@ export class AllDayManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.on('drag:column-change', (event) => {
|
eventBus.on('drag:column-change', (event) => {
|
||||||
const { draggedElement, draggedClone, mousePosition } = (event as CustomEvent<DragColumnChangeEventPayload>).detail;
|
const { originalElement: draggedElement, draggedClone, mousePosition } = (event as CustomEvent<DragColumnChangeEventPayload>).detail;
|
||||||
|
|
||||||
if (draggedClone == null)
|
if (draggedClone == null)
|
||||||
return;
|
return;
|
||||||
|
|
@ -316,8 +316,13 @@ export class AllDayManager {
|
||||||
* During drag: Place in row 1 only, calculate column from targetDate
|
* During drag: Place in row 1 only, calculate column from targetDate
|
||||||
*/
|
*/
|
||||||
private handleConvertToAllDay(payload: DragMouseEnterHeaderEventPayload): void {
|
private handleConvertToAllDay(payload: DragMouseEnterHeaderEventPayload): void {
|
||||||
|
|
||||||
|
if(payload.draggedClone?.dataset == null)
|
||||||
|
console.error("payload.cloneElement.dataset.eventId is null");
|
||||||
|
|
||||||
|
|
||||||
console.log('🔄 AllDayManager: Converting to all-day (row 1 only during drag)', {
|
console.log('🔄 AllDayManager: Converting to all-day (row 1 only during drag)', {
|
||||||
eventId: payload.cloneElement.dataset.eventId,
|
eventId: payload.draggedClone.dataset.eventId,
|
||||||
targetDate: payload.targetColumn
|
targetDate: payload.targetColumn
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -325,17 +330,18 @@ export class AllDayManager {
|
||||||
let allDayContainer = this.getAllDayContainer();
|
let allDayContainer = this.getAllDayContainer();
|
||||||
|
|
||||||
|
|
||||||
payload.cloneElement.removeAttribute('style');
|
payload.draggedClone.removeAttribute('style');
|
||||||
payload.cloneElement.classList.add('all-day-style');
|
payload.draggedClone.style.gridRow = '1';
|
||||||
payload.cloneElement.style.gridRow = '1';
|
payload.draggedClone.style.gridColumn = payload.targetColumn.index.toString();
|
||||||
payload.cloneElement.style.gridColumn = payload.targetColumn.index.toString();
|
payload.draggedClone.dataset.allday = 'true'; // Set the all-day attribute for filtering
|
||||||
payload.cloneElement.dataset.allday = 'true'; // Set the all-day attribute for filtering
|
|
||||||
|
|
||||||
// Add to container
|
// Add to container
|
||||||
allDayContainer?.appendChild(payload.cloneElement);
|
allDayContainer?.appendChild(payload.draggedClone);
|
||||||
|
|
||||||
|
ColumnDetectionUtils.updateColumnBoundsCache();
|
||||||
|
|
||||||
console.log('✅ AllDayManager: Converted to all-day style (simple row 1)', {
|
console.log('✅ AllDayManager: Converted to all-day style (simple row 1)', {
|
||||||
eventId: payload.cloneElement.dataset.eventId,
|
eventId: payload.draggedClone.dataset.eventId,
|
||||||
gridColumn: payload.targetColumn,
|
gridColumn: payload.targetColumn,
|
||||||
gridRow: 1
|
gridRow: 1
|
||||||
});
|
});
|
||||||
|
|
@ -459,7 +465,7 @@ export class AllDayManager {
|
||||||
|
|
||||||
if (oldGridArea !== newGridArea) {
|
if (oldGridArea !== newGridArea) {
|
||||||
changedCount++;
|
changedCount++;
|
||||||
const element = document.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
|
const element = dragEndEvent.draggedClone; //:end document.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`) as HTMLElement;
|
||||||
if (element) {
|
if (element) {
|
||||||
|
|
||||||
// Add transition class for smooth animation
|
// Add transition class for smooth animation
|
||||||
|
|
@ -481,6 +487,7 @@ export class AllDayManager {
|
||||||
dragEndEvent.draggedClone.style.opacity = '';
|
dragEndEvent.draggedClone.style.opacity = '';
|
||||||
|
|
||||||
// 7. Restore original element opacity
|
// 7. Restore original element opacity
|
||||||
|
dragEndEvent.originalElement.remove();
|
||||||
//originalElement.style.opacity = '';
|
//originalElement.style.opacity = '';
|
||||||
|
|
||||||
// 8. Check if height adjustment is needed
|
// 8. Check if height adjustment is needed
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ export class DragDropManager {
|
||||||
|
|
||||||
// Clean up drag state first
|
// Clean up drag state first
|
||||||
this.cleanupDragState();
|
this.cleanupDragState();
|
||||||
|
ColumnDetectionUtils.updateColumnBoundsCache();
|
||||||
this.lastMousePosition = { x: event.clientX, y: event.clientY };
|
this.lastMousePosition = { x: event.clientX, y: event.clientY };
|
||||||
this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
|
this.lastLoggedPosition = { x: event.clientX, y: event.clientY };
|
||||||
this.initialMousePosition = { x: event.clientX, y: event.clientY };
|
this.initialMousePosition = { x: event.clientX, y: event.clientY };
|
||||||
|
|
@ -171,16 +171,18 @@ export class DragDropManager {
|
||||||
this.currentMouseY = event.clientY;
|
this.currentMouseY = event.clientY;
|
||||||
this.lastMousePosition = { x: event.clientX, y: event.clientY };
|
this.lastMousePosition = { x: event.clientX, y: event.clientY };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (event.buttons === 1) {
|
||||||
|
const currentPosition: MousePosition = { x: event.clientX, y: event.clientY }; //TODO: Is this really needed? why not just use event.clientX + Y directly
|
||||||
|
|
||||||
// Check for header enter/leave during drag
|
// Check for header enter/leave during drag
|
||||||
if (this.draggedElement) {
|
if (this.draggedClone) {
|
||||||
this.checkHeaderEnterLeave(event);
|
this.checkHeaderEnterLeave(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.buttons === 1 && this.draggedElement) {
|
|
||||||
const currentPosition: MousePosition = { x: event.clientX, y: event.clientY }; //TODO: Is this really needed? why not just use event.clientX + Y directly
|
|
||||||
|
|
||||||
// Check if we need to start drag (movement threshold)
|
// Check if we need to start drag (movement threshold)
|
||||||
if (!this.isDragStarted) {
|
if (!this.isDragStarted && this.draggedElement) {
|
||||||
const deltaX = Math.abs(currentPosition.x - this.initialMousePosition.x);
|
const deltaX = Math.abs(currentPosition.x - this.initialMousePosition.x);
|
||||||
const deltaY = Math.abs(currentPosition.y - this.initialMousePosition.y);
|
const deltaY = Math.abs(currentPosition.y - this.initialMousePosition.y);
|
||||||
const totalMovement = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
const totalMovement = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
|
@ -214,7 +216,7 @@ export class DragDropManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue with normal drag behavior only if drag has started
|
// Continue with normal drag behavior only if drag has started
|
||||||
if (this.isDragStarted) {
|
if (this.isDragStarted && this.draggedElement && this.draggedClone) {
|
||||||
const deltaY = Math.abs(currentPosition.y - this.lastLoggedPosition.y);
|
const deltaY = Math.abs(currentPosition.y - this.lastLoggedPosition.y);
|
||||||
|
|
||||||
// Check for snap interval vertical movement (normal drag behavior)
|
// Check for snap interval vertical movement (normal drag behavior)
|
||||||
|
|
@ -227,6 +229,7 @@ export class DragDropManager {
|
||||||
// Emit drag move event with snapped position (normal behavior)
|
// Emit drag move event with snapped position (normal behavior)
|
||||||
const dragMovePayload: DragMoveEventPayload = {
|
const dragMovePayload: DragMoveEventPayload = {
|
||||||
draggedElement: this.draggedElement,
|
draggedElement: this.draggedElement,
|
||||||
|
draggedClone: this.draggedClone,
|
||||||
mousePosition: currentPosition,
|
mousePosition: currentPosition,
|
||||||
snappedY: positionData.snappedY,
|
snappedY: positionData.snappedY,
|
||||||
columnBounds: positionData.column,
|
columnBounds: positionData.column,
|
||||||
|
|
@ -246,7 +249,7 @@ export class DragDropManager {
|
||||||
this.currentColumnBounds = newColumn;
|
this.currentColumnBounds = newColumn;
|
||||||
|
|
||||||
const dragColumnChangePayload: DragColumnChangeEventPayload = {
|
const dragColumnChangePayload: DragColumnChangeEventPayload = {
|
||||||
draggedElement: this.draggedElement,
|
originalElement: this.draggedElement,
|
||||||
draggedClone: this.draggedClone,
|
draggedClone: this.draggedClone,
|
||||||
previousColumn,
|
previousColumn,
|
||||||
newColumn,
|
newColumn,
|
||||||
|
|
@ -276,6 +279,9 @@ export class DragDropManager {
|
||||||
// Detect drop target (swp-day-column or swp-day-header)
|
// Detect drop target (swp-day-column or swp-day-header)
|
||||||
const dropTarget = this.detectDropTarget(mousePosition);
|
const dropTarget = this.detectDropTarget(mousePosition);
|
||||||
|
|
||||||
|
if(!dropTarget)
|
||||||
|
throw "dropTarget is null";
|
||||||
|
|
||||||
console.log('🎯 DragDropManager: Emitting drag:end', {
|
console.log('🎯 DragDropManager: Emitting drag:end', {
|
||||||
draggedElement: this.draggedElement.dataset.eventId,
|
draggedElement: this.draggedElement.dataset.eventId,
|
||||||
finalColumn: positionData.column,
|
finalColumn: positionData.column,
|
||||||
|
|
@ -285,7 +291,7 @@ export class DragDropManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
const dragEndPayload: DragEndEventPayload = {
|
const dragEndPayload: DragEndEventPayload = {
|
||||||
draggedElement: this.draggedElement,
|
originalElement: this.draggedElement,
|
||||||
draggedClone: this.draggedClone,
|
draggedClone: this.draggedClone,
|
||||||
mousePosition,
|
mousePosition,
|
||||||
finalPosition: positionData,
|
finalPosition: positionData,
|
||||||
|
|
@ -293,7 +299,6 @@ export class DragDropManager {
|
||||||
};
|
};
|
||||||
this.eventBus.emit('drag:end', dragEndPayload);
|
this.eventBus.emit('drag:end', dragEndPayload);
|
||||||
|
|
||||||
this.draggedElement.remove(); // TODO: this should be changed into a subscriber which only after a succesful placement is fired, not just mouseup as this can remove elements that are not placed.
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// This was just a click - emit click event instead
|
// This was just a click - emit click event instead
|
||||||
|
|
@ -489,7 +494,7 @@ export class DragDropManager {
|
||||||
const isCurrentlyInHeader = !!headerElement;
|
const isCurrentlyInHeader = !!headerElement;
|
||||||
|
|
||||||
// Detect header enter
|
// Detect header enter
|
||||||
if (!this.isInHeader && isCurrentlyInHeader) {
|
if (!this.isInHeader && isCurrentlyInHeader && this.draggedClone) {
|
||||||
this.isInHeader = true;
|
this.isInHeader = true;
|
||||||
|
|
||||||
// Calculate target date using existing method
|
// Calculate target date using existing method
|
||||||
|
|
@ -498,15 +503,11 @@ export class DragDropManager {
|
||||||
if (targetColumn) {
|
if (targetColumn) {
|
||||||
console.log('🎯 DragDropManager: Emitting drag:mouseenter-header', { targetDate: targetColumn });
|
console.log('🎯 DragDropManager: Emitting drag:mouseenter-header', { targetDate: targetColumn });
|
||||||
|
|
||||||
// Find clone element (if it exists)
|
|
||||||
const eventId = this.draggedElement?.dataset.eventId;
|
|
||||||
const cloneElement = document.querySelector(`[data-event-id="clone-${eventId}"]`) as HTMLElement;
|
|
||||||
|
|
||||||
const dragMouseEnterPayload: DragMouseEnterHeaderEventPayload = {
|
const dragMouseEnterPayload: DragMouseEnterHeaderEventPayload = {
|
||||||
targetColumn: targetColumn,
|
targetColumn: targetColumn,
|
||||||
mousePosition: { x: event.clientX, y: event.clientY },
|
mousePosition: { x: event.clientX, y: event.clientY },
|
||||||
originalElement: this.draggedElement,
|
originalElement: this.draggedElement,
|
||||||
cloneElement: cloneElement
|
draggedClone: this.draggedClone
|
||||||
};
|
};
|
||||||
this.eventBus.emit('drag:mouseenter-header', dragMouseEnterPayload);
|
this.eventBus.emit('drag:mouseenter-header', dragMouseEnterPayload);
|
||||||
}
|
}
|
||||||
|
|
@ -525,15 +526,12 @@ export class DragDropManager {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
// Find clone element (if it exists)
|
|
||||||
const eventId = this.draggedElement?.dataset.eventId;
|
|
||||||
const cloneElement = document.querySelector(`[data-event-id="clone-${eventId}"]`) as HTMLElement;
|
|
||||||
|
|
||||||
const dragMouseLeavePayload: DragMouseLeaveHeaderEventPayload = {
|
const dragMouseLeavePayload: DragMouseLeaveHeaderEventPayload = {
|
||||||
targetDate: targetColumn.date,
|
targetDate: targetColumn.date,
|
||||||
mousePosition: { x: event.clientX, y: event.clientY },
|
mousePosition: { x: event.clientX, y: event.clientY },
|
||||||
originalElement: this.draggedElement,
|
originalElement: this.draggedElement,
|
||||||
cloneElement: cloneElement
|
draggedClone: this.draggedClone
|
||||||
};
|
};
|
||||||
this.eventBus.emit('drag:mouseleave-header', dragMouseLeavePayload);
|
this.eventBus.emit('drag:mouseleave-header', dragMouseLeavePayload);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ 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, 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,
|
||||||
|
|
@ -65,7 +65,7 @@ export class HeaderManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
||||||
const { targetDate, mousePosition, originalElement, 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,
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ export class EventRenderingService {
|
||||||
|
|
||||||
// Handle drag end events and delegate to appropriate renderer
|
// Handle drag end events and delegate to appropriate renderer
|
||||||
this.eventBus.on('drag:end', (event: Event) => {
|
this.eventBus.on('drag:end', (event: Event) => {
|
||||||
const { draggedElement, finalPosition, target } = (event as CustomEvent<DragEndEventPayload>).detail;
|
const { originalElement: draggedElement, finalPosition, target } = (event as CustomEvent<DragEndEventPayload>).detail;
|
||||||
const finalColumn = finalPosition.column;
|
const finalColumn = finalPosition.column;
|
||||||
const finalY = finalPosition.snappedY;
|
const finalY = finalPosition.snappedY;
|
||||||
const eventId = draggedElement.dataset.eventId || '';
|
const eventId = draggedElement.dataset.eventId || '';
|
||||||
|
|
@ -252,14 +252,14 @@ export class EventRenderingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.strategy.handleColumnChange) {
|
if (this.strategy.handleColumnChange) {
|
||||||
const eventId = columnChangeEvent.draggedElement.dataset.eventId || '';
|
const eventId = columnChangeEvent.originalElement.dataset.eventId || '';
|
||||||
this.strategy.handleColumnChange(columnChangeEvent);
|
this.strategy.handleColumnChange(columnChangeEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
||||||
const { targetDate, mousePosition, originalElement, cloneElement } = (event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
||||||
|
|
||||||
if (cloneElement)
|
if (cloneElement)
|
||||||
cloneElement.style.display = '';
|
cloneElement.style.display = '';
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ export class GridStyleManager {
|
||||||
* Set time-related CSS variables
|
* Set time-related CSS variables
|
||||||
*/
|
*/
|
||||||
private setTimeVariables(root: HTMLElement, gridSettings: GridSettings): void {
|
private setTimeVariables(root: HTMLElement, gridSettings: GridSettings): void {
|
||||||
|
root.style.setProperty('--header-height', '80px'); // Fixed header height
|
||||||
root.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`);
|
root.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`);
|
||||||
root.style.setProperty('--minute-height', `${gridSettings.hourHeight / 60}px`);
|
root.style.setProperty('--minute-height', `${gridSettings.hourHeight / 60}px`);
|
||||||
root.style.setProperty('--snap-interval', gridSettings.snapInterval.toString());
|
root.style.setProperty('--snap-interval', gridSettings.snapInterval.toString());
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ export interface DragStartEventPayload {
|
||||||
// Drag move event payload
|
// Drag move event payload
|
||||||
export interface DragMoveEventPayload {
|
export interface DragMoveEventPayload {
|
||||||
draggedElement: HTMLElement;
|
draggedElement: HTMLElement;
|
||||||
|
draggedClone: HTMLElement;
|
||||||
mousePosition: MousePosition;
|
mousePosition: MousePosition;
|
||||||
mouseOffset: MousePosition;
|
mouseOffset: MousePosition;
|
||||||
columnBounds: ColumnBounds | null;
|
columnBounds: ColumnBounds | null;
|
||||||
|
|
@ -65,7 +66,7 @@ export interface DragMoveEventPayload {
|
||||||
|
|
||||||
// Drag end event payload
|
// Drag end event payload
|
||||||
export interface DragEndEventPayload {
|
export interface DragEndEventPayload {
|
||||||
draggedElement: HTMLElement;
|
originalElement: HTMLElement;
|
||||||
draggedClone: HTMLElement | null;
|
draggedClone: HTMLElement | null;
|
||||||
mousePosition: MousePosition;
|
mousePosition: MousePosition;
|
||||||
finalPosition: {
|
finalPosition: {
|
||||||
|
|
@ -80,7 +81,7 @@ export interface DragMouseEnterHeaderEventPayload {
|
||||||
targetColumn: ColumnBounds;
|
targetColumn: ColumnBounds;
|
||||||
mousePosition: MousePosition;
|
mousePosition: MousePosition;
|
||||||
originalElement: HTMLElement | null;
|
originalElement: HTMLElement | null;
|
||||||
cloneElement: HTMLElement;
|
draggedClone: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drag mouse leave header event payload
|
// Drag mouse leave header event payload
|
||||||
|
|
@ -88,12 +89,12 @@ export interface DragMouseLeaveHeaderEventPayload {
|
||||||
targetDate: string | null;
|
targetDate: string | null;
|
||||||
mousePosition: MousePosition;
|
mousePosition: MousePosition;
|
||||||
originalElement: HTMLElement| null;
|
originalElement: HTMLElement| null;
|
||||||
cloneElement: HTMLElement| null;
|
draggedClone: HTMLElement| null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drag column change event payload
|
// Drag column change event payload
|
||||||
export interface DragColumnChangeEventPayload {
|
export interface DragColumnChangeEventPayload {
|
||||||
draggedElement: HTMLElement;
|
originalElement: HTMLElement;
|
||||||
draggedClone: HTMLElement | null;
|
draggedClone: HTMLElement | null;
|
||||||
previousColumn: ColumnBounds | null;
|
previousColumn: ColumnBounds | null;
|
||||||
newColumn: ColumnBounds;
|
newColumn: ColumnBounds;
|
||||||
|
|
|
||||||
|
|
@ -317,14 +317,12 @@ swp-allday-container swp-event {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide time element for all-day styled events */
|
/* Hide time element for all-day styled events */
|
||||||
swp-allday-container swp-event swp-event-time,
|
swp-allday-container swp-event swp-event-time{
|
||||||
swp-event.all-day-style swp-event-time {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust title display for all-day styled events */
|
/* Adjust title display for all-day styled events */
|
||||||
swp-allday-container swp-event swp-event-title,
|
swp-allday-container swp-event swp-event-title {
|
||||||
swp-event.all-day-style swp-event-title {
|
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue