From 2c934a7a1a10bfe39947c1090d2f979e76b74426 Mon Sep 17 00:00:00 2001 From: Janus Knudsen Date: Thu, 28 Aug 2025 00:13:11 +0200 Subject: [PATCH] Improves all-day event container creation and animation Refactors all-day event container creation to be lazy, improving initial load performance. The container is now created and animated into view only when needed, specifically when the first event is dragged into the all-day section. This avoids unnecessary DOM manipulation and improves the perceived responsiveness of the calendar. --- src/renderers/HeaderRenderer.ts | 52 ++++++++++++++++++----------- wwwroot/css/calendar-layout-css.css | 8 +++-- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/renderers/HeaderRenderer.ts b/src/renderers/HeaderRenderer.ts index 2428d06..02e7d57 100644 --- a/src/renderers/HeaderRenderer.ts +++ b/src/renderers/HeaderRenderer.ts @@ -31,6 +31,8 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { // Find the calendar header element to animate const calendarHeader = dayHeader.closest('swp-calendar-header') as HTMLElement; if (calendarHeader) { + // Ensure container exists BEFORE animation + this.createAllDayMainStructure(calendarHeader); this.animateHeaderExpansion(calendarHeader); } console.log('BaseHeaderRenderer: Header expanded for all-day row'); @@ -38,21 +40,11 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { } /** - * Ensure all-day main container and initial stack level exist (called automatically after header render) + * Ensure all-day containers exist - but don't create them initially, use lazy creation */ ensureAllDayContainers(calendarHeader: HTMLElement): void { - const root = document.documentElement; - const currentHeight = parseInt(getComputedStyle(root).getPropertyValue('--all-day-row-height') || '0'); - - // If all-day row doesn't exist yet, set it up without animation - if (currentHeight === 0) { - root.style.setProperty('--all-day-row-height', `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`); - } - - // Always ensure main container and initial stack level exist - this.createAllDayMainStructure(calendarHeader); - - console.log('BaseHeaderRenderer: Ensured all-day main structure exists'); + // Do nothing initially - containers will be created when first needed + console.log('BaseHeaderRenderer: All-day containers will be created lazily when first event is dragged'); } private animateHeaderExpansion(calendarHeader: HTMLElement): void { @@ -63,8 +55,17 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { // Find header spacer const headerSpacer = document.querySelector('swp-header-spacer') as HTMLElement; - // Animate both header and spacer simultaneously + // Find or create all-day container (it should exist but be hidden) + let allDayContainer = calendarHeader.querySelector('swp-allday-container') as HTMLElement; + if (!allDayContainer) { + // Create container if it doesn't exist + allDayContainer = document.createElement('swp-allday-container'); + calendarHeader.appendChild(allDayContainer); + } + + // Animate header, spacer, and container simultaneously const animations = [ + // Header height animation calendarHeader.animate([ { height: `${currentHeaderHeight}px` }, { height: `${targetHeight}px` } @@ -72,9 +73,20 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { duration: 150, easing: 'ease-out', fill: 'forwards' + }), + + // Container visibility and height animation + allDayContainer.animate([ + { height: '0px', opacity: '0' }, + { height: `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`, opacity: '1' } + ], { + duration: 150, + easing: 'ease-out', + fill: 'forwards' }) ]; + // Add spacer animation if spacer exists if (headerSpacer) { animations.push( headerSpacer.animate([ @@ -93,11 +105,10 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { // Set the CSS variable after animation root.style.setProperty('--all-day-row-height', `${ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT}px`); - // Create all-day main structure after animation - this.createAllDayMainStructure(calendarHeader); - // Notify ScrollManager about header height change eventBus.emit('header:height-changed'); + + console.log('HeaderRenderer: All-day container animated to visible'); }); } @@ -106,12 +117,13 @@ export abstract class BaseHeaderRenderer implements HeaderRenderer { let container = calendarHeader.querySelector('swp-allday-container'); if (!container) { - // Create simple all-day container + // Create simple all-day container (initially hidden) container = document.createElement('swp-allday-container'); calendarHeader.appendChild(container); + console.log('HeaderRenderer: Created swp-allday-container (initially hidden)'); + } else { + console.log('HeaderRenderer: swp-allday-container already exists'); } - - console.log('Created simple all-day container'); } } diff --git a/wwwroot/css/calendar-layout-css.css b/wwwroot/css/calendar-layout-css.css index 19a03a5..2228408 100644 --- a/wwwroot/css/calendar-layout-css.css +++ b/wwwroot/css/calendar-layout-css.css @@ -267,7 +267,7 @@ swp-day-header[data-today="true"] swp-day-date { } -/* All-day container - simple container with dynamic grid-template-rows */ +/* All-day container - initially hidden, animated in when first event is dragged */ swp-allday-container { grid-column: 1 / -1; /* Span all columns */ grid-row: 2; /* Second row of calendar header */ @@ -278,7 +278,11 @@ swp-allday-container { padding: 2px; align-items: center; overflow: hidden; - min-height: var(--allday-event-height, 26px); + + /* Initially hidden - no CSS transitions, use Web Animations API */ + height: 0; + min-height: 0; + opacity: 0; } /* All-day events in containers */