diff --git a/src/managers/EventRenderer.ts b/src/managers/EventRenderer.ts
index e4ff7dc..9bf1ddb 100644
--- a/src/managers/EventRenderer.ts
+++ b/src/managers/EventRenderer.ts
@@ -19,7 +19,14 @@ export class EventRenderer {
this.eventBus.on(EventTypes.EVENTS_LOADED, (event: Event) => {
const customEvent = event as CustomEvent;
const { events } = customEvent.detail;
- this.renderEvents(events);
+ // Store events but don't render yet - wait for grid to be ready
+ this.pendingEvents = events;
+ this.tryRenderEvents();
+ });
+
+ this.eventBus.on(EventTypes.GRID_RENDERED, () => {
+ // Grid is ready, now we can render events
+ this.tryRenderEvents();
});
this.eventBus.on(EventTypes.VIEW_RENDERED, () => {
@@ -28,6 +35,19 @@ export class EventRenderer {
});
}
+ private pendingEvents: CalendarEvent[] = [];
+
+ private tryRenderEvents(): void {
+ // Only render if we have both events and grid is ready
+ if (this.pendingEvents.length > 0) {
+ const dayColumns = document.querySelectorAll('swp-day-column');
+ if (dayColumns.length > 0) {
+ this.renderEvents(this.pendingEvents);
+ this.pendingEvents = []; // Clear pending events after rendering
+ }
+ }
+ }
+
private renderEvents(events: CalendarEvent[]): void {
console.log(`EventRenderer: Rendering ${events.length} events`);
@@ -62,27 +82,53 @@ export class EventRenderer {
}
private renderDayEvents(dayIndex: number, events: CalendarEvent[]): void {
- const dayColumns = document.querySelectorAll('swp-day-column');
- const dayColumn = dayColumns[dayIndex];
- if (!dayColumn) {
- console.warn(`EventRenderer: Day column ${dayIndex} not found`);
- return;
- }
-
- const eventsLayer = dayColumn.querySelector('swp-events-layer');
- if (!eventsLayer) {
- console.warn(`EventRenderer: Events layer not found for day ${dayIndex}`);
- return;
- }
-
// Sort events by start time
const sortedEvents = events.sort((a, b) => a.start.localeCompare(b.start));
sortedEvents.forEach(event => {
- this.renderEvent(event, eventsLayer);
+ // Find the appropriate events container for this event
+ const eventContainer = this.findEventContainer(event, dayIndex);
+ if (eventContainer) {
+ this.renderEvent(event, eventContainer);
+ }
});
}
+ private findEventContainer(event: CalendarEvent, dayIndex: number): Element | null {
+ // Debug: Log what we're looking for
+ console.log(`EventRenderer: Looking for day ${dayIndex} using POC structure`);
+
+ // Check what day columns actually exist
+ const dayColumns = document.querySelectorAll('swp-day-column');
+ console.log(`EventRenderer: Found ${dayColumns.length} day columns total`);
+
+ // Check first few columns to see their attributes
+ for (let i = 0; i < Math.min(3, dayColumns.length); i++) {
+ const column = dayColumns[i] as HTMLElement;
+ console.log(`Column ${i}:`, {
+ dayIndex: column.dataset.dayIndex,
+ date: column.dataset.date,
+ tagName: column.tagName
+ });
+ }
+
+ // Find the day column that corresponds to the event's day
+ const dayColumn = document.querySelector(`swp-day-column[data-dayIndex="${dayIndex}"]`);
+ if (!dayColumn) {
+ console.warn(`EventRenderer: Day column for day ${dayIndex} not found`);
+ return null;
+ }
+
+ // Find the events layer within this day column
+ const eventsLayer = dayColumn.querySelector('swp-events-layer');
+ if (!eventsLayer) {
+ console.warn(`EventRenderer: Events layer not found in day column for day ${dayIndex}`);
+ return null;
+ }
+
+ return eventsLayer;
+ }
+
private renderEvent(event: CalendarEvent, container: Element): void {
const eventElement = document.createElement('swp-event');
eventElement.dataset.eventId = event.id;
@@ -90,8 +136,12 @@ export class EventRenderer {
// Calculate position based on time
const position = this.calculateEventPosition(event);
+ eventElement.style.position = 'absolute';
eventElement.style.top = `${position.top}px`;
eventElement.style.height = `${position.height}px`;
+ eventElement.style.left = '2px';
+ eventElement.style.right = '2px';
+ eventElement.style.zIndex = '10';
// Format time for display
const startTime = this.formatTime(event.start);
diff --git a/src/managers/GridManager.ts b/src/managers/GridManager.ts
index cc41364..30f514e 100644
--- a/src/managers/GridManager.ts
+++ b/src/managers/GridManager.ts
@@ -1,4 +1,4 @@
-// Grid structure management
+// Grid structure management - Simple CSS Grid Implementation
import { eventBus } from '../core/EventBus';
import { calendarConfig } from '../core/CalendarConfig';
@@ -15,15 +15,11 @@ interface GridPosition {
}
/**
- * Manages the calendar grid structure
+ * Manages the calendar grid structure using simple CSS Grid
*/
export class GridManager {
private container: HTMLElement | null = null;
- private timeAxis: HTMLElement | null = null;
- private weekHeader: HTMLElement | null = null;
- private timeGrid: HTMLElement | null = null;
- private dayColumns: HTMLElement | null = null;
- private scrollableContent: HTMLElement | null = null;
+ private grid: HTMLElement | null = null;
private currentWeek: Date | null = null;
constructor() {
@@ -33,15 +29,27 @@ export class GridManager {
private init(): void {
this.findElements();
this.subscribeToEvents();
- this.setupScrollSync();
+
+ // Set initial current week to today if not set
+ if (!this.currentWeek) {
+ this.currentWeek = this.getWeekStart(new Date());
+ console.log('GridManager: Set initial currentWeek to', this.currentWeek);
+ // Render initial grid
+ this.render();
+ }
+ }
+
+ private getWeekStart(date: Date): Date {
+ const weekStart = new Date(date);
+ const day = weekStart.getDay();
+ const diff = weekStart.getDate() - day; // Sunday is 0
+ weekStart.setDate(diff);
+ weekStart.setHours(0, 0, 0, 0);
+ return weekStart;
}
private findElements(): void {
- this.container = document.querySelector('swp-calendar-container');
- this.timeAxis = document.querySelector('swp-time-axis');
- this.weekHeader = document.querySelector('swp-week-header');
- this.timeGrid = document.querySelector('swp-time-grid');
- this.scrollableContent = document.querySelector('swp-scrollable-content');
+ this.grid = document.querySelector('swp-calendar-container');
}
private subscribeToEvents(): void {
@@ -62,7 +70,7 @@ export class GridManager {
eventBus.on(EventTypes.PERIOD_CHANGE, (e: Event) => {
const detail = (e as CustomEvent).detail;
this.currentWeek = detail.week;
- this.renderHeaders();
+ this.render();
});
// Handle week changes from NavigationManager
@@ -72,12 +80,6 @@ export class GridManager {
this.render();
});
- // Handle new week container creation
- eventBus.on(EventTypes.WEEK_CONTAINER_CREATED, (e: Event) => {
- const detail = (e as CustomEvent).detail;
- this.renderGridForContainer(detail.container, detail.weekStart);
- });
-
// Handle grid clicks
this.setupGridInteractions();
}
@@ -86,129 +88,158 @@ export class GridManager {
* Render the complete grid structure
*/
render(): void {
- this.renderTimeAxis();
- this.renderHeaders();
+ if (!this.grid) return;
+
+ this.updateGridStyles();
this.renderGrid();
- this.renderGridLines();
// Emit grid rendered event
eventBus.emit(EventTypes.GRID_RENDERED);
}
/**
- * Render time axis (left side hours)
- */
- private renderTimeAxis(): void {
- if (!this.timeAxis) return;
-
- const startHour = calendarConfig.get('dayStartHour');
- const endHour = calendarConfig.get('dayEndHour');
-
- this.timeAxis.innerHTML = '';
-
- for (let hour = startHour; hour <= endHour; hour++) {
- const marker = document.createElement('swp-hour-marker');
- marker.textContent = this.formatHour(hour);
- (marker as any).dataset.hour = hour;
- this.timeAxis.appendChild(marker);
- }
- }
-
- /**
- * Render week headers
- */
- private renderHeaders(): void {
- if (!this.weekHeader || !this.currentWeek) return;
-
- const view = calendarConfig.get('view');
- const weekDays = calendarConfig.get('weekDays');
-
- this.weekHeader.innerHTML = '';
-
- if (view === 'week') {
- const dates = this.getWeekDates(this.currentWeek);
- const daysToShow = dates.slice(0, weekDays);
-
- daysToShow.forEach((date, index) => {
- const header = document.createElement('swp-day-header');
- header.innerHTML = `
- ${this.getDayName(date)}
- ${date.getDate()}
- `;
- (header as any).dataset.date = this.formatDate(date);
- (header as any).dataset.dayIndex = index;
-
- // Mark today
- if (this.isToday(date)) {
- (header as any).dataset.today = 'true';
- }
-
- this.weekHeader!.appendChild(header);
- });
- }
- }
-
- /**
- * Render the main grid structure
+ * Render the complete grid using POC structure
*/
private renderGrid(): void {
- if (!this.timeGrid) return;
-
- // Clear existing columns
- let dayColumns = this.timeGrid.querySelector('swp-day-columns');
- if (!dayColumns) {
- dayColumns = document.createElement('swp-day-columns');
- this.timeGrid.appendChild(dayColumns);
+ console.log('GridManager: renderGrid called', {
+ hasGrid: !!this.grid,
+ hasCurrentWeek: !!this.currentWeek,
+ currentWeek: this.currentWeek
+ });
+
+ if (!this.grid || !this.currentWeek) {
+ console.warn('GridManager: Cannot render - missing grid or currentWeek');
+ return;
}
+
+ // Clear existing grid and rebuild POC structure
+ this.grid.innerHTML = '';
- dayColumns.innerHTML = '';
+ // Create POC structure: time-axis + week-container
+ this.createTimeAxis();
+ this.createWeekContainer();
- const view = calendarConfig.get('view');
- const columnsCount = view === 'week' ? calendarConfig.get('weekDays') : 1;
+ console.log('GridManager: Grid rendered successfully with POC structure');
+ }
+
+ /**
+ * Create time axis (left column) like in POC
+ */
+ private createTimeAxis(): void {
+ if (!this.grid) return;
+
+ const timeAxis = document.createElement('swp-time-axis');
+ const startHour = calendarConfig.get('dayStartHour');
+ const endHour = calendarConfig.get('dayEndHour');
+
+ for (let hour = startHour; hour <= endHour; hour++) {
+ const marker = document.createElement('swp-hour-marker');
+ const period = hour >= 12 ? 'PM' : 'AM';
+ const displayHour = hour > 12 ? hour - 12 : (hour === 0 ? 12 : hour);
+ marker.textContent = `${displayHour} ${period}`;
+ timeAxis.appendChild(marker);
+ }
+
+ this.grid.appendChild(timeAxis);
+ }
+
+ /**
+ * Create week container with header and scrollable content like in POC
+ */
+ private createWeekContainer(): void {
+ if (!this.grid || !this.currentWeek) return;
+
+ const weekContainer = document.createElement('swp-week-container');
- // Create columns
- for (let i = 0; i < columnsCount; i++) {
- const column = document.createElement('swp-day-column');
- (column as any).dataset.columnIndex = i;
-
- if (this.currentWeek) {
- const dates = this.getWeekDates(this.currentWeek);
- if (dates[i]) {
- (column as any).dataset.date = this.formatDate(dates[i]);
- }
+ // Create week header
+ const weekHeader = document.createElement('swp-week-header');
+ this.renderWeekHeaders(weekHeader);
+ weekContainer.appendChild(weekHeader);
+
+ // Create scrollable content
+ const scrollableContent = document.createElement('swp-scrollable-content');
+ const timeGrid = document.createElement('swp-time-grid');
+
+ // Add grid lines
+ const gridLines = document.createElement('swp-grid-lines');
+ timeGrid.appendChild(gridLines);
+
+ // Create day columns
+ const dayColumns = document.createElement('swp-day-columns');
+ this.renderDayColumns(dayColumns);
+ timeGrid.appendChild(dayColumns);
+
+ scrollableContent.appendChild(timeGrid);
+ weekContainer.appendChild(scrollableContent);
+
+ this.grid.appendChild(weekContainer);
+ }
+
+ /**
+ * Render week headers like in POC
+ */
+ private renderWeekHeaders(weekHeader: HTMLElement): void {
+ if (!this.currentWeek) return;
+
+ const dates = this.getWeekDates(this.currentWeek);
+ const weekDays = calendarConfig.get('weekDays');
+ const daysToShow = dates.slice(0, weekDays);
+
+ daysToShow.forEach((date) => {
+ const header = document.createElement('swp-day-header');
+ if (this.isToday(date)) {
+ (header as any).dataset.today = 'true';
}
- // Add events container
+ header.innerHTML = `
+ ${this.getDayName(date)}
+ ${date.getDate()}
+ `;
+ (header as any).dataset.date = this.formatDate(date);
+
+ weekHeader.appendChild(header);
+ });
+ }
+
+ /**
+ * Render day columns like in POC
+ */
+ private renderDayColumns(dayColumns: HTMLElement): void {
+ console.log('GridManager: renderDayColumns called');
+ if (!this.currentWeek) {
+ console.log('GridManager: No currentWeek, returning');
+ return;
+ }
+
+ const dates = this.getWeekDates(this.currentWeek);
+ const weekDays = calendarConfig.get('weekDays');
+ const daysToShow = dates.slice(0, weekDays);
+
+ console.log('GridManager: About to render', daysToShow.length, 'day columns');
+
+ daysToShow.forEach((date, dayIndex) => {
+ const column = document.createElement('swp-day-column');
+ (column as any).dataset.date = this.formatDate(date);
+ (column as any).dataset.dayIndex = dayIndex.toString();
+
+ console.log(`GridManager: Creating day column ${dayIndex} for date ${this.formatDate(date)}`);
+
+ // Add dummy content to force column width (temporary test)
+ const dummyContent = document.createElement('div');
+ dummyContent.style.height = '20px';
+ dummyContent.style.width = '100%';
+ dummyContent.style.backgroundColor = 'red';
+ dummyContent.style.color = 'white';
+ dummyContent.style.fontSize = '12px';
+ dummyContent.style.textAlign = 'center';
+ dummyContent.textContent = `Day ${dayIndex + 1}`;
+ column.appendChild(dummyContent);
+
const eventsLayer = document.createElement('swp-events-layer');
column.appendChild(eventsLayer);
dayColumns.appendChild(column);
- }
-
- this.dayColumns = dayColumns as HTMLElement;
- this.updateGridStyles();
- }
-
- /**
- * Render grid lines
- */
- private renderGridLines(): void {
- if (!this.timeGrid) return;
-
- let gridLines = this.timeGrid.querySelector('swp-grid-lines');
- if (!gridLines) {
- gridLines = document.createElement('swp-grid-lines');
- this.timeGrid.insertBefore(gridLines, this.timeGrid.firstChild);
- }
-
- const totalHours = calendarConfig.totalHours;
- const hourHeight = calendarConfig.get('hourHeight');
-
- // Set CSS variables
- this.timeGrid.style.setProperty('--total-hours', totalHours.toString());
- this.timeGrid.style.setProperty('--hour-height', `${hourHeight}px`);
-
- // Grid lines are handled by CSS
+ });
}
/**
@@ -226,84 +257,93 @@ export class GridManager {
root.style.setProperty('--day-end-hour', config.dayEndHour.toString());
root.style.setProperty('--work-start-hour', config.workStartHour.toString());
root.style.setProperty('--work-end-hour', config.workEndHour.toString());
-
- // Set grid height
- const totalHeight = calendarConfig.totalHours * config.hourHeight;
- if (this.timeGrid) {
- this.timeGrid.style.height = `${totalHeight}px`;
- }
}
/**
- * Setup grid interaction handlers
+ * Setup grid interaction handlers for POC structure
*/
private setupGridInteractions(): void {
- if (!this.timeGrid) return;
+ if (!this.grid) return;
- // Click handler
- this.timeGrid.addEventListener('click', (e: MouseEvent) => {
+ // Click handler for day columns
+ this.grid.addEventListener('click', (e: MouseEvent) => {
// Ignore if clicking on an event
if ((e.target as Element).closest('swp-event')) return;
- const column = (e.target as Element).closest('swp-day-column') as HTMLElement;
- if (!column) return;
+ const dayColumn = (e.target as Element).closest('swp-day-column') as HTMLElement;
+ if (!dayColumn) return;
- const position = this.getClickPosition(e, column);
+ const position = this.getClickPosition(e, dayColumn);
eventBus.emit(EventTypes.GRID_CLICK, {
- date: (column as any).dataset.date,
+ date: (dayColumn as any).dataset.date,
time: position.time,
minutes: position.minutes,
- columnIndex: parseInt((column as any).dataset.columnIndex)
+ dayIndex: parseInt((dayColumn as any).dataset.dayIndex)
});
});
- // Double click handler
- this.timeGrid.addEventListener('dblclick', (e: MouseEvent) => {
+ // Double click handler for day columns
+ this.grid.addEventListener('dblclick', (e: MouseEvent) => {
// Ignore if clicking on an event
if ((e.target as Element).closest('swp-event')) return;
- const column = (e.target as Element).closest('swp-day-column') as HTMLElement;
- if (!column) return;
+ const dayColumn = (e.target as Element).closest('swp-day-column') as HTMLElement;
+ if (!dayColumn) return;
- const position = this.getClickPosition(e, column);
+ const position = this.getClickPosition(e, dayColumn);
eventBus.emit(EventTypes.GRID_DBLCLICK, {
- date: (column as any).dataset.date,
+ date: (dayColumn as any).dataset.date,
time: position.time,
minutes: position.minutes,
- columnIndex: parseInt((column as any).dataset.columnIndex)
+ dayIndex: parseInt((dayColumn as any).dataset.dayIndex)
});
});
}
/**
- * Get click position in grid
+ * Get click position in day column (POC structure)
*/
- private getClickPosition(event: MouseEvent, column: HTMLElement): GridPosition {
- const rect = column.getBoundingClientRect();
- const y = event.clientY - rect.top + (this.scrollableContent?.scrollTop || 0);
+ private getClickPosition(event: MouseEvent, dayColumn: HTMLElement): GridPosition {
+ const rect = dayColumn.getBoundingClientRect();
+ const y = event.clientY - rect.top;
- const minuteHeight = calendarConfig.minuteHeight;
+ const hourHeight = calendarConfig.get('hourHeight');
+ const minuteHeight = hourHeight / 60;
const snapInterval = calendarConfig.get('snapInterval');
const dayStartHour = calendarConfig.get('dayStartHour');
- // Calculate minutes from start of day
- let minutes = Math.floor(y / minuteHeight);
+ // Calculate total minutes from day start
+ let totalMinutes = Math.floor(y / minuteHeight);
// Snap to interval
- minutes = Math.round(minutes / snapInterval) * snapInterval;
+ totalMinutes = Math.round(totalMinutes / snapInterval) * snapInterval;
// Add day start offset
- const totalMinutes = (dayStartHour * 60) + minutes;
+ totalMinutes += dayStartHour * 60;
return {
minutes: totalMinutes,
time: this.minutesToTime(totalMinutes),
- y: minutes * minuteHeight
+ y: y
};
}
+ /**
+ * Scroll to specific hour
+ */
+ scrollToHour(hour: number): void {
+ if (!this.grid) return;
+
+ const hourHeight = calendarConfig.get('hourHeight');
+ const dayStartHour = calendarConfig.get('dayStartHour');
+ const headerHeight = 80; // Header row height
+ const scrollTop = headerHeight + ((hour - dayStartHour) * hourHeight);
+
+ this.grid.scrollTop = scrollTop;
+ }
+
/**
* Utility methods
*/
@@ -346,241 +386,4 @@ export class GridManager {
return `${displayHour}:${minutes.toString().padStart(2, '0')} ${period}`;
}
-
- /**
- * Scroll to specific hour
- */
- scrollToHour(hour: number): void {
- if (!this.scrollableContent) return;
-
- const hourHeight = calendarConfig.get('hourHeight');
- const dayStartHour = calendarConfig.get('dayStartHour');
- const scrollTop = (hour - dayStartHour) * hourHeight;
-
- this.scrollableContent.scrollTop = scrollTop;
- }
-
- /**
- * Render grid for a specific container (used during navigation transitions)
- */
- private renderGridForContainer(container: HTMLElement, weekStart: Date): void {
- // Find the week header and scrollable content within this container
- const weekHeader = container.querySelector('swp-week-header');
- const scrollableContent = container.querySelector('swp-scrollable-content');
- const timeGrid = container.querySelector('swp-time-grid');
-
- if (!weekHeader || !scrollableContent || !timeGrid) {
- console.warn('GridManager: Required elements not found in container');
- return;
- }
-
- // Render week header for this container
- this.renderWeekHeaderForContainer(weekHeader as HTMLElement, weekStart);
-
- // Render grid content for this container - pass weekStart
- this.renderGridForSpecificContainer(container, weekStart);
- this.renderGridLinesForContainer(timeGrid as HTMLElement);
- this.setupGridInteractionsForContainer(container);
-
- // Setup scroll sync for this new container
- this.setupScrollSyncForContainer(scrollableContent as HTMLElement);
- }
-
- /**
- * Render week header for a specific container
- */
- private renderWeekHeaderForContainer(weekHeader: HTMLElement, weekStart: Date): void {
- const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
-
- weekHeader.innerHTML = '';
-
- for (let i = 0; i < 7; i++) {
- const date = new Date(weekStart);
- date.setDate(date.getDate() + i);
-
- const header = document.createElement('swp-day-header');
- if (this.isToday(date)) {
- (header as any).dataset.today = 'true';
- }
-
- header.innerHTML = `
- ${days[date.getDay()]}
- ${date.getDate()}
- `;
- (header as any).dataset.date = this.formatDate(date);
-
- weekHeader.appendChild(header);
- }
- }
-
- /**
- * Render grid structure for a specific container
- */
- private renderGridForSpecificContainer(container: HTMLElement, weekStart?: Date): void {
- const timeGrid = container.querySelector('swp-time-grid');
- if (!timeGrid) {
- console.warn('GridManager: No time-grid found in container');
- return;
- }
-
- // Use the weekStart parameter or fall back to currentWeek
- const targetWeek = weekStart || this.currentWeek;
- if (!targetWeek) {
- console.warn('GridManager: No target week available');
- return;
- }
-
- // Clear existing columns
- let dayColumns = timeGrid.querySelector('swp-day-columns');
- if (!dayColumns) {
- dayColumns = document.createElement('swp-day-columns');
- timeGrid.appendChild(dayColumns);
- }
-
- dayColumns.innerHTML = '';
-
- const view = calendarConfig.get('view');
- const columnsCount = view === 'week' ? calendarConfig.get('weekDays') : 1;
-
- // Create columns using the target week
- for (let i = 0; i < columnsCount; i++) {
- const column = document.createElement('swp-day-column');
- (column as any).dataset.columnIndex = i;
-
- const dates = this.getWeekDates(targetWeek);
- if (dates[i]) {
- (column as any).dataset.date = this.formatDate(dates[i]);
- }
-
- // Add events container
- const eventsLayer = document.createElement('swp-events-layer');
- column.appendChild(eventsLayer);
-
- dayColumns.appendChild(column);
- }
-
- // Update grid styles for this container
- const totalHeight = calendarConfig.totalHours * calendarConfig.get('hourHeight');
- (timeGrid as HTMLElement).style.height = `${totalHeight}px`;
- }
-
- /**
- * Render grid lines for a specific time grid
- */
- private renderGridLinesForContainer(timeGrid: HTMLElement): void {
- let gridLines = timeGrid.querySelector('swp-grid-lines');
- if (!gridLines) {
- gridLines = document.createElement('swp-grid-lines');
- timeGrid.insertBefore(gridLines, timeGrid.firstChild);
- }
-
- const totalHours = calendarConfig.totalHours;
- const hourHeight = calendarConfig.get('hourHeight');
-
- // Set CSS variables
- timeGrid.style.setProperty('--total-hours', totalHours.toString());
- timeGrid.style.setProperty('--hour-height', `${hourHeight}px`);
- }
-
- /**
- * Setup grid interactions for a specific container
- */
- private setupGridInteractionsForContainer(container: HTMLElement): void {
- const timeGrid = container.querySelector('swp-time-grid');
- if (!timeGrid) return;
-
- // Click handler
- timeGrid.addEventListener('click', (e: Event) => {
- const mouseEvent = e as MouseEvent;
- // Ignore if clicking on an event
- if ((mouseEvent.target as Element).closest('swp-event')) return;
-
- const column = (mouseEvent.target as Element).closest('swp-day-column') as HTMLElement;
- if (!column) return;
-
- const position = this.getClickPositionForContainer(mouseEvent, column, container);
-
- eventBus.emit(EventTypes.GRID_CLICK, {
- date: (column as any).dataset.date,
- time: position.time,
- minutes: position.minutes,
- columnIndex: parseInt((column as any).dataset.columnIndex)
- });
- });
-
- // Double click handler
- timeGrid.addEventListener('dblclick', (e: Event) => {
- const mouseEvent = e as MouseEvent;
- // Ignore if clicking on an event
- if ((mouseEvent.target as Element).closest('swp-event')) return;
-
- const column = (mouseEvent.target as Element).closest('swp-day-column') as HTMLElement;
- if (!column) return;
-
- const position = this.getClickPositionForContainer(mouseEvent, column, container);
-
- eventBus.emit(EventTypes.GRID_DBLCLICK, {
- date: (column as any).dataset.date,
- time: position.time,
- minutes: position.minutes,
- columnIndex: parseInt((column as any).dataset.columnIndex)
- });
- });
- }
-
- /**
- * Get click position for a specific container
- */
- private getClickPositionForContainer(event: MouseEvent, column: HTMLElement, container: HTMLElement): GridPosition {
- const rect = column.getBoundingClientRect();
- const scrollableContent = container.querySelector('swp-scrollable-content') as HTMLElement;
- const y = event.clientY - rect.top + (scrollableContent?.scrollTop || 0);
-
- const minuteHeight = calendarConfig.minuteHeight;
- const snapInterval = calendarConfig.get('snapInterval');
- const dayStartHour = calendarConfig.get('dayStartHour');
-
- // Calculate minutes from start of day
- let minutes = Math.floor(y / minuteHeight);
-
- // Snap to interval
- minutes = Math.round(minutes / snapInterval) * snapInterval;
-
- // Add day start offset
- const totalMinutes = (dayStartHour * 60) + minutes;
-
- return {
- minutes: totalMinutes,
- time: this.minutesToTime(totalMinutes),
- y: minutes * minuteHeight
- };
- }
-
- /**
- * Setup scroll synchronization between time-axis and scrollable content
- */
- private setupScrollSync(): void {
- if (!this.scrollableContent || !this.timeAxis) return;
-
- // Sync time-axis scroll with scrollable content
- this.scrollableContent.addEventListener('scroll', () => {
- if (this.timeAxis) {
- this.timeAxis.scrollTop = this.scrollableContent!.scrollTop;
- }
- });
- }
-
- /**
- * Setup scroll synchronization for a specific container's scrollable content
- */
- private setupScrollSyncForContainer(scrollableContent: HTMLElement): void {
- if (!this.timeAxis) return;
-
- // Sync time-axis scroll with this container's scrollable content
- scrollableContent.addEventListener('scroll', () => {
- if (this.timeAxis) {
- this.timeAxis.scrollTop = scrollableContent.scrollTop;
- }
- });
- }
}
\ No newline at end of file
diff --git a/src/managers/NavigationManager.ts b/src/managers/NavigationManager.ts
index 8971e8f..44d66cb 100644
--- a/src/managers/NavigationManager.ts
+++ b/src/managers/NavigationManager.ts
@@ -4,7 +4,7 @@ import { EventTypes } from '../constants/EventTypes.js';
/**
* NavigationManager handles calendar navigation (prev/next/today buttons)
- * and week transitions with smooth animations
+ * with simplified CSS Grid approach
*/
export class NavigationManager {
private eventBus: IEventBus;
@@ -106,57 +106,22 @@ export class NavigationManager {
private animateTransition(direction: 'prev' | 'next', targetWeek: Date): void {
const calendarContainer = document.querySelector('swp-calendar-container');
- const currentWeekContainer = document.querySelector('swp-week-container');
- if (!calendarContainer || !currentWeekContainer) {
- console.warn('NavigationManager: Required DOM elements not found');
+ if (!calendarContainer) {
+ console.warn('NavigationManager: Calendar container not found');
return;
}
- // Create new week container (following POC structure)
- const newWeekContainer = document.createElement('swp-week-container');
- newWeekContainer.innerHTML = `
-
-
-
-
-
-
-
- `;
+ // Add transition class for visual feedback
+ calendarContainer.classList.add('week-transition');
- // Position new week off-screen
- newWeekContainer.style.position = 'absolute';
- newWeekContainer.style.top = '0';
- newWeekContainer.style.left = '0';
- newWeekContainer.style.width = '100%';
- newWeekContainer.style.height = '100%';
- newWeekContainer.style.transform = direction === 'next' ? 'translateX(100%)' : 'translateX(-100%)';
-
- // Add to calendar container
- calendarContainer.appendChild(newWeekContainer);
-
- // Notify other managers to render content for the new week
- this.eventBus.emit(EventTypes.WEEK_CONTAINER_CREATED, {
- container: newWeekContainer,
- weekStart: targetWeek
- });
-
- // Animate transition
- requestAnimationFrame(() => {
- // Slide out current week
- (currentWeekContainer as HTMLElement).style.transform = direction === 'next' ? 'translateX(-100%)' : 'translateX(100%)';
- (currentWeekContainer as HTMLElement).style.opacity = '0.5';
+ // Brief fade effect
+ setTimeout(() => {
+ calendarContainer.classList.add('week-transition-out');
- // Slide in new week
- newWeekContainer.style.transform = 'translateX(0)';
-
- // Clean up after animation
+ // Update the week after fade starts
setTimeout(() => {
- currentWeekContainer.remove();
- newWeekContainer.style.position = 'relative';
-
- // Update currentWeek only after animation is complete
+ // Update currentWeek
this.currentWeek = new Date(targetWeek);
this.animationQueue--;
@@ -172,8 +137,13 @@ export class NavigationManager {
weekEnd: DateUtils.addDays(this.currentWeek, 6)
});
- }, 400); // Match CSS transition duration
- });
+ // Remove transition classes
+ setTimeout(() => {
+ calendarContainer.classList.remove('week-transition', 'week-transition-out');
+ }, 150);
+
+ }, 150); // Half of transition duration
+ }, 50);
}
private updateWeekInfo(): void {
diff --git a/src/managers/ViewManager.ts b/src/managers/ViewManager.ts
index 298cd96..a121e51 100644
--- a/src/managers/ViewManager.ts
+++ b/src/managers/ViewManager.ts
@@ -49,9 +49,6 @@ export class ViewManager {
}
private initializeView(): void {
- this.renderTimeAxis();
- this.renderWeekHeaders();
- this.renderDayColumns();
this.updateViewButtons();
this.eventBus.emit(EventTypes.VIEW_RENDERED, {
@@ -75,65 +72,6 @@ export class ViewManager {
});
}
- private renderTimeAxis(): void {
- const timeAxis = document.querySelector('swp-time-axis');
- if (!timeAxis) return;
-
- const startHour = calendarConfig.get('dayStartHour');
- const endHour = calendarConfig.get('dayEndHour');
-
- timeAxis.innerHTML = '';
-
- for (let hour = startHour; hour <= endHour; hour++) {
- const marker = document.createElement('swp-hour-marker');
- const period = hour >= 12 ? 'PM' : 'AM';
- const displayHour = hour > 12 ? hour - 12 : (hour === 0 ? 12 : hour);
- marker.textContent = `${displayHour} ${period}`;
- timeAxis.appendChild(marker);
- }
- }
-
- private renderWeekHeaders(): void {
- const weekHeader = document.querySelector('swp-week-header');
- if (!weekHeader) return;
-
- const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
-
- weekHeader.innerHTML = '';
-
- for (let i = 0; i < 7; i++) {
- const header = document.createElement('swp-day-header');
- header.innerHTML = `
- ${days[i]}
- ${i + 1}
- `;
- header.dataset.dayIndex = i.toString();
-
- // Check if today (this will be updated by NavigationManager later)
- if (i === 1) { // Mock today as Monday for now
- header.setAttribute('data-today', 'true');
- }
-
- weekHeader.appendChild(header);
- }
- }
-
- private renderDayColumns(): void {
- const dayColumns = document.querySelector('swp-day-columns');
- if (!dayColumns) return;
-
- dayColumns.innerHTML = '';
-
- for (let i = 0; i < 7; i++) {
- const column = document.createElement('swp-day-column');
- column.dataset.dayIndex = i.toString();
-
- const eventsLayer = document.createElement('swp-events-layer');
- column.appendChild(eventsLayer);
-
- dayColumns.appendChild(column);
- }
- }
private updateViewButtons(): void {
const viewButtons = document.querySelectorAll('swp-view-button[data-view]');
@@ -148,9 +86,6 @@ export class ViewManager {
}
private refreshCurrentView(): void {
- this.renderWeekHeaders();
- this.renderDayColumns();
-
this.eventBus.emit(EventTypes.VIEW_RENDERED, {
view: this.currentView
});
diff --git a/wwwroot/css/calendar-base-css.css b/wwwroot/css/calendar-base-css.css
index b4db468..562ded2 100644
--- a/wwwroot/css/calendar-base-css.css
+++ b/wwwroot/css/calendar-base-css.css
@@ -86,17 +86,13 @@ body {
swp-calendar,
swp-calendar-nav,
swp-calendar-container,
-swp-time-axis,
-swp-week-header,
-swp-scrollable-content,
-swp-time-grid,
-swp-day-columns,
-swp-day-column,
-swp-events-layer,
+swp-calendar-grid,
+swp-header-cell,
+swp-time-cell,
+swp-day-cell,
+swp-events-container,
swp-event,
swp-loading-overlay,
-swp-week-container,
-swp-grid-lines,
swp-nav-group,
swp-nav-button,
swp-search-container,
@@ -107,10 +103,8 @@ swp-view-button,
swp-week-info,
swp-week-number,
swp-date-range,
-swp-day-header,
swp-day-name,
swp-day-date,
-swp-hour-marker,
swp-event-time,
swp-event-title,
swp-spinner {
diff --git a/wwwroot/css/calendar-layout-css.css b/wwwroot/css/calendar-layout-css.css
index 3d0a86a..77b4e7a 100644
--- a/wwwroot/css/calendar-layout-css.css
+++ b/wwwroot/css/calendar-layout-css.css
@@ -1,4 +1,4 @@
-/* styles/layout.css */
+/* styles/layout.css - POC Structure Implementation */
/* Main calendar container */
swp-calendar {
@@ -21,7 +21,7 @@ swp-calendar-nav {
box-shadow: var(--shadow-sm);
}
-/* Calendar container grid (following POC structure) */
+/* Calendar container grid - POC structure */
swp-calendar-container {
flex: 1;
display: grid;
@@ -31,19 +31,6 @@ swp-calendar-container {
position: relative;
}
-/* Time axis (fixed, left side) */
-swp-time-axis {
- grid-column: 1;
- grid-row: 1;
- background: var(--color-surface);
- border-right: 1px solid var(--color-border);
- position: sticky;
- left: 0;
- z-index: 4;
- padding-top: 80px; /* Match header height */
- overflow-y: hidden; /* Hide scrollbar but allow programmatic scrolling */
- overflow-x: hidden;
-}
/* Week container for sliding */
swp-week-container {
@@ -55,7 +42,40 @@ swp-week-container {
transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
-/* Week header (inside week container) */
+/* Time axis */
+swp-time-axis {
+ grid-column: 1;
+ grid-row: 1;
+ background: var(--color-surface);
+ border-right: 1px solid var(--color-border);
+ position: sticky;
+ left: 0;
+ z-index: 4;
+ padding-top: 80px; /* Match header height */
+}
+
+swp-hour-marker {
+ height: var(--hour-height);
+ padding: 0 8px 8px 8px;
+ font-size: 0.75rem;
+ color: var(--color-text-secondary);
+ display: flex;
+ align-items: flex-start;
+ position: relative;
+}
+
+swp-hour-marker::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 100%;
+ width: 100vw;
+ height: 1px;
+ background: var(--color-grid-line);
+ pointer-events: none;
+}
+
+/* Week header */
swp-week-header {
display: grid;
grid-template-columns: repeat(7, 1fr);
@@ -67,57 +87,6 @@ swp-week-header {
height: 80px; /* Fixed height */
}
-/* Scrollable content */
-swp-scrollable-content {
- overflow-y: auto;
- overflow-x: hidden;
- scroll-behavior: smooth;
- position: relative;
- flex: 1;
- min-height: 0; /* Important for flex children to shrink */
- max-height: calc(100vh - 80px - 80px); /* Subtract nav height and week-header height */
-}
-
-swp-week-container.slide-out-left {
- transform: translateX(-100%);
-}
-
-swp-week-container.slide-out-right {
- transform: translateX(100%);
-}
-
-swp-week-container.slide-in-left {
- transform: translateX(-100%);
-}
-
-swp-week-container.slide-in-right {
- transform: translateX(100%);
-}
-
-swp-hour-marker {
- height: var(--hour-height);
- padding: 8px;
- font-size: 0.75rem;
- color: var(--color-text-secondary);
- display: flex;
- align-items: flex-start;
- position: relative;
-
- /* Hour line extending into calendar */
- &::after {
- content: '';
- position: absolute;
- top: 0;
- left: 100%;
- width: 100vw;
- height: 1px;
- background: var(--color-grid-line);
- pointer-events: none;
- }
-}
-
-/* Day header styling (inside week-header) */
-
swp-day-header {
padding: 12px;
text-align: center;
@@ -158,24 +127,19 @@ swp-day-header[data-today="true"] swp-day-date {
margin: 4px auto 0;
}
-/* All-day events container */
-swp-allday-container {
- position: sticky;
- top: 0;
- background: var(--color-background);
- border-bottom: 1px solid var(--color-border);
- min-height: 0;
- z-index: 2;
-
- &:not(:empty) {
- padding: 8px 0;
- }
+/* Scrollable content */
+swp-scrollable-content {
+ overflow-y: auto;
+ overflow-x: hidden;
+ scroll-behavior: smooth;
+ position: relative;
+ display: grid;
}
/* Time grid */
swp-time-grid {
position: relative;
- height: calc(24 * var(--hour-height));
+ height: calc(12 * var(--hour-height));
}
swp-time-grid::before {
@@ -195,38 +159,13 @@ swp-grid-lines {
inset: 0;
pointer-events: none;
z-index: var(--z-grid);
- background-image:
- /* Hour lines (stronger) */
- repeating-linear-gradient(
- to bottom,
- transparent,
- transparent calc(var(--hour-height) - 1px),
- var(--color-grid-line) calc(var(--hour-height) - 1px),
- var(--color-grid-line) var(--hour-height)
- ),
- /* Quarter hour lines (lighter) */
- repeating-linear-gradient(
- to bottom,
- transparent,
- transparent calc(var(--hour-height) / 4 - 1px),
- var(--color-grid-line-light) calc(var(--hour-height) / 4 - 1px),
- var(--color-grid-line-light) calc(var(--hour-height) / 4)
- );
-}
-
-/* Ensure grid lines are visible during transitions */
-swp-week-container swp-grid-lines {
- opacity: 1;
- visibility: visible;
-}
-
-/* Grid lines should remain visible even during animations */
-swp-week-container.slide-out-left swp-grid-lines,
-swp-week-container.slide-out-right swp-grid-lines,
-swp-week-container.slide-in-left swp-grid-lines,
-swp-week-container.slide-in-right swp-grid-lines {
- opacity: 1;
- visibility: visible;
+ background-image: repeating-linear-gradient(
+ to bottom,
+ transparent,
+ transparent calc(var(--hour-height) / 4 - 1px),
+ var(--color-grid-line-light) calc(var(--hour-height) / 4 - 1px),
+ var(--color-grid-line-light) calc(var(--hour-height) / 4)
+ );
}
/* Day columns */
@@ -251,6 +190,10 @@ swp-events-layer {
inset: 0;
}
+swp-event {
+ pointer-events: auto;
+}
+
/* Current time indicator */
swp-current-time-indicator {
position: absolute;
@@ -287,7 +230,13 @@ swp-current-time-indicator {
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.3);
}
-
- /* Position based on current time */
- top: calc(var(--current-minutes) * var(--minute-height));
+}
+
+/* Week navigation animations - simplified */
+swp-calendar-container.week-transition {
+ transition: opacity 300ms ease;
+}
+
+swp-calendar-container.week-transition-out {
+ opacity: 0.5;
}
\ No newline at end of file
diff --git a/wwwroot/index.html b/wwwroot/index.html
index e467812..6918d5a 100644
--- a/wwwroot/index.html
+++ b/wwwroot/index.html
@@ -49,20 +49,9 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+