249 lines
No EOL
8.8 KiB
TypeScript
249 lines
No EOL
8.8 KiB
TypeScript
import { IEventBus } from '../types/CalendarTypes';
|
|
import { CoreEvents } from '../constants/CoreEvents';
|
|
import { CalendarConfig } from '../core/CalendarConfig';
|
|
import { DateCalculator } from '../utils/DateCalculator';
|
|
import { EventRenderingService } from './EventRendererManager';
|
|
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
|
import { eventBus } from '../core/EventBus';
|
|
|
|
/**
|
|
* NavigationRenderer - Handles DOM rendering for navigation containers
|
|
* Separated from NavigationManager to follow Single Responsibility Principle
|
|
*/
|
|
export class NavigationRenderer {
|
|
private eventBus: IEventBus;
|
|
private config: CalendarConfig;
|
|
private dateCalculator: DateCalculator;
|
|
private eventRenderer: EventRenderingService;
|
|
|
|
constructor(eventBus: IEventBus, config: CalendarConfig, eventRenderer: EventRenderingService) {
|
|
this.eventBus = eventBus;
|
|
this.config = config;
|
|
this.eventRenderer = eventRenderer;
|
|
this.dateCalculator = new DateCalculator(config);
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
/**
|
|
* Setup event listeners for DOM updates
|
|
*/
|
|
private setupEventListeners(): void {
|
|
this.eventBus.on(CoreEvents.WEEK_CHANGED, (event: Event) => {
|
|
const customEvent = event as CustomEvent;
|
|
const { weekNumber, dateRange } = customEvent.detail;
|
|
this.updateWeekInfoInDOM(weekNumber, dateRange);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update week info in DOM elements
|
|
*/
|
|
private updateWeekInfoInDOM(weekNumber: number, dateRange: string): void {
|
|
const weekNumberElement = document.querySelector('swp-week-number');
|
|
const dateRangeElement = document.querySelector('swp-date-range');
|
|
|
|
if (weekNumberElement) {
|
|
weekNumberElement.textContent = `Week ${weekNumber}`;
|
|
}
|
|
|
|
if (dateRangeElement) {
|
|
dateRangeElement.textContent = dateRange;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply filter state to pre-rendered grids
|
|
*/
|
|
public applyFilterToPreRenderedGrids(filterState: { active: boolean; matchingIds: string[] }): void {
|
|
// Find all grid containers (including pre-rendered ones)
|
|
const allGridContainers = document.querySelectorAll('swp-grid-container');
|
|
|
|
allGridContainers.forEach(container => {
|
|
const eventsLayers = container.querySelectorAll('swp-events-layer');
|
|
|
|
eventsLayers.forEach(layer => {
|
|
if (filterState.active) {
|
|
// Apply filter active state
|
|
layer.setAttribute('data-filter-active', 'true');
|
|
|
|
// Mark matching events in this layer
|
|
const events = layer.querySelectorAll('swp-event');
|
|
events.forEach(event => {
|
|
const eventId = event.getAttribute('data-event-id');
|
|
if (eventId && filterState.matchingIds.includes(eventId)) {
|
|
event.setAttribute('data-matches', 'true');
|
|
} else {
|
|
event.removeAttribute('data-matches');
|
|
}
|
|
});
|
|
} else {
|
|
// Remove filter state
|
|
layer.removeAttribute('data-filter-active');
|
|
|
|
// Remove all match attributes
|
|
const events = layer.querySelectorAll('swp-event');
|
|
events.forEach(event => {
|
|
event.removeAttribute('data-matches');
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Render a complete container with content and events
|
|
*/
|
|
public renderContainer(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
|
|
const weekEnd = this.dateCalculator.addDays(weekStart, 6);
|
|
|
|
console.group(`🎨 RENDERING CONTAINER: ${weekStart.toDateString()} - ${weekEnd.toDateString()}`);
|
|
console.log('1. Creating grid structure...');
|
|
|
|
// Create new grid container
|
|
const newGrid = document.createElement('swp-grid-container');
|
|
newGrid.innerHTML = `
|
|
<swp-calendar-header></swp-calendar-header>
|
|
<swp-scrollable-content>
|
|
<swp-time-grid>
|
|
<swp-grid-lines></swp-grid-lines>
|
|
<swp-day-columns></swp-day-columns>
|
|
</swp-time-grid>
|
|
</swp-scrollable-content>
|
|
`;
|
|
|
|
// Position new grid - NO transform here, let Animation API handle it
|
|
newGrid.style.position = 'absolute';
|
|
newGrid.style.top = '0';
|
|
newGrid.style.left = '0';
|
|
newGrid.style.width = '100%';
|
|
newGrid.style.height = '100%';
|
|
|
|
// Add to parent container
|
|
parentContainer.appendChild(newGrid);
|
|
|
|
console.log('2. Rendering headers and columns...');
|
|
this.renderWeekContentInContainer(newGrid, weekStart);
|
|
|
|
console.log('3. Emitting GRID_RENDERED for navigation container...');
|
|
this.eventBus.emit(CoreEvents.GRID_RENDERED, {
|
|
container: newGrid, // Specific grid container, not parent
|
|
currentDate: weekStart,
|
|
startDate: weekStart,
|
|
endDate: weekEnd,
|
|
isNavigation: true // Flag to indicate this is navigation rendering
|
|
});
|
|
|
|
console.log('✅ Container ready with GRID_RENDERED event emitted');
|
|
console.groupEnd();
|
|
return newGrid;
|
|
}
|
|
|
|
/**
|
|
* Render week content in specific container
|
|
*/
|
|
private renderWeekContentInContainer(gridContainer: HTMLElement, weekStart: Date): void {
|
|
const header = gridContainer.querySelector('swp-calendar-header');
|
|
const dayColumns = gridContainer.querySelector('swp-day-columns');
|
|
|
|
if (!header || !dayColumns) return;
|
|
|
|
// Clear existing content
|
|
header.innerHTML = '';
|
|
dayColumns.innerHTML = '';
|
|
|
|
// Get dates using DateCalculator
|
|
const dates = this.dateCalculator.getWorkWeekDates(weekStart);
|
|
|
|
// Render headers for target week
|
|
dates.forEach((date, i) => {
|
|
const headerElement = document.createElement('swp-day-header');
|
|
if (this.dateCalculator.isToday(date)) {
|
|
headerElement.dataset.today = 'true';
|
|
}
|
|
|
|
const dayName = this.dateCalculator.getDayName(date, 'short');
|
|
|
|
headerElement.innerHTML = `
|
|
<swp-day-name>${dayName}</swp-day-name>
|
|
<swp-day-date>${date.getDate()}</swp-day-date>
|
|
`;
|
|
headerElement.dataset.date = this.dateCalculator.formatISODate(date);
|
|
|
|
header.appendChild(headerElement);
|
|
});
|
|
|
|
// Always ensure all-day containers exist for all days
|
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(this.config.getCalendarMode());
|
|
headerRenderer.ensureAllDayContainers(header as HTMLElement);
|
|
|
|
// Add event delegation listener for drag & drop functionality
|
|
this.setupHeaderEventListener(header as HTMLElement);
|
|
|
|
// Render day columns for target week
|
|
dates.forEach(date => {
|
|
const column = document.createElement('swp-day-column');
|
|
column.dataset.date = this.dateCalculator.formatISODate(date);
|
|
|
|
const eventsLayer = document.createElement('swp-events-layer');
|
|
column.appendChild(eventsLayer);
|
|
|
|
dayColumns.appendChild(column);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup event delegation listener for header mouseover (same logic as GridRenderer)
|
|
*/
|
|
private setupHeaderEventListener(calendarHeader: HTMLElement): void {
|
|
calendarHeader.addEventListener('mouseover', (event) => {
|
|
const target = event.target as HTMLElement;
|
|
|
|
// Check what was hovered - could be day-header OR all-day-container
|
|
const dayHeader = target.closest('swp-day-header');
|
|
const allDayContainer = target.closest('swp-allday-container');
|
|
|
|
if (dayHeader || allDayContainer) {
|
|
let hoveredElement: HTMLElement;
|
|
let targetDate: string | undefined;
|
|
|
|
if (dayHeader) {
|
|
hoveredElement = dayHeader as HTMLElement;
|
|
targetDate = hoveredElement.dataset.date;
|
|
} else if (allDayContainer) {
|
|
// For all-day areas, we need to determine which day column we're over
|
|
hoveredElement = allDayContainer as HTMLElement;
|
|
|
|
// Calculate which day we're hovering over based on mouse position
|
|
const headerRect = calendarHeader.getBoundingClientRect();
|
|
const dayHeaders = calendarHeader.querySelectorAll('swp-day-header');
|
|
const mouseX = (event as MouseEvent).clientX - headerRect.left;
|
|
const dayWidth = headerRect.width / dayHeaders.length;
|
|
const dayIndex = Math.floor(mouseX / dayWidth);
|
|
|
|
const targetDayHeader = dayHeaders[dayIndex] as HTMLElement;
|
|
targetDate = targetDayHeader?.dataset.date;
|
|
} else {
|
|
return; // No valid element found
|
|
}
|
|
|
|
console.log('NavigationRenderer: Detected hover over:', {
|
|
elementType: dayHeader ? 'day-header' : 'all-day-container',
|
|
targetDate,
|
|
element: hoveredElement
|
|
});
|
|
|
|
// Get the header renderer for addToAllDay functionality
|
|
const calendarType = this.config.getCalendarMode();
|
|
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
|
|
|
eventBus.emit('header:mouseover', {
|
|
element: hoveredElement,
|
|
targetDate,
|
|
headerRenderer
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
} |