Some ignored filles was missing
This commit is contained in:
parent
7db22245e2
commit
fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions
296
wwwroot/js/renderers/EventRenderer.js
Normal file
296
wwwroot/js/renderers/EventRenderer.js
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
// Event rendering strategy interface and implementations
|
||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||
/**
|
||||
* Date-based event renderer
|
||||
*/
|
||||
export class DateEventRenderer {
|
||||
constructor(dateService, stackManager, layoutCoordinator, config, positionUtils) {
|
||||
this.draggedClone = null;
|
||||
this.originalEvent = null;
|
||||
this.dateService = dateService;
|
||||
this.stackManager = stackManager;
|
||||
this.layoutCoordinator = layoutCoordinator;
|
||||
this.config = config;
|
||||
this.positionUtils = positionUtils;
|
||||
}
|
||||
applyDragStyling(element) {
|
||||
element.classList.add('dragging');
|
||||
element.style.removeProperty("margin-left");
|
||||
}
|
||||
/**
|
||||
* Handle drag start event
|
||||
*/
|
||||
handleDragStart(payload) {
|
||||
this.originalEvent = payload.originalElement;
|
||||
;
|
||||
// Use the clone from the payload instead of creating a new one
|
||||
this.draggedClone = payload.draggedClone;
|
||||
if (this.draggedClone && payload.columnBounds) {
|
||||
// Apply drag styling
|
||||
this.applyDragStyling(this.draggedClone);
|
||||
// Add to current column's events layer (not directly to column)
|
||||
const eventsLayer = payload.columnBounds.element.querySelector('swp-events-layer');
|
||||
if (eventsLayer) {
|
||||
eventsLayer.appendChild(this.draggedClone);
|
||||
// Set initial position to prevent "jump to top" effect
|
||||
// Calculate absolute Y position from original element
|
||||
const originalRect = this.originalEvent.getBoundingClientRect();
|
||||
const columnRect = payload.columnBounds.boundingClientRect;
|
||||
const initialTop = originalRect.top - columnRect.top;
|
||||
this.draggedClone.style.top = `${initialTop}px`;
|
||||
}
|
||||
}
|
||||
// Make original semi-transparent
|
||||
this.originalEvent.style.opacity = '0.3';
|
||||
this.originalEvent.style.userSelect = 'none';
|
||||
}
|
||||
/**
|
||||
* Handle drag move event
|
||||
*/
|
||||
handleDragMove(payload) {
|
||||
const swpEvent = payload.draggedClone;
|
||||
const columnDate = this.dateService.parseISO(payload.columnBounds.date);
|
||||
swpEvent.updatePosition(columnDate, payload.snappedY);
|
||||
}
|
||||
/**
|
||||
* Handle column change during drag
|
||||
*/
|
||||
handleColumnChange(payload) {
|
||||
const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer');
|
||||
if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) {
|
||||
eventsLayer.appendChild(payload.draggedClone);
|
||||
// Recalculate timestamps with new column date
|
||||
const currentTop = parseFloat(payload.draggedClone.style.top) || 0;
|
||||
const swpEvent = payload.draggedClone;
|
||||
const columnDate = this.dateService.parseISO(payload.newColumn.date);
|
||||
swpEvent.updatePosition(columnDate, currentTop);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handle conversion of all-day event to timed event
|
||||
*/
|
||||
handleConvertAllDayToTimed(payload) {
|
||||
console.log('🎯 DateEventRenderer: Converting all-day to timed event', {
|
||||
eventId: payload.calendarEvent.id,
|
||||
targetColumn: payload.targetColumn.date,
|
||||
snappedY: payload.snappedY
|
||||
});
|
||||
let timedClone = SwpEventElement.fromCalendarEvent(payload.calendarEvent);
|
||||
let position = this.calculateEventPosition(payload.calendarEvent);
|
||||
// Set position at snapped Y
|
||||
//timedClone.style.top = `${snappedY}px`;
|
||||
// Set complete styling for dragged clone (matching normal event rendering)
|
||||
timedClone.style.height = `${position.height - 3}px`;
|
||||
timedClone.style.left = '2px';
|
||||
timedClone.style.right = '2px';
|
||||
timedClone.style.width = 'auto';
|
||||
timedClone.style.pointerEvents = 'none';
|
||||
// Apply drag styling
|
||||
this.applyDragStyling(timedClone);
|
||||
// Find the events layer in the target column
|
||||
let eventsLayer = payload.targetColumn.element.querySelector('swp-events-layer');
|
||||
// Add "clone-" prefix to match clone ID pattern
|
||||
//timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`;
|
||||
// Remove old all-day clone and replace with new timed clone
|
||||
payload.draggedClone.remove();
|
||||
payload.replaceClone(timedClone);
|
||||
eventsLayer.appendChild(timedClone);
|
||||
}
|
||||
/**
|
||||
* Handle drag end event
|
||||
*/
|
||||
handleDragEnd(originalElement, draggedClone, finalColumn, finalY) {
|
||||
if (!draggedClone || !originalElement) {
|
||||
console.warn('Missing draggedClone or originalElement');
|
||||
return;
|
||||
}
|
||||
// Only fade out and remove if it's a swp-event (not swp-allday-event)
|
||||
// AllDayManager handles removal of swp-allday-event elements
|
||||
if (originalElement.tagName === 'SWP-EVENT') {
|
||||
this.fadeOutAndRemove(originalElement);
|
||||
}
|
||||
// Remove clone prefix and normalize clone to be a regular event
|
||||
const cloneId = draggedClone.dataset.eventId;
|
||||
if (cloneId && cloneId.startsWith('clone-')) {
|
||||
draggedClone.dataset.eventId = cloneId.replace('clone-', '');
|
||||
}
|
||||
// Fully normalize the clone to be a regular event
|
||||
draggedClone.classList.remove('dragging');
|
||||
draggedClone.style.pointerEvents = ''; // Re-enable pointer events
|
||||
// Clean up instance state
|
||||
this.draggedClone = null;
|
||||
this.originalEvent = null;
|
||||
// Clean up any remaining day event clones
|
||||
const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${cloneId}"]`);
|
||||
if (dayEventClone) {
|
||||
dayEventClone.remove();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handle navigation completed event
|
||||
*/
|
||||
handleNavigationCompleted() {
|
||||
// Default implementation - can be overridden by subclasses
|
||||
}
|
||||
/**
|
||||
* Fade out and remove element
|
||||
*/
|
||||
fadeOutAndRemove(element) {
|
||||
element.style.transition = 'opacity 0.3s ease-out';
|
||||
element.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
element.remove();
|
||||
}, 300);
|
||||
}
|
||||
renderEvents(events, container) {
|
||||
// Filter out all-day events - they should be handled by AllDayEventRenderer
|
||||
const timedEvents = events.filter(event => !event.allDay);
|
||||
// Find columns in the specific container for regular events
|
||||
const columns = this.getColumns(container);
|
||||
columns.forEach(column => {
|
||||
const columnEvents = this.getEventsForColumn(column, timedEvents);
|
||||
const eventsLayer = column.querySelector('swp-events-layer');
|
||||
if (eventsLayer) {
|
||||
this.renderColumnEvents(columnEvents, eventsLayer);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Render events for a single column
|
||||
*/
|
||||
renderSingleColumnEvents(column, events) {
|
||||
const columnEvents = this.getEventsForColumn(column.element, events);
|
||||
const eventsLayer = column.element.querySelector('swp-events-layer');
|
||||
if (eventsLayer) {
|
||||
this.renderColumnEvents(columnEvents, eventsLayer);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Render events in a column using combined stacking + grid algorithm
|
||||
*/
|
||||
renderColumnEvents(columnEvents, eventsLayer) {
|
||||
if (columnEvents.length === 0)
|
||||
return;
|
||||
// Get layout from coordinator
|
||||
const layout = this.layoutCoordinator.calculateColumnLayout(columnEvents);
|
||||
// Render grid groups
|
||||
layout.gridGroups.forEach(gridGroup => {
|
||||
this.renderGridGroup(gridGroup, eventsLayer);
|
||||
});
|
||||
// Render stacked events
|
||||
layout.stackedEvents.forEach(stackedEvent => {
|
||||
const element = this.renderEvent(stackedEvent.event);
|
||||
this.stackManager.applyStackLinkToElement(element, stackedEvent.stackLink);
|
||||
this.stackManager.applyVisualStyling(element, stackedEvent.stackLink.stackLevel);
|
||||
eventsLayer.appendChild(element);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Render events in a grid container (side-by-side with column sharing)
|
||||
*/
|
||||
renderGridGroup(gridGroup, eventsLayer) {
|
||||
const groupElement = document.createElement('swp-event-group');
|
||||
// Add grid column class based on number of columns (not events)
|
||||
const colCount = gridGroup.columns.length;
|
||||
groupElement.classList.add(`cols-${colCount}`);
|
||||
// Add stack level class for margin-left offset
|
||||
groupElement.classList.add(`stack-level-${gridGroup.stackLevel}`);
|
||||
// Position from layout
|
||||
groupElement.style.top = `${gridGroup.position.top}px`;
|
||||
// Add stack-link attribute for drag-drop (group acts as a stacked item)
|
||||
const stackLink = {
|
||||
stackLevel: gridGroup.stackLevel
|
||||
};
|
||||
this.stackManager.applyStackLinkToElement(groupElement, stackLink);
|
||||
// Apply visual styling (margin-left and z-index) using StackManager
|
||||
this.stackManager.applyVisualStyling(groupElement, gridGroup.stackLevel);
|
||||
// Render each column
|
||||
const earliestEvent = gridGroup.events[0];
|
||||
gridGroup.columns.forEach((columnEvents) => {
|
||||
const columnContainer = this.renderGridColumn(columnEvents, earliestEvent.start);
|
||||
groupElement.appendChild(columnContainer);
|
||||
});
|
||||
eventsLayer.appendChild(groupElement);
|
||||
}
|
||||
/**
|
||||
* Render a single column within a grid group
|
||||
* Column may contain multiple events that don't overlap
|
||||
*/
|
||||
renderGridColumn(columnEvents, containerStart) {
|
||||
const columnContainer = document.createElement('div');
|
||||
columnContainer.style.position = 'relative';
|
||||
columnEvents.forEach(event => {
|
||||
const element = this.renderEventInGrid(event, containerStart);
|
||||
columnContainer.appendChild(element);
|
||||
});
|
||||
return columnContainer;
|
||||
}
|
||||
/**
|
||||
* Render event within a grid container (absolute positioning within column)
|
||||
*/
|
||||
renderEventInGrid(event, containerStart) {
|
||||
const element = SwpEventElement.fromCalendarEvent(event);
|
||||
// Calculate event height
|
||||
const position = this.calculateEventPosition(event);
|
||||
// Calculate relative top offset if event starts after container start
|
||||
// (e.g., if container starts at 07:00 and event starts at 08:15, offset = 75 min)
|
||||
const timeDiffMs = event.start.getTime() - containerStart.getTime();
|
||||
const timeDiffMinutes = timeDiffMs / (1000 * 60);
|
||||
const gridSettings = this.config.gridSettings;
|
||||
const relativeTop = timeDiffMinutes > 0 ? (timeDiffMinutes / 60) * gridSettings.hourHeight : 0;
|
||||
// Events in grid columns are positioned absolutely within their column container
|
||||
element.style.position = 'absolute';
|
||||
element.style.top = `${relativeTop}px`;
|
||||
element.style.height = `${position.height - 3}px`;
|
||||
element.style.left = '0';
|
||||
element.style.right = '0';
|
||||
return element;
|
||||
}
|
||||
renderEvent(event) {
|
||||
const element = SwpEventElement.fromCalendarEvent(event);
|
||||
// Apply positioning (moved from SwpEventElement.applyPositioning)
|
||||
const position = this.calculateEventPosition(event);
|
||||
element.style.position = 'absolute';
|
||||
element.style.top = `${position.top + 1}px`;
|
||||
element.style.height = `${position.height - 3}px`;
|
||||
element.style.left = '2px';
|
||||
element.style.right = '2px';
|
||||
return element;
|
||||
}
|
||||
calculateEventPosition(event) {
|
||||
// Delegate to PositionUtils for centralized position calculation
|
||||
return this.positionUtils.calculateEventPosition(event.start, event.end);
|
||||
}
|
||||
clearEvents(container) {
|
||||
const eventSelector = 'swp-event';
|
||||
const groupSelector = 'swp-event-group';
|
||||
const existingEvents = container
|
||||
? container.querySelectorAll(eventSelector)
|
||||
: document.querySelectorAll(eventSelector);
|
||||
const existingGroups = container
|
||||
? container.querySelectorAll(groupSelector)
|
||||
: document.querySelectorAll(groupSelector);
|
||||
existingEvents.forEach(event => event.remove());
|
||||
existingGroups.forEach(group => group.remove());
|
||||
}
|
||||
getColumns(container) {
|
||||
const columns = container.querySelectorAll('swp-day-column');
|
||||
return Array.from(columns);
|
||||
}
|
||||
getEventsForColumn(column, events) {
|
||||
const columnDate = column.dataset.date;
|
||||
if (!columnDate) {
|
||||
return [];
|
||||
}
|
||||
// Create start and end of day for interval overlap check
|
||||
const columnStart = this.dateService.parseISO(`${columnDate}T00:00:00`);
|
||||
const columnEnd = this.dateService.parseISO(`${columnDate}T23:59:59.999`);
|
||||
const columnEvents = events.filter(event => {
|
||||
// Interval overlap: event overlaps with column day if event.start < columnEnd AND event.end > columnStart
|
||||
const overlaps = event.start < columnEnd && event.end > columnStart;
|
||||
return overlaps;
|
||||
});
|
||||
return columnEvents;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=EventRenderer.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue