mirror of
https://github.com/bfloydd/streams.git
synced 2026-07-22 12:50:25 +00:00
months are also selected by calendar
This commit is contained in:
parent
8c0bd33713
commit
b0f2fd0ec4
2 changed files with 43 additions and 43 deletions
|
|
@ -15,7 +15,7 @@ export class CalendarRenderer extends Component {
|
|||
private dateNavigationService: DateNavigationService;
|
||||
private dateStateManager: DateStateManager;
|
||||
private currentMonthView: Date;
|
||||
private onDateSelected: (day: number) => Promise<void>;
|
||||
private onDateSelected: (day: number, monthView: Date) => Promise<void>;
|
||||
private onDropdownClose?: () => void;
|
||||
private eventRegistry: EventHandlerRegistry;
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ export class CalendarRenderer extends Component {
|
|||
contentIndicatorService: ContentIndicatorService,
|
||||
dateNavigationService: DateNavigationService,
|
||||
currentMonthView: Date,
|
||||
onDateSelected: (day: number) => Promise<void>,
|
||||
onDateSelected: (day: number, monthView: Date) => Promise<void>,
|
||||
onDropdownClose?: () => void
|
||||
) {
|
||||
super();
|
||||
|
|
@ -43,24 +43,24 @@ export class CalendarRenderer extends Component {
|
|||
*/
|
||||
async updateCalendarGrid(): Promise<void> {
|
||||
const endTiming = performanceMonitor.startTiming('calendar-grid-update');
|
||||
|
||||
|
||||
try {
|
||||
if (this.grid.children.length > 0) {
|
||||
await this.updateGridContent();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Use DocumentFragment for batch DOM operations
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
|
||||
const state = this.dateStateManager.getState();
|
||||
const currentDate = this.currentMonthView;
|
||||
const daysInMonth = this.dateNavigationService.getDaysInMonth(
|
||||
currentDate.getFullYear(),
|
||||
currentDate.getFullYear(),
|
||||
currentDate.getMonth()
|
||||
);
|
||||
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getDay();
|
||||
|
||||
|
||||
// Create day headers
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const dayHeader = document.createElement('div');
|
||||
|
|
@ -68,79 +68,79 @@ export class CalendarRenderer extends Component {
|
|||
dayHeader.textContent = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'][i];
|
||||
fragment.appendChild(dayHeader);
|
||||
}
|
||||
|
||||
|
||||
// Create empty day placeholders
|
||||
for (let i = 0; i < firstDayOfMonth; i++) {
|
||||
const emptyDay = document.createElement('div');
|
||||
emptyDay.className = 'streams-bar-day empty';
|
||||
fragment.appendChild(emptyDay);
|
||||
}
|
||||
|
||||
|
||||
// Batch create all day elements and prepare content indicators
|
||||
const dayElements: HTMLElement[] = [];
|
||||
const contentPromises: Promise<ContentIndicator>[] = [];
|
||||
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const dayEl = document.createElement('div');
|
||||
dayEl.className = 'streams-bar-day';
|
||||
dayEl.setAttribute('data-day', String(day));
|
||||
|
||||
|
||||
const dateContainer = document.createElement('div');
|
||||
dateContainer.className = 'streams-date-container';
|
||||
dateContainer.textContent = String(day);
|
||||
dayEl.appendChild(dateContainer);
|
||||
|
||||
|
||||
const dotContainer = document.createElement('div');
|
||||
dotContainer.className = 'streams-dot-container';
|
||||
dayEl.appendChild(dotContainer);
|
||||
|
||||
|
||||
dayElements.push(dayEl);
|
||||
fragment.appendChild(dayEl);
|
||||
|
||||
|
||||
// Prepare content indicator promise
|
||||
const dayDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
|
||||
contentPromises.push(this.contentIndicatorService.getContentIndicator(dayDate));
|
||||
}
|
||||
|
||||
|
||||
// Clear grid and append all elements at once
|
||||
this.grid.empty();
|
||||
this.grid.appendChild(fragment);
|
||||
|
||||
|
||||
// Process content indicators and apply styles in batch
|
||||
const contentIndicators = await Promise.all(contentPromises);
|
||||
|
||||
|
||||
// Batch apply styles and content
|
||||
this.applyDayStylesAndContent(dayElements, contentIndicators, currentDate, state);
|
||||
|
||||
|
||||
// Use event delegation for better performance
|
||||
this.setupCalendarEventDelegation();
|
||||
|
||||
|
||||
} finally {
|
||||
endTiming();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update existing calendar grid content
|
||||
*/
|
||||
async updateGridContent(): Promise<void> {
|
||||
const endTiming = performanceMonitor.startTiming('calendar-grid-content-update');
|
||||
|
||||
|
||||
try {
|
||||
const dayElements = Array.from(this.grid.querySelectorAll('.streams-bar-day:not(.empty)')) as HTMLElement[];
|
||||
const state = this.dateStateManager.getState();
|
||||
const currentDate = this.currentMonthView;
|
||||
|
||||
|
||||
// Batch prepare all content indicators
|
||||
const contentPromises = dayElements.map((dayEl, i) => {
|
||||
const day = i + 1;
|
||||
const dayDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
|
||||
return this.contentIndicatorService.getContentIndicator(dayDate);
|
||||
});
|
||||
|
||||
|
||||
// Wait for all content indicators to load
|
||||
const contentIndicators = await Promise.all(contentPromises);
|
||||
|
||||
|
||||
// Batch apply all updates
|
||||
this.applyDayStylesAndContent(dayElements, contentIndicators, currentDate, state);
|
||||
} finally {
|
||||
|
|
@ -166,9 +166,9 @@ export class CalendarRenderer extends Component {
|
|||
* Batch apply styles and content to day elements for optimal performance
|
||||
*/
|
||||
private applyDayStylesAndContent(
|
||||
dayElements: HTMLElement[],
|
||||
contentIndicators: ContentIndicator[],
|
||||
currentDate: Date,
|
||||
dayElements: HTMLElement[],
|
||||
contentIndicators: ContentIndicator[],
|
||||
currentDate: Date,
|
||||
state: DateState
|
||||
): void {
|
||||
// Use requestAnimationFrame to batch DOM updates
|
||||
|
|
@ -178,29 +178,29 @@ export class CalendarRenderer extends Component {
|
|||
const content = contentIndicators[i];
|
||||
const dayDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
|
||||
const dateString = this.dateNavigationService.formatDateString(dayDate);
|
||||
|
||||
|
||||
// Get dot container once
|
||||
const dotContainer = dayEl.querySelector('.streams-dot-container') as HTMLElement;
|
||||
|
||||
|
||||
// Clear existing dots
|
||||
if (dotContainer) {
|
||||
while (dotContainer.firstChild) {
|
||||
dotContainer.removeChild(dotContainer.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply classes efficiently
|
||||
const classList = dayEl.classList;
|
||||
classList.remove('viewed', 'today');
|
||||
|
||||
|
||||
if (dateString === state.currentViewedDate) {
|
||||
classList.add('viewed');
|
||||
}
|
||||
|
||||
|
||||
if (this.dateNavigationService.isToday(dayDate)) {
|
||||
classList.add('today');
|
||||
}
|
||||
|
||||
|
||||
// Add content dots if needed
|
||||
if (content.exists && dotContainer) {
|
||||
const dots = content.size === 'small' ? 1 : content.size === 'medium' ? 2 : 3;
|
||||
|
|
@ -214,7 +214,7 @@ export class CalendarRenderer extends Component {
|
|||
if (content.isEncrypted) {
|
||||
const encryptionIcon = document.createElement('div');
|
||||
encryptionIcon.className = 'streams-encryption-icon';
|
||||
|
||||
|
||||
// Set the appropriate icon based on lock status
|
||||
if (content.isLocked) {
|
||||
setIcon(encryptionIcon, 'lock');
|
||||
|
|
@ -223,7 +223,7 @@ export class CalendarRenderer extends Component {
|
|||
setIcon(encryptionIcon, 'unlock');
|
||||
encryptionIcon.setAttribute('title', 'Encrypted file (unlocked)');
|
||||
}
|
||||
|
||||
|
||||
dotContainer.appendChild(encryptionIcon);
|
||||
}
|
||||
}
|
||||
|
|
@ -238,25 +238,25 @@ export class CalendarRenderer extends Component {
|
|||
// Create single event handler for all day clicks
|
||||
const calendarClickHandler = (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
|
||||
|
||||
// Close dropdown if it's open when clicking anywhere in calendar
|
||||
if (this.onDropdownClose) {
|
||||
this.onDropdownClose();
|
||||
}
|
||||
|
||||
|
||||
const dayEl = target.closest('.streams-bar-day:not(.empty)') as HTMLElement;
|
||||
|
||||
|
||||
if (dayEl) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
||||
const day = parseInt(dayEl.getAttribute('data-day') || '0', 10);
|
||||
if (day > 0) {
|
||||
this.onDateSelected(day);
|
||||
this.onDateSelected(day, this.currentMonthView);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Register event listeners using EventHandlerRegistry for automatic cleanup
|
||||
this.eventRegistry.register(this.grid, 'click', calendarClickHandler);
|
||||
this.eventRegistry.register(this.grid, 'touchend', calendarClickHandler, { passive: true } as AddEventListenerOptions);
|
||||
|
|
|
|||
|
|
@ -308,8 +308,8 @@ export class ComponentUIBuilder {
|
|||
this.contentIndicatorService,
|
||||
this.dateNavigationService,
|
||||
currentMonthView,
|
||||
async (day: number) => {
|
||||
await this.dateNavigationService.selectDate(currentMonthView, day);
|
||||
async (day: number, monthView: Date) => {
|
||||
await this.dateNavigationService.selectDate(monthView, day);
|
||||
if (this.callbacks.isExpanded()) {
|
||||
this.callbacks.toggleExpanded();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue