Refactors grid and navigation rendering
Attempt 1
This commit is contained in:
parent
6026d28e6f
commit
32ee35eb02
10 changed files with 436 additions and 811 deletions
|
|
@ -24,7 +24,7 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
|
|||
// clearEvents() would remove events from all containers, breaking the animation
|
||||
// Events are now rendered directly into the new container without clearing
|
||||
|
||||
// Events should already be filtered by DataManager - no need to filter here
|
||||
// Events should already be filtered by EventManager - no need to filter here
|
||||
console.log('BaseEventRenderer: Rendering', events.length, 'pre-filtered events');
|
||||
|
||||
// Find columns in the specific container
|
||||
|
|
|
|||
148
src/renderers/EventRendererManager.ts
Normal file
148
src/renderers/EventRendererManager.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import { EventBus } from '../core/EventBus';
|
||||
import { IEventBus, CalendarEvent, RenderContext } from '../types/CalendarTypes';
|
||||
import { EventTypes } from '../constants/EventTypes';
|
||||
import { StateEvents } from '../types/CalendarState';
|
||||
import { calendarConfig } from '../core/CalendarConfig';
|
||||
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
||||
import { EventManager } from '../managers/EventManager';
|
||||
import { EventRendererStrategy } from './EventRenderer';
|
||||
|
||||
/**
|
||||
* EventRenderer - Render events i DOM med positionering using Strategy Pattern
|
||||
* Håndterer event positioning og overlap detection
|
||||
*/
|
||||
export class EventRenderer {
|
||||
private eventBus: IEventBus;
|
||||
private eventManager: EventManager;
|
||||
private strategy: EventRendererStrategy;
|
||||
|
||||
constructor(eventBus: IEventBus, eventManager: EventManager) {
|
||||
this.eventBus = eventBus;
|
||||
this.eventManager = eventManager;
|
||||
|
||||
// Cache strategy at initialization
|
||||
const calendarType = calendarConfig.getCalendarMode();
|
||||
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
|
||||
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render events in a specific container for a given period
|
||||
*/
|
||||
public renderEvents(context: RenderContext): void {
|
||||
console.log('EventRenderer: Rendering events for period', {
|
||||
startDate: context.startDate,
|
||||
endDate: context.endDate,
|
||||
container: context.container
|
||||
});
|
||||
|
||||
// Get events from EventManager for the period
|
||||
const events = this.eventManager.getEventsForPeriod(
|
||||
context.startDate,
|
||||
context.endDate
|
||||
);
|
||||
|
||||
console.log(`EventRenderer: Found ${events.length} events for period`);
|
||||
|
||||
if (events.length === 0) {
|
||||
console.log('EventRenderer: No events to render for this period');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use cached strategy to render events in the specific container
|
||||
this.strategy.renderEvents(events, context.container, calendarConfig);
|
||||
|
||||
console.log(`EventRenderer: Successfully rendered ${events.length} events`);
|
||||
}
|
||||
|
||||
private setupEventListeners(): void {
|
||||
// Event-driven rendering: React to grid and container events
|
||||
this.eventBus.on(EventTypes.GRID_RENDERED, (event: Event) => {
|
||||
console.log('EventRenderer: Received GRID_RENDERED event');
|
||||
this.handleGridRendered(event as CustomEvent);
|
||||
});
|
||||
|
||||
this.eventBus.on(EventTypes.CONTAINER_READY_FOR_EVENTS, (event: Event) => {
|
||||
console.log('EventRenderer: Received CONTAINER_READY_FOR_EVENTS event');
|
||||
this.handleContainerReady(event as CustomEvent);
|
||||
});
|
||||
|
||||
this.eventBus.on(EventTypes.VIEW_CHANGED, (event: Event) => {
|
||||
console.log('EventRenderer: Received VIEW_CHANGED event');
|
||||
this.handleViewChanged(event as CustomEvent);
|
||||
});
|
||||
|
||||
// Handle calendar type changes - update cached strategy
|
||||
this.eventBus.on(EventTypes.CALENDAR_TYPE_CHANGED, () => {
|
||||
const calendarType = calendarConfig.getCalendarMode();
|
||||
this.strategy = CalendarTypeFactory.getEventRenderer(calendarType);
|
||||
console.log(`EventRenderer: Updated strategy to ${calendarType}`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle GRID_RENDERED event - render events in the current grid
|
||||
*/
|
||||
private handleGridRendered(event: CustomEvent): void {
|
||||
const { container, startDate, endDate } = event.detail;
|
||||
|
||||
if (!container) {
|
||||
console.error('EventRenderer: No container in GRID_RENDERED event', event.detail);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use period from event or fallback to calculated period
|
||||
const periodStart = startDate;
|
||||
const periodEnd = endDate;
|
||||
|
||||
this.renderEvents({
|
||||
container: container,
|
||||
startDate: periodStart,
|
||||
endDate: periodEnd
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle CONTAINER_READY_FOR_EVENTS event - render events in pre-rendered container
|
||||
*/
|
||||
private handleContainerReady(event: CustomEvent): void {
|
||||
const { container, startDate, endDate } = event.detail;
|
||||
|
||||
if (!container || !startDate || !endDate) {
|
||||
console.error('EventRenderer: Invalid CONTAINER_READY_FOR_EVENTS event data', event.detail);
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderEvents({
|
||||
container: container,
|
||||
startDate: new Date(startDate),
|
||||
endDate: new Date(endDate)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle VIEW_CHANGED event - clear and re-render for new view
|
||||
*/
|
||||
private handleViewChanged(event: CustomEvent): void {
|
||||
// Clear all existing events since view structure may have changed
|
||||
this.clearEvents();
|
||||
|
||||
// New rendering will be triggered by subsequent GRID_RENDERED event
|
||||
console.log('EventRenderer: Cleared events for view change, waiting for GRID_RENDERED');
|
||||
}
|
||||
private clearEvents(container?: HTMLElement): void {
|
||||
console.log(`EventRenderer: Clearing events`, container ? 'in container' : 'globally');
|
||||
this.strategy.clearEvents(container);
|
||||
}
|
||||
|
||||
public refresh(container?: HTMLElement): void {
|
||||
// Clear events in specific container or globally
|
||||
this.clearEvents(container);
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.clearEvents();
|
||||
}
|
||||
}
|
||||
182
src/renderers/GridRenderer.ts
Normal file
182
src/renderers/GridRenderer.ts
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ResourceCalendarData } from '../types/CalendarTypes';
|
||||
import { CalendarTypeFactory } from '../factories/CalendarTypeFactory';
|
||||
import { HeaderRenderContext } from './HeaderRenderer';
|
||||
import { ColumnRenderContext } from './ColumnRenderer';
|
||||
|
||||
/**
|
||||
* GridRenderer - Handles DOM rendering for the calendar grid
|
||||
* Separated from GridManager to follow Single Responsibility Principle
|
||||
*/
|
||||
export class GridRenderer {
|
||||
private config: CalendarConfig;
|
||||
|
||||
constructor(config: CalendarConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the complete grid structure
|
||||
*/
|
||||
public renderGrid(
|
||||
grid: HTMLElement,
|
||||
currentWeek: Date,
|
||||
resourceData: ResourceCalendarData | null,
|
||||
allDayEvents: any[]
|
||||
): void {
|
||||
console.log('GridRenderer: renderGrid called', {
|
||||
hasGrid: !!grid,
|
||||
hasCurrentWeek: !!currentWeek,
|
||||
currentWeek: currentWeek
|
||||
});
|
||||
|
||||
if (!grid || !currentWeek) {
|
||||
console.warn('GridRenderer: Cannot render - missing grid or currentWeek');
|
||||
return;
|
||||
}
|
||||
|
||||
// Only clear and rebuild if grid is empty (first render)
|
||||
if (grid.children.length === 0) {
|
||||
console.log('GridRenderer: First render - creating grid structure');
|
||||
// Create POC structure: header-spacer + time-axis + grid-container
|
||||
this.createHeaderSpacer(grid);
|
||||
this.createTimeAxis(grid);
|
||||
this.createGridContainer(grid, currentWeek, resourceData, allDayEvents);
|
||||
} else {
|
||||
console.log('GridRenderer: Re-render - updating existing structure');
|
||||
// Just update the calendar header for all-day events
|
||||
this.updateCalendarHeader(grid, currentWeek, resourceData, allDayEvents);
|
||||
}
|
||||
|
||||
console.log('GridRenderer: Grid rendered successfully with POC structure');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create header spacer to align time axis with week content
|
||||
*/
|
||||
private createHeaderSpacer(grid: HTMLElement): void {
|
||||
const headerSpacer = document.createElement('swp-header-spacer');
|
||||
grid.appendChild(headerSpacer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create time axis (positioned beside grid container)
|
||||
*/
|
||||
private createTimeAxis(grid: HTMLElement): void {
|
||||
const timeAxis = document.createElement('swp-time-axis');
|
||||
const timeAxisContent = document.createElement('swp-time-axis-content');
|
||||
const gridSettings = this.config.getGridSettings();
|
||||
const startHour = gridSettings.dayStartHour;
|
||||
const endHour = gridSettings.dayEndHour;
|
||||
|
||||
console.log('GridRenderer: Creating time axis - startHour:', startHour, 'endHour:', endHour);
|
||||
|
||||
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}`;
|
||||
timeAxisContent.appendChild(marker);
|
||||
}
|
||||
|
||||
timeAxis.appendChild(timeAxisContent);
|
||||
grid.appendChild(timeAxis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create grid container with header and scrollable content
|
||||
*/
|
||||
private createGridContainer(
|
||||
grid: HTMLElement,
|
||||
currentWeek: Date,
|
||||
resourceData: ResourceCalendarData | null,
|
||||
allDayEvents: any[]
|
||||
): void {
|
||||
const gridContainer = document.createElement('swp-grid-container');
|
||||
|
||||
// Create calendar header using Strategy Pattern
|
||||
const calendarHeader = document.createElement('swp-calendar-header');
|
||||
this.renderCalendarHeader(calendarHeader, currentWeek, resourceData, allDayEvents);
|
||||
gridContainer.appendChild(calendarHeader);
|
||||
|
||||
// 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 column container using Strategy Pattern
|
||||
const columnContainer = document.createElement('swp-day-columns');
|
||||
this.renderColumnContainer(columnContainer, currentWeek, resourceData);
|
||||
timeGrid.appendChild(columnContainer);
|
||||
|
||||
scrollableContent.appendChild(timeGrid);
|
||||
gridContainer.appendChild(scrollableContent);
|
||||
|
||||
grid.appendChild(gridContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render calendar header using Strategy Pattern
|
||||
*/
|
||||
private renderCalendarHeader(
|
||||
calendarHeader: HTMLElement,
|
||||
currentWeek: Date,
|
||||
resourceData: ResourceCalendarData | null,
|
||||
allDayEvents: any[]
|
||||
): void {
|
||||
const calendarType = this.config.getCalendarMode();
|
||||
const headerRenderer = CalendarTypeFactory.getHeaderRenderer(calendarType);
|
||||
|
||||
const context: HeaderRenderContext = {
|
||||
currentWeek: currentWeek,
|
||||
config: this.config,
|
||||
allDayEvents: allDayEvents,
|
||||
resourceData: resourceData
|
||||
};
|
||||
|
||||
headerRenderer.render(calendarHeader, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render column container using Strategy Pattern
|
||||
*/
|
||||
private renderColumnContainer(
|
||||
columnContainer: HTMLElement,
|
||||
currentWeek: Date,
|
||||
resourceData: ResourceCalendarData | null
|
||||
): void {
|
||||
console.log('GridRenderer: renderColumnContainer called');
|
||||
const calendarType = this.config.getCalendarMode();
|
||||
const columnRenderer = CalendarTypeFactory.getColumnRenderer(calendarType);
|
||||
|
||||
const context: ColumnRenderContext = {
|
||||
currentWeek: currentWeek,
|
||||
config: this.config,
|
||||
resourceData: resourceData
|
||||
};
|
||||
|
||||
columnRenderer.render(columnContainer, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update only the calendar header without rebuilding entire grid
|
||||
*/
|
||||
private updateCalendarHeader(
|
||||
grid: HTMLElement,
|
||||
currentWeek: Date,
|
||||
resourceData: ResourceCalendarData | null,
|
||||
allDayEvents: any[]
|
||||
): void {
|
||||
const calendarHeader = grid.querySelector('swp-calendar-header');
|
||||
if (!calendarHeader) return;
|
||||
|
||||
// Clear existing content
|
||||
calendarHeader.innerHTML = '';
|
||||
|
||||
// Re-render headers using Strategy Pattern
|
||||
this.renderCalendarHeader(calendarHeader as HTMLElement, currentWeek, resourceData, allDayEvents);
|
||||
}
|
||||
}
|
||||
110
src/renderers/GridStyleManager.ts
Normal file
110
src/renderers/GridStyleManager.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ResourceCalendarData } from '../types/CalendarTypes';
|
||||
|
||||
/**
|
||||
* GridStyleManager - Manages CSS variables and styling for the grid
|
||||
* Separated from GridManager to follow Single Responsibility Principle
|
||||
*/
|
||||
export class GridStyleManager {
|
||||
private config: CalendarConfig;
|
||||
|
||||
constructor(config: CalendarConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all grid CSS variables
|
||||
*/
|
||||
public updateGridStyles(resourceData: ResourceCalendarData | null = null): void {
|
||||
const root = document.documentElement;
|
||||
const gridSettings = this.config.getGridSettings();
|
||||
const calendar = document.querySelector('swp-calendar') as HTMLElement;
|
||||
const calendarType = this.config.getCalendarMode();
|
||||
|
||||
// Set CSS variables for time and grid measurements
|
||||
this.setTimeVariables(root, gridSettings);
|
||||
|
||||
// Set column count based on calendar type
|
||||
const columnCount = this.calculateColumnCount(calendarType, resourceData);
|
||||
root.style.setProperty('--grid-columns', columnCount.toString());
|
||||
|
||||
// Set column width based on fitToWidth setting
|
||||
this.setColumnWidth(root, gridSettings);
|
||||
|
||||
// Set fitToWidth data attribute for CSS targeting
|
||||
if (calendar) {
|
||||
calendar.setAttribute('data-fit-to-width', gridSettings.fitToWidth.toString());
|
||||
}
|
||||
|
||||
console.log('GridStyleManager: Updated grid styles with', columnCount, 'columns for', calendarType, 'calendar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time-related CSS variables
|
||||
*/
|
||||
private setTimeVariables(root: HTMLElement, gridSettings: any): void {
|
||||
root.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`);
|
||||
root.style.setProperty('--minute-height', `${gridSettings.hourHeight / 60}px`);
|
||||
root.style.setProperty('--snap-interval', gridSettings.snapInterval.toString());
|
||||
root.style.setProperty('--day-start-hour', gridSettings.dayStartHour.toString());
|
||||
root.style.setProperty('--day-end-hour', gridSettings.dayEndHour.toString());
|
||||
root.style.setProperty('--work-start-hour', gridSettings.workStartHour.toString());
|
||||
root.style.setProperty('--work-end-hour', gridSettings.workEndHour.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate number of columns based on calendar type and view
|
||||
*/
|
||||
private calculateColumnCount(calendarType: string, resourceData: ResourceCalendarData | null): number {
|
||||
if (calendarType === 'resource' && resourceData) {
|
||||
return resourceData.resources.length;
|
||||
} else if (calendarType === 'date') {
|
||||
const dateSettings = this.config.getDateViewSettings();
|
||||
switch (dateSettings.period) {
|
||||
case 'day':
|
||||
return 1;
|
||||
case 'week':
|
||||
return dateSettings.weekDays;
|
||||
case 'month':
|
||||
return 7;
|
||||
default:
|
||||
return dateSettings.weekDays;
|
||||
}
|
||||
}
|
||||
|
||||
return 7; // Default
|
||||
}
|
||||
|
||||
/**
|
||||
* Set column width based on fitToWidth setting
|
||||
*/
|
||||
private setColumnWidth(root: HTMLElement, gridSettings: any): void {
|
||||
if (gridSettings.fitToWidth) {
|
||||
root.style.setProperty('--day-column-min-width', '50px'); // Small min-width allows columns to fit available space
|
||||
} else {
|
||||
root.style.setProperty('--day-column-min-width', '250px'); // Default min-width for horizontal scroll mode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update spacer heights based on all-day events
|
||||
*/
|
||||
public updateSpacerHeights(allDayEventCount: number = 1): void {
|
||||
const eventHeight = 26; // Height per all-day event in pixels
|
||||
const padding = 0; // Top/bottom padding
|
||||
const allDayHeight = allDayEventCount > 0 ? (allDayEventCount * eventHeight) + padding : 0;
|
||||
|
||||
// Set CSS variable for dynamic spacer height
|
||||
document.documentElement.style.setProperty('--all-day-row-height', `${allDayHeight}px`);
|
||||
|
||||
console.log('GridStyleManager: Updated --all-day-row-height to', `${allDayHeight}px`, 'for', allDayEventCount, 'events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current column count
|
||||
*/
|
||||
public getColumnCount(resourceData: ResourceCalendarData | null = null): number {
|
||||
const calendarType = this.config.getCalendarMode();
|
||||
return this.calculateColumnCount(calendarType, resourceData);
|
||||
}
|
||||
}
|
||||
119
src/renderers/NavigationRenderer.ts
Normal file
119
src/renderers/NavigationRenderer.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import { IEventBus } from '../types/CalendarTypes';
|
||||
import { EventTypes } from '../constants/EventTypes';
|
||||
import { DateUtils } from '../utils/DateUtils';
|
||||
|
||||
/**
|
||||
* NavigationRenderer - Handles DOM rendering for navigation containers
|
||||
* Separated from NavigationManager to follow Single Responsibility Principle
|
||||
*/
|
||||
export class NavigationRenderer {
|
||||
private eventBus: IEventBus;
|
||||
|
||||
constructor(eventBus: IEventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a complete container with content and events
|
||||
*/
|
||||
public renderContainer(parentContainer: HTMLElement, weekStart: Date): HTMLElement {
|
||||
console.log('NavigationRenderer: Rendering new container for week:', weekStart.toDateString());
|
||||
|
||||
// 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);
|
||||
|
||||
// Render week content (headers and columns)
|
||||
this.renderWeekContentInContainer(newGrid, weekStart);
|
||||
|
||||
// Emit event to trigger event rendering
|
||||
const weekEnd = DateUtils.addDays(weekStart, 6);
|
||||
this.eventBus.emit(EventTypes.CONTAINER_READY_FOR_EVENTS, {
|
||||
container: newGrid,
|
||||
startDate: weekStart,
|
||||
endDate: weekEnd
|
||||
});
|
||||
|
||||
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 = '';
|
||||
|
||||
// Render headers for target week
|
||||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const date = new Date(weekStart);
|
||||
date.setDate(date.getDate() + i);
|
||||
|
||||
const headerElement = document.createElement('swp-day-header');
|
||||
if (this.isToday(date)) {
|
||||
headerElement.dataset.today = 'true';
|
||||
}
|
||||
|
||||
headerElement.innerHTML = `
|
||||
<swp-day-name>${days[date.getDay()]}</swp-day-name>
|
||||
<swp-day-date>${date.getDate()}</swp-day-date>
|
||||
`;
|
||||
headerElement.dataset.date = this.formatDate(date);
|
||||
|
||||
header.appendChild(headerElement);
|
||||
}
|
||||
|
||||
// Render day columns for target week
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const column = document.createElement('swp-day-column');
|
||||
const date = new Date(weekStart);
|
||||
date.setDate(date.getDate() + i);
|
||||
column.dataset.date = this.formatDate(date);
|
||||
|
||||
const eventsLayer = document.createElement('swp-events-layer');
|
||||
column.appendChild(eventsLayer);
|
||||
|
||||
dayColumns.appendChild(column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to format date
|
||||
*/
|
||||
private formatDate(date: Date): string {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is today
|
||||
*/
|
||||
private isToday(date: Date): boolean {
|
||||
const today = new Date();
|
||||
return date.toDateString() === today.toDateString();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue