Improves all-day event rendering and animation

Refactors all-day event handling to enhance user experience.

Introduces dynamic height animation for all-day event rows, adapting to the number of overlapping events. This ensures efficient use of screen space and prevents unnecessary scrolling.

Additionally, events now store all relevant data, and the header height is checked and animated after navigation.

The previous HeaderRenderer.ts file has been refactored.
This commit is contained in:
Janus Knudsen 2025-09-01 23:37:47 +02:00
parent 58d6ad2ed2
commit 542a6874d0
3 changed files with 121 additions and 32 deletions

View file

@ -5,6 +5,7 @@ import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig';
import { CalendarConfig } from '../core/CalendarConfig';
import { DateCalculator } from '../utils/DateCalculator';
import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents';
/**
* Interface for event rendering strategies
@ -63,6 +64,15 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
const { eventId, targetDate, headerRenderer } = (event as CustomEvent).detail;
this.handleConvertToAllDay(eventId, targetDate, headerRenderer);
});
// Handle navigation period change (when slide animation completes)
eventBus.on(CoreEvents.PERIOD_CHANGED, () => {
// Animate all-day height after navigation completes
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
const headerRenderer = new DateHeaderRenderer();
headerRenderer.checkAndAnimateAllDayHeight();
});
});
}
/**
@ -278,10 +288,14 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
const allDayContainer = calendarHeader.querySelector('swp-allday-container');
if (!allDayContainer) return;
// Extract title
// Extract all original event data
const titleElement = clone.querySelector('swp-event-title');
const eventTitle = titleElement ? titleElement.textContent || 'Untitled' : 'Untitled';
const timeElement = clone.querySelector('swp-event-time');
const eventTime = timeElement ? timeElement.textContent || '' : '';
const eventDuration = timeElement ? timeElement.getAttribute('data-duration') || '' : '';
// Calculate column index
const dayHeaders = document.querySelectorAll('swp-day-header');
let columnIndex = 1;
@ -291,15 +305,19 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
}
});
// Create all-day event
// Create all-day event with standardized data attributes
const allDayEvent = document.createElement('swp-allday-event');
allDayEvent.dataset.eventId = clone.dataset.eventId || '';
allDayEvent.dataset.title = eventTitle;
allDayEvent.dataset.start = `${targetDate}T${eventTime.split(' - ')[0]}:00`;
allDayEvent.dataset.end = `${targetDate}T${eventTime.split(' - ')[1]}:00`;
allDayEvent.dataset.type = clone.dataset.type || 'work';
allDayEvent.dataset.duration = eventDuration;
allDayEvent.textContent = eventTitle;
// Position in grid
(allDayEvent as HTMLElement).style.gridColumn = columnIndex.toString();
(allDayEvent as HTMLElement).style.gridRow = '1';
// grid-row will be set by checkAndAnimateAllDayHeight() based on actual position
// Remove original clone
if (clone.parentElement) {
@ -311,8 +329,16 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Update reference
this.draggedClone = allDayEvent;
// Check if height animation is needed
import('./HeaderRenderer').then(({ DateHeaderRenderer }) => {
const headerRenderer = new DateHeaderRenderer();
headerRenderer.checkAndAnimateAllDayHeight();
});
}
/**
* Fade out and remove element
*/
@ -487,8 +513,14 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Create the all-day event element
const allDayEvent = document.createElement('swp-allday-event');
allDayEvent.textContent = event.title;
allDayEvent.setAttribute('data-event-id', event.id);
allDayEvent.setAttribute('data-type', event.type || 'work');
// Set data attributes directly from CalendarEvent
allDayEvent.dataset.eventId = event.id;
allDayEvent.dataset.title = event.title;
allDayEvent.dataset.start = event.start;
allDayEvent.dataset.end = event.end;
allDayEvent.dataset.type = event.type;
allDayEvent.dataset.duration = event.metadata?.duration?.toString() || '60';
// Set grid position (column and row)
(allDayEvent as HTMLElement).style.gridColumn = span.columnSpan > 1
@ -510,7 +542,11 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
protected renderEvent(event: CalendarEvent, container: Element, config: CalendarConfig): void {
const eventElement = document.createElement('swp-event');
eventElement.dataset.eventId = event.id;
eventElement.dataset.title = event.title;
eventElement.dataset.start = event.start;
eventElement.dataset.end = event.end;
eventElement.dataset.type = event.type;
eventElement.dataset.duration = event.metadata?.duration?.toString() || '60';
// Calculate position based on time
const position = this.calculateEventPosition(event, config);