Refactors calendar architecture for month view
Prepares the calendar component for month view implementation by introducing a strategy pattern for view management, splitting configuration settings, and consolidating events into a core set. It also removes dead code and enforces type safety, improving overall code quality and maintainability. Addresses critical issues identified in the code review, laying the groundwork for efficient feature addition.
This commit is contained in:
parent
7d513600d8
commit
3ddc6352f2
17 changed files with 1347 additions and 428 deletions
|
|
@ -12,12 +12,26 @@ export class DateCalculator {
|
|||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a date is valid
|
||||
* @param date - Date to validate
|
||||
* @param methodName - Name of calling method for error messages
|
||||
* @throws Error if date is invalid
|
||||
*/
|
||||
private validateDate(date: Date, methodName: string): void {
|
||||
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
||||
throw new Error(`${methodName}: Invalid date provided - ${date}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7)
|
||||
* @param weekStart - Any date in the week
|
||||
* @returns Array of dates for the configured work days
|
||||
*/
|
||||
getWorkWeekDates(weekStart: Date): Date[] {
|
||||
this.validateDate(weekStart, 'getWorkWeekDates');
|
||||
|
||||
const dates: Date[] = [];
|
||||
const workWeekSettings = this.config.getWorkWeekSettings();
|
||||
|
||||
|
|
@ -42,6 +56,8 @@ export class DateCalculator {
|
|||
* @returns The Monday of the ISO week
|
||||
*/
|
||||
getISOWeekStart(date: Date): Date {
|
||||
this.validateDate(date, 'getISOWeekStart');
|
||||
|
||||
const monday = new Date(date);
|
||||
const currentDay = monday.getDay();
|
||||
const daysToSubtract = currentDay === 0 ? 6 : currentDay - 1;
|
||||
|
|
@ -57,6 +73,8 @@ export class DateCalculator {
|
|||
* @returns The end date of the ISO week (Sunday)
|
||||
*/
|
||||
getWeekEnd(date: Date): Date {
|
||||
this.validateDate(date, 'getWeekEnd');
|
||||
|
||||
const weekStart = this.getISOWeekStart(date);
|
||||
const weekEnd = new Date(weekStart);
|
||||
weekEnd.setDate(weekStart.getDate() + 6);
|
||||
|
|
|
|||
|
|
@ -1,218 +0,0 @@
|
|||
/**
|
||||
* PositionUtils - Utility functions for converting between pixels and minutes/hours in the calendar
|
||||
* This module provides essential conversion functions for positioning events and calculating dimensions
|
||||
*/
|
||||
|
||||
import { calendarConfig } from '../core/CalendarConfig.js';
|
||||
|
||||
export class PositionUtils {
|
||||
/**
|
||||
* Convert minutes to pixels based on the current time scale
|
||||
* @param {number} minutes - Number of minutes to convert
|
||||
* @returns {number} Pixel value
|
||||
*/
|
||||
static minutesToPixels(minutes) {
|
||||
return minutes * calendarConfig.minuteHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert pixels to minutes based on the current time scale
|
||||
* @param {number} pixels - Number of pixels to convert
|
||||
* @returns {number} Minutes value
|
||||
*/
|
||||
static pixelsToMinutes(pixels) {
|
||||
return pixels / calendarConfig.minuteHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a time string (HH:MM) to minutes from start of day
|
||||
* @param {string} timeString - Time in format "HH:MM"
|
||||
* @returns {number} Minutes from start of day
|
||||
*/
|
||||
static timeStringToMinutes(timeString) {
|
||||
const [hours, minutes] = timeString.split(':').map(Number);
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert minutes from start of day to time string (HH:MM)
|
||||
* @param {number} minutes - Minutes from start of day
|
||||
* @returns {string} Time in format "HH:MM"
|
||||
*/
|
||||
static minutesToTimeString(minutes) {
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
return `${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pixel position for a given time
|
||||
* @param {string|number} time - Time as string "HH:MM" or minutes from start of day
|
||||
* @returns {number} Pixel position from top of calendar
|
||||
*/
|
||||
static getPixelPositionForTime(time) {
|
||||
const startHour = calendarConfig.get('dayStartHour');
|
||||
|
||||
let minutes;
|
||||
if (typeof time === 'string') {
|
||||
minutes = this.timeStringToMinutes(time);
|
||||
} else {
|
||||
minutes = time;
|
||||
}
|
||||
|
||||
// Subtract start hour offset
|
||||
const adjustedMinutes = minutes - (startHour * 60);
|
||||
|
||||
return this.minutesToPixels(adjustedMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the time for a given pixel position
|
||||
* @param {number} pixelPosition - Pixel position from top of calendar
|
||||
* @returns {number} Minutes from start of day
|
||||
*/
|
||||
static getTimeForPixelPosition(pixelPosition) {
|
||||
const startHour = calendarConfig.get('dayStartHour');
|
||||
|
||||
const minutes = this.pixelsToMinutes(pixelPosition);
|
||||
|
||||
// Add start hour offset
|
||||
return minutes + (startHour * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate event height based on duration
|
||||
* @param {number} durationMinutes - Duration in minutes
|
||||
* @returns {number} Height in pixels
|
||||
*/
|
||||
static getEventHeight(durationMinutes) {
|
||||
return this.minutesToPixels(durationMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate event duration based on height
|
||||
* @param {number} heightPixels - Height in pixels
|
||||
* @returns {number} Duration in minutes
|
||||
*/
|
||||
static getEventDuration(heightPixels) {
|
||||
return this.pixelsToMinutes(heightPixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pixel position for a specific day column
|
||||
* @param {number} dayIndex - Day index (0 = Monday, 6 = Sunday)
|
||||
* @returns {number} Pixel position from left
|
||||
*/
|
||||
static getDayColumnPosition(dayIndex) {
|
||||
// These values should be calculated based on actual calendar layout
|
||||
const timeAxisWidth = 60; // Default time axis width
|
||||
const scrollableContent = document.querySelector('swp-scrollable-content');
|
||||
const dayColumnWidth = scrollableContent ?
|
||||
(scrollableContent.clientWidth) / calendarConfig.get('weekDays') :
|
||||
120; // Default day column width
|
||||
|
||||
return timeAxisWidth + (dayIndex * dayColumnWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the day index for a given pixel position
|
||||
* @param {number} pixelPosition - Pixel position from left
|
||||
* @returns {number} Day index (0-6) or -1 if outside day columns
|
||||
*/
|
||||
static getDayIndexForPosition(pixelPosition) {
|
||||
const timeAxisWidth = 60; // Default time axis width
|
||||
const scrollableContent = document.querySelector('swp-scrollable-content');
|
||||
const dayColumnWidth = scrollableContent ?
|
||||
(scrollableContent.clientWidth) / calendarConfig.get('weekDays') :
|
||||
120; // Default day column width
|
||||
|
||||
if (pixelPosition < timeAxisWidth) {
|
||||
return -1; // In time axis area
|
||||
}
|
||||
|
||||
const dayPosition = pixelPosition - timeAxisWidth;
|
||||
const dayIndex = Math.floor(dayPosition / dayColumnWidth);
|
||||
|
||||
return dayIndex >= 0 && dayIndex < calendarConfig.get('weekDays') ? dayIndex : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the bounds for an event element
|
||||
* @param {Object} eventData - Event data with startTime, endTime, and day
|
||||
* @returns {Object} Bounds object with top, left, width, height
|
||||
*/
|
||||
static getEventBounds(eventData) {
|
||||
const startMinutes = typeof eventData.startTime === 'string'
|
||||
? this.timeStringToMinutes(eventData.startTime)
|
||||
: eventData.startTime;
|
||||
|
||||
const endMinutes = typeof eventData.endTime === 'string'
|
||||
? this.timeStringToMinutes(eventData.endTime)
|
||||
: eventData.endTime;
|
||||
|
||||
const duration = endMinutes - startMinutes;
|
||||
|
||||
const scrollableContent = document.querySelector('swp-scrollable-content');
|
||||
const timeAxisWidth = 60; // Default time axis width
|
||||
const dayColumnWidth = scrollableContent ?
|
||||
(scrollableContent.clientWidth) / calendarConfig.get('weekDays') :
|
||||
120; // Default day column width
|
||||
|
||||
return {
|
||||
top: this.getPixelPositionForTime(startMinutes),
|
||||
left: this.getDayColumnPosition(eventData.day),
|
||||
width: dayColumnWidth,
|
||||
height: this.getEventHeight(duration)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a pixel position is within the visible time range
|
||||
* @param {number} pixelPosition - Pixel position from top
|
||||
* @returns {boolean} True if within visible range
|
||||
*/
|
||||
static isWithinVisibleTimeRange(pixelPosition) {
|
||||
const startHour = calendarConfig.get('dayStartHour');
|
||||
const endHour = calendarConfig.get('dayEndHour');
|
||||
|
||||
const minutes = this.getTimeForPixelPosition(pixelPosition);
|
||||
const hours = minutes / 60;
|
||||
|
||||
return hours >= startHour && hours <= endHour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp a pixel position to the visible time range
|
||||
* @param {number} pixelPosition - Pixel position from top
|
||||
* @returns {number} Clamped pixel position
|
||||
*/
|
||||
static clampToVisibleTimeRange(pixelPosition) {
|
||||
const startHour = calendarConfig.get('dayStartHour');
|
||||
const endHour = calendarConfig.get('dayEndHour');
|
||||
|
||||
const minPosition = this.getPixelPositionForTime(startHour * 60);
|
||||
const maxPosition = this.getPixelPositionForTime(endHour * 60);
|
||||
|
||||
return Math.max(minPosition, Math.min(maxPosition, pixelPosition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total height of the calendar content area
|
||||
* @returns {number} Total height in pixels
|
||||
*/
|
||||
static getTotalCalendarHeight() {
|
||||
return calendarConfig.get('hourHeight') * calendarConfig.totalHours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a pixel position to the nearest time interval
|
||||
* @param {number} pixelPosition - Pixel position to round
|
||||
* @param {number} intervalMinutes - Interval in minutes (default: 15)
|
||||
* @returns {number} Rounded pixel position
|
||||
*/
|
||||
static roundToTimeInterval(pixelPosition, intervalMinutes = 15) {
|
||||
const minutes = this.getTimeForPixelPosition(pixelPosition);
|
||||
const roundedMinutes = Math.round(minutes / intervalMinutes) * intervalMinutes;
|
||||
return this.getPixelPositionForTime(roundedMinutes);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue