Adds I-prefix to all interfaces
This commit is contained in:
parent
80aaab46f2
commit
8ec5f52872
44 changed files with 1731 additions and 1949 deletions
|
|
@ -10,7 +10,9 @@
|
|||
"Bash(rm:*)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npm test)",
|
||||
"Bash(cat:*)"
|
||||
"Bash(cat:*)",
|
||||
"Bash(npm run test:run:*)",
|
||||
"Bash(npx tsc)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
|
|
|||
179
src/configuration/CalendarConfig.ts
Normal file
179
src/configuration/CalendarConfig.ts
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import { ICalendarConfig } from './ICalendarConfig';
|
||||
import { IGridSettings } from './GridSettings';
|
||||
import { IDateViewSettings } from './DateViewSettings';
|
||||
import { ITimeFormatConfig } from './TimeFormatConfig';
|
||||
import { IWorkWeekSettings } from './WorkWeekSettings';
|
||||
|
||||
/**
|
||||
* All-day event layout constants
|
||||
*/
|
||||
export const ALL_DAY_CONSTANTS = {
|
||||
EVENT_HEIGHT: 22,
|
||||
EVENT_GAP: 2,
|
||||
CONTAINER_PADDING: 4,
|
||||
MAX_COLLAPSED_ROWS: 4,
|
||||
get SINGLE_ROW_HEIGHT() {
|
||||
return this.EVENT_HEIGHT + this.EVENT_GAP; // 28px
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Work week presets
|
||||
*/
|
||||
export const WORK_WEEK_PRESETS: { [key: string]: IWorkWeekSettings } = {
|
||||
'standard': {
|
||||
id: 'standard',
|
||||
workDays: [1, 2, 3, 4, 5],
|
||||
totalDays: 5,
|
||||
firstWorkDay: 1
|
||||
},
|
||||
'compressed': {
|
||||
id: 'compressed',
|
||||
workDays: [1, 2, 3, 4],
|
||||
totalDays: 4,
|
||||
firstWorkDay: 1
|
||||
},
|
||||
'midweek': {
|
||||
id: 'midweek',
|
||||
workDays: [3, 4, 5],
|
||||
totalDays: 3,
|
||||
firstWorkDay: 3
|
||||
},
|
||||
'weekend': {
|
||||
id: 'weekend',
|
||||
workDays: [6, 7],
|
||||
totalDays: 2,
|
||||
firstWorkDay: 6
|
||||
},
|
||||
'fullweek': {
|
||||
id: 'fullweek',
|
||||
workDays: [1, 2, 3, 4, 5, 6, 7],
|
||||
totalDays: 7,
|
||||
firstWorkDay: 1
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration - DTO container for all configuration
|
||||
* Pure data object loaded from JSON via ConfigManager
|
||||
*/
|
||||
export class Configuration {
|
||||
private static _instance: Configuration | null = null;
|
||||
|
||||
public config: ICalendarConfig;
|
||||
public gridSettings: IGridSettings;
|
||||
public dateViewSettings: IDateViewSettings;
|
||||
public timeFormatConfig: ITimeFormatConfig;
|
||||
public currentWorkWeek: string;
|
||||
public selectedDate: Date;
|
||||
|
||||
constructor(
|
||||
config: ICalendarConfig,
|
||||
gridSettings: IGridSettings,
|
||||
dateViewSettings: IDateViewSettings,
|
||||
timeFormatConfig: ITimeFormatConfig,
|
||||
currentWorkWeek: string,
|
||||
selectedDate: Date = new Date()
|
||||
) {
|
||||
this.config = config;
|
||||
this.gridSettings = gridSettings;
|
||||
this.dateViewSettings = dateViewSettings;
|
||||
this.timeFormatConfig = timeFormatConfig;
|
||||
this.currentWorkWeek = currentWorkWeek;
|
||||
this.selectedDate = selectedDate;
|
||||
|
||||
// Store as singleton instance for web components
|
||||
Configuration._instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Configuration instance
|
||||
* Used by web components that can't use dependency injection
|
||||
*/
|
||||
public static getInstance(): Configuration {
|
||||
if (!Configuration._instance) {
|
||||
throw new Error('Configuration has not been initialized. Call ConfigManager.load() first.');
|
||||
}
|
||||
return Configuration._instance;
|
||||
}
|
||||
|
||||
// Computed properties
|
||||
get minuteHeight(): number {
|
||||
return this.gridSettings.hourHeight / 60;
|
||||
}
|
||||
|
||||
get totalHours(): number {
|
||||
return this.gridSettings.dayEndHour - this.gridSettings.dayStartHour;
|
||||
}
|
||||
|
||||
get totalMinutes(): number {
|
||||
return this.totalHours * 60;
|
||||
}
|
||||
|
||||
get slotsPerHour(): number {
|
||||
return 60 / this.gridSettings.snapInterval;
|
||||
}
|
||||
|
||||
get totalSlots(): number {
|
||||
return this.totalHours * this.slotsPerHour;
|
||||
}
|
||||
|
||||
get slotHeight(): number {
|
||||
return this.gridSettings.hourHeight / this.slotsPerHour;
|
||||
}
|
||||
|
||||
// Backward compatibility getters
|
||||
getGridSettings(): IGridSettings {
|
||||
return this.gridSettings;
|
||||
}
|
||||
|
||||
getDateViewSettings(): IDateViewSettings {
|
||||
return this.dateViewSettings;
|
||||
}
|
||||
|
||||
getWorkWeekSettings(): IWorkWeekSettings {
|
||||
return WORK_WEEK_PRESETS[this.currentWorkWeek] || WORK_WEEK_PRESETS['standard'];
|
||||
}
|
||||
|
||||
getCurrentWorkWeek(): string {
|
||||
return this.currentWorkWeek;
|
||||
}
|
||||
|
||||
getTimezone(): string {
|
||||
return this.timeFormatConfig.timezone;
|
||||
}
|
||||
|
||||
getLocale(): string {
|
||||
return this.timeFormatConfig.locale;
|
||||
}
|
||||
|
||||
getTimeFormatSettings(): ITimeFormatConfig {
|
||||
return this.timeFormatConfig;
|
||||
}
|
||||
|
||||
is24HourFormat(): boolean {
|
||||
return this.timeFormatConfig.use24HourFormat;
|
||||
}
|
||||
|
||||
getDateFormat(): 'locale' | 'technical' {
|
||||
return this.timeFormatConfig.dateFormat;
|
||||
}
|
||||
|
||||
setWorkWeek(workWeekId: string): void {
|
||||
if (WORK_WEEK_PRESETS[workWeekId]) {
|
||||
this.currentWorkWeek = workWeekId;
|
||||
this.dateViewSettings.weekDays = WORK_WEEK_PRESETS[workWeekId].totalDays;
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedDate(date: Date): void {
|
||||
this.selectedDate = date;
|
||||
}
|
||||
|
||||
isValidSnapInterval(interval: number): boolean {
|
||||
return [5, 10, 15, 30, 60].includes(interval);
|
||||
}
|
||||
}
|
||||
|
||||
// Backward compatibility alias
|
||||
export { Configuration as CalendarConfig };
|
||||
55
src/configuration/ConfigManager.ts
Normal file
55
src/configuration/ConfigManager.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { Configuration } from './CalendarConfig';
|
||||
import { ICalendarConfig } from './ICalendarConfig';
|
||||
import { TimeFormatter } from '../utils/TimeFormatter';
|
||||
|
||||
/**
|
||||
* ConfigManager - Static configuration loader
|
||||
* Loads JSON and creates Configuration instance
|
||||
*/
|
||||
export class ConfigManager {
|
||||
/**
|
||||
* Load configuration from JSON and create Configuration instance
|
||||
*/
|
||||
static async load(): Promise<Configuration> {
|
||||
const response = await fetch('/wwwroot/data/calendar-config.json');
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load config: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Build main config
|
||||
const mainConfig: ICalendarConfig = {
|
||||
scrollbarWidth: data.scrollbar.width,
|
||||
scrollbarColor: data.scrollbar.color,
|
||||
scrollbarTrackColor: data.scrollbar.trackColor,
|
||||
scrollbarHoverColor: data.scrollbar.hoverColor,
|
||||
scrollbarBorderRadius: data.scrollbar.borderRadius,
|
||||
allowDrag: data.interaction.allowDrag,
|
||||
allowResize: data.interaction.allowResize,
|
||||
allowCreate: data.interaction.allowCreate,
|
||||
apiEndpoint: data.api.endpoint,
|
||||
dateFormat: data.api.dateFormat,
|
||||
timeFormat: data.api.timeFormat,
|
||||
enableSearch: data.features.enableSearch,
|
||||
enableTouch: data.features.enableTouch,
|
||||
defaultEventDuration: data.eventDefaults.defaultEventDuration,
|
||||
minEventDuration: data.gridSettings.snapInterval,
|
||||
maxEventDuration: data.eventDefaults.maxEventDuration
|
||||
};
|
||||
|
||||
// Create Configuration instance
|
||||
const config = new Configuration(
|
||||
mainConfig,
|
||||
data.gridSettings,
|
||||
data.dateViewSettings,
|
||||
data.timeFormatConfig,
|
||||
data.currentWorkWeek
|
||||
);
|
||||
|
||||
// Configure TimeFormatter
|
||||
TimeFormatter.configure(config.timeFormatConfig);
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
11
src/configuration/DateViewSettings.ts
Normal file
11
src/configuration/DateViewSettings.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { ViewPeriod } from '../types/CalendarTypes';
|
||||
|
||||
/**
|
||||
* View settings for date-based calendar mode
|
||||
*/
|
||||
export interface IDateViewSettings {
|
||||
period: ViewPeriod;
|
||||
weekDays: number;
|
||||
firstDayOfWeek: number;
|
||||
showAllDay: boolean;
|
||||
}
|
||||
16
src/configuration/GridSettings.ts
Normal file
16
src/configuration/GridSettings.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Grid display settings interface
|
||||
*/
|
||||
export interface IGridSettings {
|
||||
dayStartHour: number;
|
||||
dayEndHour: number;
|
||||
workStartHour: number;
|
||||
workEndHour: number;
|
||||
hourHeight: number;
|
||||
snapInterval: number;
|
||||
fitToWidth: boolean;
|
||||
scrollToHour: number | null;
|
||||
gridStartThresholdMinutes: number;
|
||||
showCurrentTime: boolean;
|
||||
showWorkHours: boolean;
|
||||
}
|
||||
30
src/configuration/ICalendarConfig.ts
Normal file
30
src/configuration/ICalendarConfig.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Main calendar configuration interface
|
||||
*/
|
||||
export interface ICalendarConfig {
|
||||
// Scrollbar styling
|
||||
scrollbarWidth: number;
|
||||
scrollbarColor: string;
|
||||
scrollbarTrackColor: string;
|
||||
scrollbarHoverColor: string;
|
||||
scrollbarBorderRadius: number;
|
||||
|
||||
// Interaction settings
|
||||
allowDrag: boolean;
|
||||
allowResize: boolean;
|
||||
allowCreate: boolean;
|
||||
|
||||
// API settings
|
||||
apiEndpoint: string;
|
||||
dateFormat: string;
|
||||
timeFormat: string;
|
||||
|
||||
// Feature flags
|
||||
enableSearch: boolean;
|
||||
enableTouch: boolean;
|
||||
|
||||
// Event defaults
|
||||
defaultEventDuration: number;
|
||||
minEventDuration: number;
|
||||
maxEventDuration: number;
|
||||
}
|
||||
10
src/configuration/TimeFormatConfig.ts
Normal file
10
src/configuration/TimeFormatConfig.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Time format configuration settings
|
||||
*/
|
||||
export interface ITimeFormatConfig {
|
||||
timezone: string;
|
||||
use24HourFormat: boolean;
|
||||
locale: string;
|
||||
dateFormat: 'locale' | 'technical';
|
||||
showSeconds: boolean;
|
||||
}
|
||||
9
src/configuration/WorkWeekSettings.ts
Normal file
9
src/configuration/WorkWeekSettings.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Work week configuration settings
|
||||
*/
|
||||
export interface IWorkWeekSettings {
|
||||
id: string;
|
||||
workDays: number[];
|
||||
totalDays: number;
|
||||
firstWorkDay: number;
|
||||
}
|
||||
|
|
@ -1,436 +0,0 @@
|
|||
// Calendar configuration management
|
||||
// Pure static configuration class - no dependencies, no events
|
||||
|
||||
import { ICalendarConfig, ViewPeriod } from '../types/CalendarTypes';
|
||||
import { TimeFormatter, TimeFormatSettings } from '../utils/TimeFormatter';
|
||||
|
||||
/**
|
||||
* All-day event layout constants
|
||||
*/
|
||||
export const ALL_DAY_CONSTANTS = {
|
||||
EVENT_HEIGHT: 22, // Height of single all-day event
|
||||
EVENT_GAP: 2, // Gap between stacked events
|
||||
CONTAINER_PADDING: 4, // Container padding (top + bottom)
|
||||
MAX_COLLAPSED_ROWS: 4, // Show 4 rows when collapsed (3 events + 1 indicator row)
|
||||
get SINGLE_ROW_HEIGHT() {
|
||||
return this.EVENT_HEIGHT + this.EVENT_GAP; // 28px
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Layout and timing settings for the calendar grid
|
||||
*/
|
||||
interface GridSettings {
|
||||
// Time boundaries
|
||||
dayStartHour: number;
|
||||
dayEndHour: number;
|
||||
workStartHour: number;
|
||||
workEndHour: number;
|
||||
|
||||
// Layout settings
|
||||
hourHeight: number;
|
||||
snapInterval: number;
|
||||
fitToWidth: boolean;
|
||||
scrollToHour: number | null;
|
||||
|
||||
// Event grouping settings
|
||||
gridStartThresholdMinutes: number; // ±N minutes for events to share grid columns
|
||||
|
||||
// Display options
|
||||
showCurrentTime: boolean;
|
||||
showWorkHours: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* View settings for date-based calendar mode
|
||||
*/
|
||||
interface DateViewSettings {
|
||||
period: ViewPeriod; // day/week/month
|
||||
weekDays: number; // Number of days to show in week view
|
||||
firstDayOfWeek: number; // 0=Sunday, 1=Monday
|
||||
showAllDay: boolean; // Show all-day event row
|
||||
}
|
||||
|
||||
/**
|
||||
* Work week configuration settings
|
||||
*/
|
||||
interface WorkWeekSettings {
|
||||
id: string;
|
||||
workDays: number[]; // ISO 8601: [1,2,3,4,5] for mon-fri (1=Mon, 7=Sun)
|
||||
totalDays: number; // 5
|
||||
firstWorkDay: number; // ISO: 1 = Monday, 7 = Sunday
|
||||
}
|
||||
|
||||
/**
|
||||
* Time format configuration settings
|
||||
*/
|
||||
interface TimeFormatConfig {
|
||||
timezone: string;
|
||||
use24HourFormat: boolean;
|
||||
locale: string;
|
||||
dateFormat: 'locale' | 'technical';
|
||||
showSeconds: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calendar configuration management - Pure static config
|
||||
*/
|
||||
export class CalendarConfig {
|
||||
private static config: ICalendarConfig = {
|
||||
// Scrollbar styling
|
||||
scrollbarWidth: 16, // Width of scrollbar in pixels
|
||||
scrollbarColor: '#666', // Scrollbar thumb color
|
||||
scrollbarTrackColor: '#f0f0f0', // Scrollbar track color
|
||||
scrollbarHoverColor: '#b53f7aff', // Scrollbar thumb hover color
|
||||
scrollbarBorderRadius: 6, // Border radius for scrollbar thumb
|
||||
|
||||
// Interaction settings
|
||||
allowDrag: true,
|
||||
allowResize: true,
|
||||
allowCreate: true,
|
||||
|
||||
// API settings
|
||||
apiEndpoint: '/api/events',
|
||||
dateFormat: 'YYYY-MM-DD',
|
||||
timeFormat: 'HH:mm',
|
||||
|
||||
// Feature flags
|
||||
enableSearch: true,
|
||||
enableTouch: true,
|
||||
|
||||
// Event defaults
|
||||
defaultEventDuration: 60, // Minutes
|
||||
minEventDuration: 15, // Will be same as snapInterval
|
||||
maxEventDuration: 480 // 8 hours
|
||||
};
|
||||
|
||||
private static selectedDate: Date | null = new Date();
|
||||
private static currentWorkWeek: string = 'standard';
|
||||
|
||||
// Grid display settings
|
||||
private static gridSettings: GridSettings = {
|
||||
hourHeight: 60,
|
||||
dayStartHour: 0,
|
||||
dayEndHour: 24,
|
||||
workStartHour: 8,
|
||||
workEndHour: 17,
|
||||
snapInterval: 15,
|
||||
gridStartThresholdMinutes: 30, // Events starting within ±15 min share grid columns
|
||||
showCurrentTime: true,
|
||||
showWorkHours: true,
|
||||
fitToWidth: false,
|
||||
scrollToHour: 8
|
||||
};
|
||||
|
||||
// Date view settings
|
||||
private static dateViewSettings: DateViewSettings = {
|
||||
period: 'week',
|
||||
weekDays: 7,
|
||||
firstDayOfWeek: 1,
|
||||
showAllDay: true
|
||||
};
|
||||
|
||||
// Time format settings - default to Denmark with technical format
|
||||
private static timeFormatConfig: TimeFormatConfig = {
|
||||
timezone: 'Europe/Copenhagen',
|
||||
use24HourFormat: true,
|
||||
locale: 'da-DK',
|
||||
dateFormat: 'technical',
|
||||
showSeconds: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize configuration - called once at startup
|
||||
*/
|
||||
static initialize(): void {
|
||||
// Set computed values
|
||||
CalendarConfig.config.minEventDuration = CalendarConfig.gridSettings.snapInterval;
|
||||
|
||||
// Initialize TimeFormatter with default settings
|
||||
TimeFormatter.configure(CalendarConfig.timeFormatConfig);
|
||||
|
||||
// Load from data attributes
|
||||
CalendarConfig.loadFromDOM();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load configuration from DOM data attributes
|
||||
*/
|
||||
private static loadFromDOM(): void {
|
||||
const calendar = document.querySelector('swp-calendar') as HTMLElement;
|
||||
if (!calendar) return;
|
||||
|
||||
// Read data attributes
|
||||
const attrs = calendar.dataset;
|
||||
|
||||
// Update date view settings
|
||||
if (attrs.view) CalendarConfig.dateViewSettings.period = attrs.view as ViewPeriod;
|
||||
if (attrs.weekDays) CalendarConfig.dateViewSettings.weekDays = parseInt(attrs.weekDays);
|
||||
|
||||
// Update grid settings
|
||||
if (attrs.snapInterval) CalendarConfig.gridSettings.snapInterval = parseInt(attrs.snapInterval);
|
||||
if (attrs.dayStartHour) CalendarConfig.gridSettings.dayStartHour = parseInt(attrs.dayStartHour);
|
||||
if (attrs.dayEndHour) CalendarConfig.gridSettings.dayEndHour = parseInt(attrs.dayEndHour);
|
||||
if (attrs.hourHeight) CalendarConfig.gridSettings.hourHeight = parseInt(attrs.hourHeight);
|
||||
if (attrs.fitToWidth !== undefined) CalendarConfig.gridSettings.fitToWidth = attrs.fitToWidth === 'true';
|
||||
|
||||
// Update computed values
|
||||
CalendarConfig.config.minEventDuration = CalendarConfig.gridSettings.snapInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a config value
|
||||
*/
|
||||
static get<K extends keyof ICalendarConfig>(key: K): ICalendarConfig[K] {
|
||||
return CalendarConfig.config[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a config value (no events - use ConfigManager for updates with events)
|
||||
*/
|
||||
static set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void {
|
||||
CalendarConfig.config[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple config values (no events - use ConfigManager for updates with events)
|
||||
*/
|
||||
static update(updates: Partial<ICalendarConfig>): void {
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
CalendarConfig.set(key as keyof ICalendarConfig, value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config
|
||||
*/
|
||||
static getAll(): ICalendarConfig {
|
||||
return { ...CalendarConfig.config };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate derived values
|
||||
*/
|
||||
|
||||
static get minuteHeight(): number {
|
||||
return CalendarConfig.gridSettings.hourHeight / 60;
|
||||
}
|
||||
|
||||
static get totalHours(): number {
|
||||
return CalendarConfig.gridSettings.dayEndHour - CalendarConfig.gridSettings.dayStartHour;
|
||||
}
|
||||
|
||||
static get totalMinutes(): number {
|
||||
return CalendarConfig.totalHours * 60;
|
||||
}
|
||||
|
||||
static get slotsPerHour(): number {
|
||||
return 60 / CalendarConfig.gridSettings.snapInterval;
|
||||
}
|
||||
|
||||
static get totalSlots(): number {
|
||||
return CalendarConfig.totalHours * CalendarConfig.slotsPerHour;
|
||||
}
|
||||
|
||||
static get slotHeight(): number {
|
||||
return CalendarConfig.gridSettings.hourHeight / CalendarConfig.slotsPerHour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate snap interval
|
||||
*/
|
||||
static isValidSnapInterval(interval: number): boolean {
|
||||
return [5, 10, 15, 30, 60].includes(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grid display settings
|
||||
*/
|
||||
static getGridSettings(): GridSettings {
|
||||
return { ...CalendarConfig.gridSettings };
|
||||
}
|
||||
|
||||
/**
|
||||
* Update grid display settings (no events - use ConfigManager for updates with events)
|
||||
*/
|
||||
static updateGridSettings(updates: Partial<GridSettings>): void {
|
||||
CalendarConfig.gridSettings = { ...CalendarConfig.gridSettings, ...updates };
|
||||
|
||||
// Update computed values
|
||||
if (updates.snapInterval) {
|
||||
CalendarConfig.config.minEventDuration = updates.snapInterval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date view settings
|
||||
*/
|
||||
static getDateViewSettings(): DateViewSettings {
|
||||
return { ...CalendarConfig.dateViewSettings };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected date
|
||||
*/
|
||||
static getSelectedDate(): Date | null {
|
||||
return CalendarConfig.selectedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selected date
|
||||
* Note: Does not emit events - caller is responsible for event emission
|
||||
*/
|
||||
static setSelectedDate(date: Date): void {
|
||||
CalendarConfig.selectedDate = date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get work week presets
|
||||
*/
|
||||
private static getWorkWeekPresets(): { [key: string]: WorkWeekSettings } {
|
||||
return {
|
||||
'standard': {
|
||||
id: 'standard',
|
||||
workDays: [1,2,3,4,5], // Monday-Friday (ISO)
|
||||
totalDays: 5,
|
||||
firstWorkDay: 1
|
||||
},
|
||||
'compressed': {
|
||||
id: 'compressed',
|
||||
workDays: [1,2,3,4], // Monday-Thursday (ISO)
|
||||
totalDays: 4,
|
||||
firstWorkDay: 1
|
||||
},
|
||||
'midweek': {
|
||||
id: 'midweek',
|
||||
workDays: [3,4,5], // Wednesday-Friday (ISO)
|
||||
totalDays: 3,
|
||||
firstWorkDay: 3
|
||||
},
|
||||
'weekend': {
|
||||
id: 'weekend',
|
||||
workDays: [6,7], // Saturday-Sunday (ISO)
|
||||
totalDays: 2,
|
||||
firstWorkDay: 6
|
||||
},
|
||||
'fullweek': {
|
||||
id: 'fullweek',
|
||||
workDays: [1,2,3,4,5,6,7], // Monday-Sunday (ISO)
|
||||
totalDays: 7,
|
||||
firstWorkDay: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current work week settings
|
||||
*/
|
||||
static getWorkWeekSettings(): WorkWeekSettings {
|
||||
const presets = CalendarConfig.getWorkWeekPresets();
|
||||
return presets[CalendarConfig.currentWorkWeek] || presets['standard'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set work week preset
|
||||
* Note: Does not emit events - caller is responsible for event emission
|
||||
*/
|
||||
static setWorkWeek(workWeekId: string): void {
|
||||
const presets = CalendarConfig.getWorkWeekPresets();
|
||||
if (presets[workWeekId]) {
|
||||
CalendarConfig.currentWorkWeek = workWeekId;
|
||||
|
||||
// Update dateViewSettings to match work week
|
||||
CalendarConfig.dateViewSettings.weekDays = presets[workWeekId].totalDays;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current work week ID
|
||||
*/
|
||||
static getCurrentWorkWeek(): string {
|
||||
return CalendarConfig.currentWorkWeek;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time format settings
|
||||
*/
|
||||
static getTimeFormatSettings(): TimeFormatConfig {
|
||||
return { ...CalendarConfig.timeFormatConfig };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured timezone
|
||||
*/
|
||||
static getTimezone(): string {
|
||||
return CalendarConfig.timeFormatConfig.timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured locale
|
||||
*/
|
||||
static getLocale(): string {
|
||||
return CalendarConfig.timeFormatConfig.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if using 24-hour format
|
||||
*/
|
||||
static is24HourFormat(): boolean {
|
||||
return CalendarConfig.timeFormatConfig.use24HourFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current date format
|
||||
*/
|
||||
static getDateFormat(): 'locale' | 'technical' {
|
||||
return CalendarConfig.timeFormatConfig.dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from JSON
|
||||
*/
|
||||
static loadFromJSON(json: string): void {
|
||||
try {
|
||||
const data = JSON.parse(json);
|
||||
if (data.gridSettings) CalendarConfig.updateGridSettings(data.gridSettings);
|
||||
if (data.dateViewSettings) CalendarConfig.dateViewSettings = { ...CalendarConfig.dateViewSettings, ...data.dateViewSettings };
|
||||
if (data.timeFormatConfig) {
|
||||
CalendarConfig.timeFormatConfig = { ...CalendarConfig.timeFormatConfig, ...data.timeFormatConfig };
|
||||
TimeFormatter.configure(CalendarConfig.timeFormatConfig);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load config from JSON:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Instance method wrappers for backward compatibility
|
||||
// These allow injected CalendarConfig to work with existing code
|
||||
// ========================================================================
|
||||
|
||||
get(key: keyof ICalendarConfig) { return CalendarConfig.get(key); }
|
||||
set(key: keyof ICalendarConfig, value: any) { return CalendarConfig.set(key, value); }
|
||||
update(updates: Partial<ICalendarConfig>) { return CalendarConfig.update(updates); }
|
||||
getAll() { return CalendarConfig.getAll(); }
|
||||
get minuteHeight() { return CalendarConfig.minuteHeight; }
|
||||
get totalHours() { return CalendarConfig.totalHours; }
|
||||
get totalMinutes() { return CalendarConfig.totalMinutes; }
|
||||
get slotsPerHour() { return CalendarConfig.slotsPerHour; }
|
||||
get totalSlots() { return CalendarConfig.totalSlots; }
|
||||
get slotHeight() { return CalendarConfig.slotHeight; }
|
||||
isValidSnapInterval(interval: number) { return CalendarConfig.isValidSnapInterval(interval); }
|
||||
getGridSettings() { return CalendarConfig.getGridSettings(); }
|
||||
updateGridSettings(updates: Partial<GridSettings>) { return CalendarConfig.updateGridSettings(updates); }
|
||||
getDateViewSettings() { return CalendarConfig.getDateViewSettings(); }
|
||||
getSelectedDate() { return CalendarConfig.getSelectedDate(); }
|
||||
setSelectedDate(date: Date) { return CalendarConfig.setSelectedDate(date); }
|
||||
getWorkWeekSettings() { return CalendarConfig.getWorkWeekSettings(); }
|
||||
setWorkWeek(workWeekId: string) { return CalendarConfig.setWorkWeek(workWeekId); }
|
||||
getCurrentWorkWeek() { return CalendarConfig.getCurrentWorkWeek(); }
|
||||
getTimeFormatSettings() { return CalendarConfig.getTimeFormatSettings(); }
|
||||
getTimezone() { return CalendarConfig.getTimezone(); }
|
||||
getLocale() { return CalendarConfig.getLocale(); }
|
||||
is24HourFormat() { return CalendarConfig.is24HourFormat(); }
|
||||
getDateFormat() { return CalendarConfig.getDateFormat(); }
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
// Core EventBus using pure DOM CustomEvents
|
||||
import { EventLogEntry, ListenerEntry, IEventBus } from '../types/CalendarTypes';
|
||||
import { IEventLogEntry, IListenerEntry, IEventBus } from '../types/CalendarTypes';
|
||||
|
||||
/**
|
||||
* Central event dispatcher for calendar using DOM CustomEvents
|
||||
* Provides logging and debugging capabilities
|
||||
*/
|
||||
export class EventBus implements IEventBus {
|
||||
private eventLog: EventLogEntry[] = [];
|
||||
private eventLog: IEventLogEntry[] = [];
|
||||
private debug: boolean = false;
|
||||
private listeners: Set<ListenerEntry> = new Set();
|
||||
private listeners: Set<IListenerEntry> = new Set();
|
||||
|
||||
// Log configuration for different categories
|
||||
private logConfig: { [key: string]: boolean } = {
|
||||
|
|
@ -161,7 +161,7 @@ export class EventBus implements IEventBus {
|
|||
/**
|
||||
* Get event history
|
||||
*/
|
||||
getEventLog(eventType?: string): EventLogEntry[] {
|
||||
getEventLog(eventType?: string): IEventLogEntry[] {
|
||||
if (eventType) {
|
||||
return this.eventLog.filter(e => e.type === eventType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { TimeFormatter } from '../utils/TimeFormatter';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { DateService } from '../utils/DateService';
|
||||
|
|
@ -9,12 +9,12 @@ import { DateService } from '../utils/DateService';
|
|||
*/
|
||||
export abstract class BaseSwpEventElement extends HTMLElement {
|
||||
protected dateService: DateService;
|
||||
protected config: CalendarConfig;
|
||||
protected config: Configuration;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// TODO: Find better solution for web component DI
|
||||
this.config = new CalendarConfig();
|
||||
// Get singleton instance for web components (can't use DI)
|
||||
this.config = Configuration.getInstance();
|
||||
this.dateService = new DateService(this.config);
|
||||
}
|
||||
|
||||
|
|
@ -256,11 +256,11 @@ export class SwpEventElement extends BaseSwpEventElement {
|
|||
// ============================================
|
||||
|
||||
/**
|
||||
* Create SwpEventElement from CalendarEvent
|
||||
* Create SwpEventElement from ICalendarEvent
|
||||
*/
|
||||
public static fromCalendarEvent(event: CalendarEvent): SwpEventElement {
|
||||
public static fromCalendarEvent(event: ICalendarEvent): SwpEventElement {
|
||||
const element = document.createElement('swp-event') as SwpEventElement;
|
||||
const config = new CalendarConfig();
|
||||
const config = Configuration.getInstance();
|
||||
const dateService = new DateService(config);
|
||||
|
||||
element.dataset.eventId = event.id;
|
||||
|
|
@ -274,9 +274,9 @@ export class SwpEventElement extends BaseSwpEventElement {
|
|||
}
|
||||
|
||||
/**
|
||||
* Extract CalendarEvent from DOM element
|
||||
* Extract ICalendarEvent from DOM element
|
||||
*/
|
||||
public static extractCalendarEventFromElement(element: HTMLElement): CalendarEvent {
|
||||
public static extractCalendarEventFromElement(element: HTMLElement): ICalendarEvent {
|
||||
return {
|
||||
id: element.dataset.eventId || '',
|
||||
title: element.dataset.title || '',
|
||||
|
|
@ -331,11 +331,11 @@ export class SwpAllDayEventElement extends BaseSwpEventElement {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create from CalendarEvent
|
||||
* Create from ICalendarEvent
|
||||
*/
|
||||
public static fromCalendarEvent(event: CalendarEvent): SwpAllDayEventElement {
|
||||
public static fromCalendarEvent(event: ICalendarEvent): SwpAllDayEventElement {
|
||||
const element = document.createElement('swp-allday-event') as SwpAllDayEventElement;
|
||||
const config = new CalendarConfig();
|
||||
const config = Configuration.getInstance();
|
||||
const dateService = new DateService(config);
|
||||
|
||||
element.dataset.eventId = event.id;
|
||||
|
|
|
|||
25
src/index.ts
25
src/index.ts
|
|
@ -1,7 +1,8 @@
|
|||
// Main entry point for Calendar Plantempus
|
||||
import { Container } from '@novadi/core';
|
||||
import { eventBus } from './core/EventBus';
|
||||
import { CalendarConfig } from './core/CalendarConfig';
|
||||
import { ConfigManager } from './configuration/ConfigManager';
|
||||
import { Configuration } from './configuration/CalendarConfig';
|
||||
import { URLManager } from './utils/URLManager';
|
||||
import { IEventBus } from './types/CalendarTypes';
|
||||
|
||||
|
|
@ -19,7 +20,6 @@ import { ResizeHandleManager } from './managers/ResizeHandleManager';
|
|||
import { EdgeScrollManager } from './managers/EdgeScrollManager';
|
||||
import { DragHoverManager } from './managers/DragHoverManager';
|
||||
import { HeaderManager } from './managers/HeaderManager';
|
||||
import { ConfigManager } from './managers/ConfigManager';
|
||||
|
||||
// Import repositories
|
||||
import { IEventRepository } from './repositories/IEventRepository';
|
||||
|
|
@ -27,7 +27,7 @@ import { MockEventRepository } from './repositories/MockEventRepository';
|
|||
|
||||
// Import renderers
|
||||
import { DateHeaderRenderer, type IHeaderRenderer } from './renderers/DateHeaderRenderer';
|
||||
import { DateColumnRenderer, type ColumnRenderer } from './renderers/ColumnRenderer';
|
||||
import { DateColumnRenderer, type IColumnRenderer } from './renderers/ColumnRenderer';
|
||||
import { DateEventRenderer, type IEventRenderer } from './renderers/EventRenderer';
|
||||
import { AllDayEventRenderer } from './renderers/AllDayEventRenderer';
|
||||
import { GridRenderer } from './renderers/GridRenderer';
|
||||
|
|
@ -70,8 +70,8 @@ async function handleDeepLinking(eventManager: EventManager, urlManager: URLMana
|
|||
*/
|
||||
async function initializeCalendar(): Promise<void> {
|
||||
try {
|
||||
// Initialize static calendar configuration
|
||||
CalendarConfig.initialize();
|
||||
// Load configuration from JSON
|
||||
const config = await ConfigManager.load();
|
||||
|
||||
// Create NovaDI container
|
||||
const container = new Container();
|
||||
|
|
@ -80,21 +80,18 @@ async function initializeCalendar(): Promise<void> {
|
|||
// Enable debug mode for development
|
||||
eventBus.setDebug(true);
|
||||
|
||||
// Register CalendarConfig as singleton instance (static class, not instantiated)
|
||||
builder.registerInstance(CalendarConfig).as<CalendarConfig>();
|
||||
|
||||
// Register ConfigManager for event-driven config updates
|
||||
builder.registerType(ConfigManager).as<ConfigManager>();
|
||||
|
||||
// Bind core services as instances
|
||||
builder.registerInstance(eventBus).as<IEventBus>();
|
||||
|
||||
// Register configuration instance
|
||||
builder.registerInstance(config).as<Configuration>();
|
||||
|
||||
// Register repositories
|
||||
builder.registerType(MockEventRepository).as<IEventRepository>();
|
||||
|
||||
// Register renderers
|
||||
builder.registerType(DateHeaderRenderer).as<IHeaderRenderer>();
|
||||
builder.registerType(DateColumnRenderer).as<ColumnRenderer>();
|
||||
builder.registerType(DateColumnRenderer).as<IColumnRenderer>();
|
||||
builder.registerType(DateEventRenderer).as<IEventRenderer>();
|
||||
|
||||
// Register core services and utilities
|
||||
|
|
@ -130,7 +127,6 @@ async function initializeCalendar(): Promise<void> {
|
|||
|
||||
// Get managers from container
|
||||
const eb = app.resolveType<IEventBus>();
|
||||
const configManager = app.resolveType<ConfigManager>();
|
||||
const calendarManager = app.resolveType<CalendarManager>();
|
||||
const eventManager = app.resolveType<EventManager>();
|
||||
const resizeHandleManager = app.resolveType<ResizeHandleManager>();
|
||||
|
|
@ -143,9 +139,6 @@ async function initializeCalendar(): Promise<void> {
|
|||
const allDayManager = app.resolveType<AllDayManager>();
|
||||
const urlManager = app.resolveType<URLManager>();
|
||||
|
||||
// Initialize CSS variables before any rendering
|
||||
configManager.initialize();
|
||||
|
||||
// Initialize managers
|
||||
await calendarManager.initialize?.();
|
||||
await resizeHandleManager.initialize?.();
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
// All-day row height management and animations
|
||||
|
||||
import { eventBus } from '../core/EventBus';
|
||||
import { ALL_DAY_CONSTANTS } from '../core/CalendarConfig';
|
||||
import { ALL_DAY_CONSTANTS } from '../configuration/CalendarConfig';
|
||||
import { AllDayEventRenderer } from '../renderers/AllDayEventRenderer';
|
||||
import { AllDayLayoutEngine, EventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { ColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { AllDayLayoutEngine, IEventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { SwpAllDayEventElement } from '../elements/SwpEventElement';
|
||||
import {
|
||||
DragMouseEnterHeaderEventPayload,
|
||||
DragStartEventPayload,
|
||||
DragMoveEventPayload,
|
||||
DragEndEventPayload,
|
||||
DragColumnChangeEventPayload,
|
||||
HeaderReadyEventPayload
|
||||
IDragMouseEnterHeaderEventPayload,
|
||||
IDragStartEventPayload,
|
||||
IDragMoveEventPayload,
|
||||
IDragEndEventPayload,
|
||||
IDragColumnChangeEventPayload,
|
||||
IHeaderReadyEventPayload
|
||||
} from '../types/EventTypes';
|
||||
import { DragOffset, MousePosition } from '../types/DragDropTypes';
|
||||
import { IDragOffset, IMousePosition } from '../types/DragDropTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { EventManager } from './EventManager';
|
||||
import { differenceInCalendarDays } from 'date-fns';
|
||||
|
|
@ -33,10 +33,10 @@ export class AllDayManager {
|
|||
private layoutEngine: AllDayLayoutEngine | null = null;
|
||||
|
||||
// State tracking for differential updates
|
||||
private currentLayouts: EventLayout[] = [];
|
||||
private currentAllDayEvents: CalendarEvent[] = [];
|
||||
private currentWeekDates: ColumnBounds[] = [];
|
||||
private newLayouts: EventLayout[] = [];
|
||||
private currentLayouts: IEventLayout[] = [];
|
||||
private currentAllDayEvents: ICalendarEvent[] = [];
|
||||
private currentWeekDates: IColumnBounds[] = [];
|
||||
private newLayouts: IEventLayout[] = [];
|
||||
|
||||
// Expand/collapse state
|
||||
private isExpanded: boolean = false;
|
||||
|
|
@ -62,7 +62,7 @@ export class AllDayManager {
|
|||
*/
|
||||
private setupEventListeners(): void {
|
||||
eventBus.on('drag:mouseenter-header', (event) => {
|
||||
const payload = (event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
|
||||
const payload = (event as CustomEvent<IDragMouseEnterHeaderEventPayload>).detail;
|
||||
|
||||
if (payload.draggedClone.hasAttribute('data-allday'))
|
||||
return;
|
||||
|
|
@ -87,7 +87,7 @@ export class AllDayManager {
|
|||
|
||||
// Listen for drag operations on all-day events
|
||||
eventBus.on('drag:start', (event) => {
|
||||
let payload: DragStartEventPayload = (event as CustomEvent<DragStartEventPayload>).detail;
|
||||
let payload: IDragStartEventPayload = (event as CustomEvent<IDragStartEventPayload>).detail;
|
||||
|
||||
if (!payload.draggedClone?.hasAttribute('data-allday')) {
|
||||
return;
|
||||
|
|
@ -97,7 +97,7 @@ export class AllDayManager {
|
|||
});
|
||||
|
||||
eventBus.on('drag:column-change', (event) => {
|
||||
let payload: DragColumnChangeEventPayload = (event as CustomEvent<DragColumnChangeEventPayload>).detail;
|
||||
let payload: IDragColumnChangeEventPayload = (event as CustomEvent<IDragColumnChangeEventPayload>).detail;
|
||||
|
||||
if (!payload.draggedClone?.hasAttribute('data-allday')) {
|
||||
return;
|
||||
|
|
@ -107,7 +107,7 @@ export class AllDayManager {
|
|||
});
|
||||
|
||||
eventBus.on('drag:end', (event) => {
|
||||
let draggedElement: DragEndEventPayload = (event as CustomEvent<DragEndEventPayload>).detail;
|
||||
let draggedElement: IDragEndEventPayload = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||
|
||||
if (draggedElement.target != 'swp-day-header') // we are not inside the swp-day-header, so just ignore.
|
||||
return;
|
||||
|
|
@ -128,12 +128,12 @@ export class AllDayManager {
|
|||
|
||||
// Listen for header ready - when dates are populated with period data
|
||||
eventBus.on('header:ready', (event: Event) => {
|
||||
let headerReadyEventPayload = (event as CustomEvent<HeaderReadyEventPayload>).detail;
|
||||
let headerReadyEventPayload = (event as CustomEvent<IHeaderReadyEventPayload>).detail;
|
||||
|
||||
let startDate = new Date(headerReadyEventPayload.headerElements.at(0)!.date);
|
||||
let endDate = new Date(headerReadyEventPayload.headerElements.at(-1)!.date);
|
||||
|
||||
let events: CalendarEvent[] = this.eventManager.getEventsForPeriod(startDate, endDate);
|
||||
let events: ICalendarEvent[] = this.eventManager.getEventsForPeriod(startDate, endDate);
|
||||
// Filter for all-day events
|
||||
const allDayEvents = events.filter(event => event.allDay);
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ export class AllDayManager {
|
|||
* Calculate layout for ALL all-day events using AllDayLayoutEngine
|
||||
* This is the correct method that processes all events together for proper overlap detection
|
||||
*/
|
||||
private calculateAllDayEventsLayout(events: CalendarEvent[], dayHeaders: ColumnBounds[]): EventLayout[] {
|
||||
private calculateAllDayEventsLayout(events: ICalendarEvent[], dayHeaders: IColumnBounds[]): IEventLayout[] {
|
||||
|
||||
// Store current state
|
||||
this.currentAllDayEvents = events;
|
||||
|
|
@ -316,12 +316,12 @@ export class AllDayManager {
|
|||
|
||||
}
|
||||
|
||||
private handleConvertToAllDay(payload: DragMouseEnterHeaderEventPayload): void {
|
||||
private handleConvertToAllDay(payload: IDragMouseEnterHeaderEventPayload): void {
|
||||
|
||||
let allDayContainer = this.getAllDayContainer();
|
||||
if (!allDayContainer) return;
|
||||
|
||||
// Create SwpAllDayEventElement from CalendarEvent
|
||||
// Create SwpAllDayEventElement from ICalendarEvent
|
||||
const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent);
|
||||
|
||||
// Apply grid positioning
|
||||
|
|
@ -345,7 +345,7 @@ export class AllDayManager {
|
|||
/**
|
||||
* Handle drag move for all-day events - SPECIALIZED FOR ALL-DAY CONTAINER
|
||||
*/
|
||||
private handleColumnChange(dragColumnChangeEventPayload: DragColumnChangeEventPayload): void {
|
||||
private handleColumnChange(dragColumnChangeEventPayload: IDragColumnChangeEventPayload): void {
|
||||
|
||||
let allDayContainer = this.getAllDayContainer();
|
||||
if (!allDayContainer) return;
|
||||
|
|
@ -380,7 +380,7 @@ export class AllDayManager {
|
|||
}
|
||||
|
||||
|
||||
private handleDragEnd(dragEndEvent: DragEndEventPayload): void {
|
||||
private handleDragEnd(dragEndEvent: IDragEndEventPayload): void {
|
||||
|
||||
const getEventDurationDays = (start: string | undefined, end: string | undefined): number => {
|
||||
|
||||
|
|
@ -433,7 +433,7 @@ export class AllDayManager {
|
|||
dragEndEvent.draggedClone.dataset.start = this.dateService.toUTC(newStartDate);
|
||||
dragEndEvent.draggedClone.dataset.end = this.dateService.toUTC(newEndDate);
|
||||
|
||||
const droppedEvent: CalendarEvent = {
|
||||
const droppedEvent: ICalendarEvent = {
|
||||
id: eventId,
|
||||
title: dragEndEvent.draggedClone.dataset.title || '',
|
||||
start: newStartDate,
|
||||
|
|
@ -557,9 +557,9 @@ export class AllDayManager {
|
|||
});
|
||||
}
|
||||
/**
|
||||
* Count number of events in a specific column using ColumnBounds
|
||||
* Count number of events in a specific column using IColumnBounds
|
||||
*/
|
||||
private countEventsInColumn(columnBounds: ColumnBounds): number {
|
||||
private countEventsInColumn(columnBounds: IColumnBounds): number {
|
||||
let columnIndex = columnBounds.index;
|
||||
let count = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { CalendarView, IEventBus } from '../types/CalendarTypes';
|
||||
import { EventManager } from './EventManager';
|
||||
import { GridManager } from './GridManager';
|
||||
|
|
@ -15,7 +15,7 @@ export class CalendarManager {
|
|||
private gridManager: GridManager;
|
||||
private eventRenderer: EventRenderingService;
|
||||
private scrollManager: ScrollManager;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private currentView: CalendarView = 'week';
|
||||
private currentDate: Date = new Date();
|
||||
private isInitialized: boolean = false;
|
||||
|
|
@ -26,7 +26,7 @@ export class CalendarManager {
|
|||
gridManager: GridManager,
|
||||
eventRenderingService: EventRenderingService,
|
||||
scrollManager: ScrollManager,
|
||||
config: CalendarConfig
|
||||
config: Configuration
|
||||
) {
|
||||
this.eventBus = eventBus;
|
||||
this.eventManager = eventManager;
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
// Configuration manager - handles config updates with event emission
|
||||
// Uses static CalendarConfig internally but adds event-driven updates
|
||||
|
||||
import { IEventBus, ICalendarConfig } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
|
||||
/**
|
||||
* Grid display settings interface (re-export from CalendarConfig)
|
||||
*/
|
||||
interface GridSettings {
|
||||
dayStartHour: number;
|
||||
dayEndHour: number;
|
||||
workStartHour: number;
|
||||
workEndHour: number;
|
||||
hourHeight: number;
|
||||
snapInterval: number;
|
||||
fitToWidth: boolean;
|
||||
scrollToHour: number | null;
|
||||
gridStartThresholdMinutes: number;
|
||||
showCurrentTime: boolean;
|
||||
showWorkHours: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* ConfigManager - Handles configuration updates with event emission
|
||||
* Wraps static CalendarConfig with event-driven functionality for DI system
|
||||
* Also manages CSS custom properties that reflect config values
|
||||
*/
|
||||
export class ConfigManager {
|
||||
constructor(private eventBus: IEventBus) {}
|
||||
|
||||
/**
|
||||
* Initialize CSS variables on startup
|
||||
* Must be called after DOM is ready but before any rendering
|
||||
*/
|
||||
public initialize(): void {
|
||||
this.updateCSSVariables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a config value and emit event
|
||||
*/
|
||||
set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void {
|
||||
const oldValue = CalendarConfig.get(key);
|
||||
CalendarConfig.set(key, value);
|
||||
|
||||
// Update CSS variables to reflect config change
|
||||
this.updateCSSVariables();
|
||||
|
||||
// Emit config update event
|
||||
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
|
||||
key,
|
||||
value,
|
||||
oldValue
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple config values and emit event
|
||||
*/
|
||||
update(updates: Partial<ICalendarConfig>): void {
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
this.set(key as keyof ICalendarConfig, value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update grid display settings and emit event
|
||||
*/
|
||||
updateGridSettings(updates: Partial<GridSettings>): void {
|
||||
CalendarConfig.updateGridSettings(updates);
|
||||
|
||||
// Update CSS variables to reflect config change
|
||||
this.updateCSSVariables();
|
||||
|
||||
// Emit event after update
|
||||
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
|
||||
key: 'gridSettings',
|
||||
value: CalendarConfig.getGridSettings()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selected date and emit event
|
||||
*/
|
||||
setSelectedDate(date: Date): void {
|
||||
const oldDate = CalendarConfig.getSelectedDate();
|
||||
CalendarConfig.setSelectedDate(date);
|
||||
|
||||
// Emit date change event if it actually changed
|
||||
if (!oldDate || oldDate.getTime() !== date.getTime()) {
|
||||
this.eventBus.emit(CoreEvents.DATE_CHANGED, {
|
||||
date,
|
||||
oldDate
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set work week and emit event
|
||||
*/
|
||||
setWorkWeek(workWeekId: string): void {
|
||||
const oldWorkWeek = CalendarConfig.getCurrentWorkWeek();
|
||||
CalendarConfig.setWorkWeek(workWeekId);
|
||||
|
||||
// Update CSS variables to reflect config change
|
||||
this.updateCSSVariables();
|
||||
|
||||
// Emit event if changed
|
||||
if (oldWorkWeek !== workWeekId) {
|
||||
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
|
||||
key: 'workWeek',
|
||||
value: workWeekId,
|
||||
oldValue: oldWorkWeek
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all CSS custom properties based on current config
|
||||
* This keeps the DOM in sync with config values
|
||||
*/
|
||||
private updateCSSVariables(): void {
|
||||
const root = document.documentElement;
|
||||
const gridSettings = CalendarConfig.getGridSettings();
|
||||
const calendar = document.querySelector('swp-calendar') as HTMLElement;
|
||||
|
||||
// Set time-related CSS variables
|
||||
root.style.setProperty('--header-height', '80px'); // Fixed header height
|
||||
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());
|
||||
|
||||
// Set column count based on view
|
||||
const columnCount = this.calculateColumnCount();
|
||||
root.style.setProperty('--grid-columns', columnCount.toString());
|
||||
|
||||
// Set column width based on fitToWidth setting
|
||||
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
|
||||
}
|
||||
|
||||
// Set fitToWidth data attribute for CSS targeting
|
||||
if (calendar) {
|
||||
calendar.setAttribute('data-fit-to-width', gridSettings.fitToWidth.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate number of columns based on view
|
||||
*/
|
||||
private calculateColumnCount(): number {
|
||||
const dateSettings = CalendarConfig.getDateViewSettings();
|
||||
const workWeekSettings = CalendarConfig.getWorkWeekSettings();
|
||||
|
||||
switch (dateSettings.period) {
|
||||
case 'day':
|
||||
return 1;
|
||||
case 'week':
|
||||
return workWeekSettings.totalDays;
|
||||
case 'month':
|
||||
return workWeekSettings.totalDays; // Use work week for month view too
|
||||
default:
|
||||
return workWeekSettings.totalDays;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,33 +134,33 @@
|
|||
|
||||
import { IEventBus } from '../types/CalendarTypes';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { ColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { IColumnBounds, ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
import { SwpEventElement, BaseSwpEventElement } from '../elements/SwpEventElement';
|
||||
import {
|
||||
DragStartEventPayload,
|
||||
DragMoveEventPayload,
|
||||
DragEndEventPayload,
|
||||
DragMouseEnterHeaderEventPayload,
|
||||
DragMouseLeaveHeaderEventPayload,
|
||||
DragMouseEnterColumnEventPayload,
|
||||
DragColumnChangeEventPayload
|
||||
IDragStartEventPayload,
|
||||
IDragMoveEventPayload,
|
||||
IDragEndEventPayload,
|
||||
IDragMouseEnterHeaderEventPayload,
|
||||
IDragMouseLeaveHeaderEventPayload,
|
||||
IDragMouseEnterColumnEventPayload,
|
||||
IDragColumnChangeEventPayload
|
||||
} from '../types/EventTypes';
|
||||
import { MousePosition } from '../types/DragDropTypes';
|
||||
import { IMousePosition } from '../types/DragDropTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
|
||||
export class DragDropManager {
|
||||
private eventBus: IEventBus;
|
||||
|
||||
// Mouse tracking with optimized state
|
||||
private mouseDownPosition: MousePosition = { x: 0, y: 0 };
|
||||
private currentMousePosition: MousePosition = { x: 0, y: 0 };
|
||||
private mouseOffset: MousePosition = { x: 0, y: 0 };
|
||||
private mouseDownPosition: IMousePosition = { x: 0, y: 0 };
|
||||
private currentMousePosition: IMousePosition = { x: 0, y: 0 };
|
||||
private mouseOffset: IMousePosition = { x: 0, y: 0 };
|
||||
|
||||
// Drag state
|
||||
private originalElement!: HTMLElement | null;
|
||||
private draggedClone!: HTMLElement | null;
|
||||
private currentColumn: ColumnBounds | null = null;
|
||||
private previousColumn: ColumnBounds | null = null;
|
||||
private currentColumn: IColumnBounds | null = null;
|
||||
private previousColumn: IColumnBounds | null = null;
|
||||
private isDragStarted = false;
|
||||
|
||||
// Movement threshold to distinguish click from drag
|
||||
|
|
@ -176,7 +176,7 @@ export class DragDropManager {
|
|||
private dragAnimationId: number | null = null;
|
||||
private targetY = 0;
|
||||
private currentY = 0;
|
||||
private targetColumn: ColumnBounds | null = null;
|
||||
private targetColumn: IColumnBounds | null = null;
|
||||
private positionUtils: PositionUtils;
|
||||
|
||||
constructor(eventBus: IEventBus, positionUtils: PositionUtils) {
|
||||
|
|
@ -336,7 +336,7 @@ export class DragDropManager {
|
|||
* Try to initialize drag based on movement threshold
|
||||
* Returns true if drag was initialized, false if not enough movement
|
||||
*/
|
||||
private initializeDrag(currentPosition: MousePosition): boolean {
|
||||
private initializeDrag(currentPosition: IMousePosition): boolean {
|
||||
const deltaX = Math.abs(currentPosition.x - this.mouseDownPosition.x);
|
||||
const deltaY = Math.abs(currentPosition.y - this.mouseDownPosition.y);
|
||||
const totalMovement = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
|
@ -362,7 +362,7 @@ export class DragDropManager {
|
|||
this.currentColumn = ColumnDetectionUtils.getColumnBounds(currentPosition);
|
||||
this.draggedClone = originalElement.createClone();
|
||||
|
||||
const dragStartPayload: DragStartEventPayload = {
|
||||
const dragStartPayload: IDragStartEventPayload = {
|
||||
originalElement: this.originalElement!,
|
||||
draggedClone: this.draggedClone,
|
||||
mousePosition: this.mouseDownPosition,
|
||||
|
|
@ -375,7 +375,7 @@ export class DragDropManager {
|
|||
}
|
||||
|
||||
|
||||
private continueDrag(currentPosition: MousePosition): void {
|
||||
private continueDrag(currentPosition: IMousePosition): void {
|
||||
|
||||
if (!this.draggedClone!.hasAttribute("data-allday")) {
|
||||
// Calculate raw position from mouse (no snapping)
|
||||
|
|
@ -405,7 +405,7 @@ export class DragDropManager {
|
|||
/**
|
||||
* Detect column change and emit event
|
||||
*/
|
||||
private detectColumnChange(currentPosition: MousePosition): void {
|
||||
private detectColumnChange(currentPosition: IMousePosition): void {
|
||||
const newColumn = ColumnDetectionUtils.getColumnBounds(currentPosition);
|
||||
if (newColumn == null) return;
|
||||
|
||||
|
|
@ -413,7 +413,7 @@ export class DragDropManager {
|
|||
this.previousColumn = this.currentColumn;
|
||||
this.currentColumn = newColumn;
|
||||
|
||||
const dragColumnChangePayload: DragColumnChangeEventPayload = {
|
||||
const dragColumnChangePayload: IDragColumnChangeEventPayload = {
|
||||
originalElement: this.originalElement!,
|
||||
draggedClone: this.draggedClone!,
|
||||
previousColumn: this.previousColumn,
|
||||
|
|
@ -434,7 +434,7 @@ export class DragDropManager {
|
|||
|
||||
// Only emit drag:end if drag was actually started
|
||||
if (this.isDragStarted) {
|
||||
const mousePosition: MousePosition = { x: event.clientX, y: event.clientY };
|
||||
const mousePosition: IMousePosition = { x: event.clientX, y: event.clientY };
|
||||
|
||||
// Snap to grid on mouse up (like ResizeHandleManager)
|
||||
const column = ColumnDetectionUtils.getColumnBounds(mousePosition);
|
||||
|
|
@ -455,7 +455,7 @@ export class DragDropManager {
|
|||
if (!dropTarget)
|
||||
throw "dropTarget is null";
|
||||
|
||||
const dragEndPayload: DragEndEventPayload = {
|
||||
const dragEndPayload: IDragEndEventPayload = {
|
||||
originalElement: this.originalElement,
|
||||
draggedClone: this.draggedClone,
|
||||
mousePosition,
|
||||
|
|
@ -530,7 +530,7 @@ export class DragDropManager {
|
|||
/**
|
||||
* Optimized snap position calculation using PositionUtils
|
||||
*/
|
||||
private calculateSnapPosition(mouseY: number, column: ColumnBounds): number {
|
||||
private calculateSnapPosition(mouseY: number, column: IColumnBounds): number {
|
||||
// Calculate where the event top would be (accounting for mouse offset)
|
||||
const eventTopY = mouseY - this.mouseOffset.y;
|
||||
|
||||
|
|
@ -560,7 +560,7 @@ export class DragDropManager {
|
|||
this.currentY += step;
|
||||
|
||||
// Emit drag:move event with current draggedClone reference
|
||||
const dragMovePayload: DragMoveEventPayload = {
|
||||
const dragMovePayload: IDragMoveEventPayload = {
|
||||
originalElement: this.originalElement!,
|
||||
draggedClone: this.draggedClone, // Always uses current reference
|
||||
mousePosition: this.currentMousePosition, // Use current mouse position!
|
||||
|
|
@ -576,7 +576,7 @@ export class DragDropManager {
|
|||
this.currentY = this.targetY;
|
||||
|
||||
// Emit final position
|
||||
const dragMovePayload: DragMoveEventPayload = {
|
||||
const dragMovePayload: IDragMoveEventPayload = {
|
||||
originalElement: this.originalElement!,
|
||||
draggedClone: this.draggedClone,
|
||||
mousePosition: this.currentMousePosition, // Use current mouse position!
|
||||
|
|
@ -633,7 +633,7 @@ export class DragDropManager {
|
|||
/**
|
||||
* Detect drop target - whether dropped in swp-day-column or swp-day-header
|
||||
*/
|
||||
private detectDropTarget(position: MousePosition): 'swp-day-column' | 'swp-day-header' | null {
|
||||
private detectDropTarget(position: IMousePosition): 'swp-day-column' | 'swp-day-header' | null {
|
||||
|
||||
// Traverse up the DOM tree to find the target container
|
||||
let currentElement = this.draggedClone;
|
||||
|
|
@ -659,13 +659,13 @@ export class DragDropManager {
|
|||
return;
|
||||
}
|
||||
|
||||
const position: MousePosition = { x: event.clientX, y: event.clientY };
|
||||
const position: IMousePosition = { x: event.clientX, y: event.clientY };
|
||||
const targetColumn = ColumnDetectionUtils.getColumnBounds(position);
|
||||
|
||||
if (targetColumn) {
|
||||
const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone);
|
||||
|
||||
const dragMouseEnterPayload: DragMouseEnterHeaderEventPayload = {
|
||||
const dragMouseEnterPayload: IDragMouseEnterHeaderEventPayload = {
|
||||
targetColumn: targetColumn,
|
||||
mousePosition: position,
|
||||
originalElement: this.originalElement,
|
||||
|
|
@ -689,7 +689,7 @@ export class DragDropManager {
|
|||
return;
|
||||
}
|
||||
|
||||
const position: MousePosition = { x: event.clientX, y: event.clientY };
|
||||
const position: IMousePosition = { x: event.clientX, y: event.clientY };
|
||||
const targetColumn = ColumnDetectionUtils.getColumnBounds(position);
|
||||
|
||||
if (!targetColumn) {
|
||||
|
|
@ -699,10 +699,10 @@ export class DragDropManager {
|
|||
// Calculate snapped Y position
|
||||
const snappedY = this.calculateSnapPosition(position.y, targetColumn);
|
||||
|
||||
// Extract CalendarEvent from the dragged clone
|
||||
// Extract ICalendarEvent from the dragged clone
|
||||
const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone);
|
||||
|
||||
const dragMouseEnterPayload: DragMouseEnterColumnEventPayload = {
|
||||
const dragMouseEnterPayload: IDragMouseEnterColumnEventPayload = {
|
||||
targetColumn: targetColumn,
|
||||
mousePosition: position,
|
||||
snappedY: snappedY,
|
||||
|
|
@ -727,14 +727,14 @@ export class DragDropManager {
|
|||
return;
|
||||
}
|
||||
|
||||
const position: MousePosition = { x: event.clientX, y: event.clientY };
|
||||
const position: IMousePosition = { x: event.clientX, y: event.clientY };
|
||||
const targetColumn = ColumnDetectionUtils.getColumnBounds(position);
|
||||
|
||||
if (!targetColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dragMouseLeavePayload: DragMouseLeaveHeaderEventPayload = {
|
||||
const dragMouseLeavePayload: IDragMouseLeaveHeaderEventPayload = {
|
||||
targetDate: targetColumn.date,
|
||||
mousePosition: position,
|
||||
originalElement: this.originalElement,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { IEventBus } from '../types/CalendarTypes';
|
||||
import { DragMoveEventPayload, DragStartEventPayload } from '../types/EventTypes';
|
||||
import { IDragMoveEventPayload, IDragStartEventPayload } from '../types/EventTypes';
|
||||
|
||||
export class EdgeScrollManager {
|
||||
private scrollableContent: HTMLElement | null = null;
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@
|
|||
|
||||
import { eventBus } from '../core/EventBus';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
|
||||
// Import Fuse.js from npm
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
interface FuseResult {
|
||||
item: CalendarEvent;
|
||||
item: ICalendarEvent;
|
||||
refIndex: number;
|
||||
score?: number;
|
||||
}
|
||||
|
||||
export class EventFilterManager {
|
||||
private searchInput: HTMLInputElement | null = null;
|
||||
private allEvents: CalendarEvent[] = [];
|
||||
private allEvents: ICalendarEvent[] = [];
|
||||
private matchingEventIds: Set<string> = new Set();
|
||||
private isFilterActive: boolean = false;
|
||||
private frameRequest: number | null = null;
|
||||
private fuse: Fuse<CalendarEvent> | null = null;
|
||||
private fuse: Fuse<ICalendarEvent> | null = null;
|
||||
|
||||
constructor() {
|
||||
// Wait for DOM to be ready before initializing
|
||||
|
|
@ -77,7 +77,7 @@ export class EventFilterManager {
|
|||
});
|
||||
}
|
||||
|
||||
private updateEventsList(events: CalendarEvent[]): void {
|
||||
private updateEventsList(events: ICalendarEvent[]): void {
|
||||
this.allEvents = events;
|
||||
|
||||
// Initialize Fuse with the new events list
|
||||
|
|
|
|||
|
|
@ -5,35 +5,35 @@
|
|||
* Calculates stack levels, groups events, and determines rendering strategy.
|
||||
*/
|
||||
|
||||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { EventStackManager, EventGroup, StackLink } from './EventStackManager';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { EventStackManager, IEventGroup, IStackLink } from './EventStackManager';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
|
||||
export interface GridGroupLayout {
|
||||
events: CalendarEvent[];
|
||||
export interface IGridGroupLayout {
|
||||
events: ICalendarEvent[];
|
||||
stackLevel: number;
|
||||
position: { top: number };
|
||||
columns: CalendarEvent[][]; // Events grouped by column (events in same array share a column)
|
||||
columns: ICalendarEvent[][]; // Events grouped by column (events in same array share a column)
|
||||
}
|
||||
|
||||
export interface StackedEventLayout {
|
||||
event: CalendarEvent;
|
||||
stackLink: StackLink;
|
||||
export interface IStackedEventLayout {
|
||||
event: ICalendarEvent;
|
||||
stackLink: IStackLink;
|
||||
position: { top: number; height: number };
|
||||
}
|
||||
|
||||
export interface ColumnLayout {
|
||||
gridGroups: GridGroupLayout[];
|
||||
stackedEvents: StackedEventLayout[];
|
||||
export interface IColumnLayout {
|
||||
gridGroups: IGridGroupLayout[];
|
||||
stackedEvents: IStackedEventLayout[];
|
||||
}
|
||||
|
||||
export class EventLayoutCoordinator {
|
||||
private stackManager: EventStackManager;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private positionUtils: PositionUtils;
|
||||
|
||||
constructor(stackManager: EventStackManager, config: CalendarConfig, positionUtils: PositionUtils) {
|
||||
constructor(stackManager: EventStackManager, config: Configuration, positionUtils: PositionUtils) {
|
||||
this.stackManager = stackManager;
|
||||
this.config = config;
|
||||
this.positionUtils = positionUtils;
|
||||
|
|
@ -42,14 +42,14 @@ export class EventLayoutCoordinator {
|
|||
/**
|
||||
* Calculate complete layout for a column of events (recursive approach)
|
||||
*/
|
||||
public calculateColumnLayout(columnEvents: CalendarEvent[]): ColumnLayout {
|
||||
public calculateColumnLayout(columnEvents: ICalendarEvent[]): IColumnLayout {
|
||||
if (columnEvents.length === 0) {
|
||||
return { gridGroups: [], stackedEvents: [] };
|
||||
}
|
||||
|
||||
const gridGroupLayouts: GridGroupLayout[] = [];
|
||||
const stackedEventLayouts: StackedEventLayout[] = [];
|
||||
const renderedEventsWithLevels: Array<{ event: CalendarEvent; level: number }> = [];
|
||||
const gridGroupLayouts: IGridGroupLayout[] = [];
|
||||
const stackedEventLayouts: IStackedEventLayout[] = [];
|
||||
const renderedEventsWithLevels: Array<{ event: ICalendarEvent; level: number }> = [];
|
||||
let remaining = [...columnEvents].sort((a, b) => a.start.getTime() - b.start.getTime());
|
||||
|
||||
// Process events recursively
|
||||
|
|
@ -66,7 +66,7 @@ export class EventLayoutCoordinator {
|
|||
const gridCandidates = this.expandGridCandidates(firstEvent, remaining, thresholdMinutes);
|
||||
|
||||
// Decide: should this group be GRID or STACK?
|
||||
const group: EventGroup = {
|
||||
const group: IEventGroup = {
|
||||
events: gridCandidates,
|
||||
containerType: 'NONE',
|
||||
startTime: firstEvent.start
|
||||
|
|
@ -129,8 +129,8 @@ export class EventLayoutCoordinator {
|
|||
* Calculate stack level for a grid group based on already rendered events
|
||||
*/
|
||||
private calculateGridGroupStackLevelFromRendered(
|
||||
gridEvents: CalendarEvent[],
|
||||
renderedEventsWithLevels: Array<{ event: CalendarEvent; level: number }>
|
||||
gridEvents: ICalendarEvent[],
|
||||
renderedEventsWithLevels: Array<{ event: ICalendarEvent; level: number }>
|
||||
): number {
|
||||
// Find highest stack level of any rendered event that overlaps with this grid
|
||||
let maxOverlappingLevel = -1;
|
||||
|
|
@ -150,8 +150,8 @@ export class EventLayoutCoordinator {
|
|||
* Calculate stack level for a single stacked event based on already rendered events
|
||||
*/
|
||||
private calculateStackLevelFromRendered(
|
||||
event: CalendarEvent,
|
||||
renderedEventsWithLevels: Array<{ event: CalendarEvent; level: number }>
|
||||
event: ICalendarEvent,
|
||||
renderedEventsWithLevels: Array<{ event: ICalendarEvent; level: number }>
|
||||
): number {
|
||||
// Find highest stack level of any rendered event that overlaps with this event
|
||||
let maxOverlappingLevel = -1;
|
||||
|
|
@ -173,7 +173,7 @@ export class EventLayoutCoordinator {
|
|||
* @param thresholdMinutes - Threshold in minutes
|
||||
* @returns true if events conflict
|
||||
*/
|
||||
private detectConflict(event1: CalendarEvent, event2: CalendarEvent, thresholdMinutes: number): boolean {
|
||||
private detectConflict(event1: ICalendarEvent, event2: ICalendarEvent, thresholdMinutes: number): boolean {
|
||||
// Check 1: Start-to-start conflict (starts within threshold)
|
||||
const startToStartDiff = Math.abs(event1.start.getTime() - event2.start.getTime()) / (1000 * 60);
|
||||
if (startToStartDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(event1, event2)) {
|
||||
|
|
@ -206,10 +206,10 @@ export class EventLayoutCoordinator {
|
|||
* @returns Array of all events in the conflict chain
|
||||
*/
|
||||
private expandGridCandidates(
|
||||
firstEvent: CalendarEvent,
|
||||
remaining: CalendarEvent[],
|
||||
firstEvent: ICalendarEvent,
|
||||
remaining: ICalendarEvent[],
|
||||
thresholdMinutes: number
|
||||
): CalendarEvent[] {
|
||||
): ICalendarEvent[] {
|
||||
const gridCandidates = [firstEvent];
|
||||
let candidatesChanged = true;
|
||||
|
||||
|
|
@ -246,11 +246,11 @@ export class EventLayoutCoordinator {
|
|||
* @param events - Events in the grid group (should already be sorted by start time)
|
||||
* @returns Array of columns, where each column is an array of events
|
||||
*/
|
||||
private allocateColumns(events: CalendarEvent[]): CalendarEvent[][] {
|
||||
private allocateColumns(events: ICalendarEvent[]): ICalendarEvent[][] {
|
||||
if (events.length === 0) return [];
|
||||
if (events.length === 1) return [[events[0]]];
|
||||
|
||||
const columns: CalendarEvent[][] = [];
|
||||
const columns: ICalendarEvent[][] = [];
|
||||
|
||||
// For each event, try to place it in an existing column where it doesn't overlap
|
||||
for (const event of events) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { IEventBus, CalendarEvent } from '../types/CalendarTypes';
|
||||
import { IEventBus, ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { IEventRepository } from '../repositories/IEventRepository';
|
||||
|
||||
|
|
@ -10,15 +10,15 @@ import { IEventRepository } from '../repositories/IEventRepository';
|
|||
*/
|
||||
export class EventManager {
|
||||
|
||||
private events: CalendarEvent[] = [];
|
||||
private events: ICalendarEvent[] = [];
|
||||
private dateService: DateService;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private repository: IEventRepository;
|
||||
|
||||
constructor(
|
||||
private eventBus: IEventBus,
|
||||
dateService: DateService,
|
||||
config: CalendarConfig,
|
||||
config: Configuration,
|
||||
repository: IEventRepository
|
||||
) {
|
||||
this.dateService = dateService;
|
||||
|
|
@ -42,14 +42,14 @@ export class EventManager {
|
|||
/**
|
||||
* Get events with optional copying for performance
|
||||
*/
|
||||
public getEvents(copy: boolean = false): CalendarEvent[] {
|
||||
public getEvents(copy: boolean = false): ICalendarEvent[] {
|
||||
return copy ? [...this.events] : this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized event lookup with early return
|
||||
*/
|
||||
public getEventById(id: string): CalendarEvent | undefined {
|
||||
public getEventById(id: string): ICalendarEvent | undefined {
|
||||
// Use find for better performance than filter + first
|
||||
return this.events.find(event => event.id === id);
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ export class EventManager {
|
|||
* @param id Event ID to find
|
||||
* @returns Event with navigation info or null if not found
|
||||
*/
|
||||
public getEventForNavigation(id: string): { event: CalendarEvent; eventDate: Date } | null {
|
||||
public getEventForNavigation(id: string): { event: ICalendarEvent; eventDate: Date } | null {
|
||||
const event = this.getEventById(id);
|
||||
if (!event) {
|
||||
return null;
|
||||
|
|
@ -113,7 +113,7 @@ export class EventManager {
|
|||
/**
|
||||
* Get events that overlap with a given time period
|
||||
*/
|
||||
public getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[] {
|
||||
public getEventsForPeriod(startDate: Date, endDate: Date): ICalendarEvent[] {
|
||||
// Event overlaps period if it starts before period ends AND ends after period starts
|
||||
return this.events.filter(event => {
|
||||
return event.start <= endDate && event.end >= startDate;
|
||||
|
|
@ -123,8 +123,8 @@ export class EventManager {
|
|||
/**
|
||||
* Create a new event and add it to the calendar
|
||||
*/
|
||||
public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent {
|
||||
const newEvent: CalendarEvent = {
|
||||
public addEvent(event: Omit<ICalendarEvent, 'id'>): ICalendarEvent {
|
||||
const newEvent: ICalendarEvent = {
|
||||
...event,
|
||||
id: `event_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
};
|
||||
|
|
@ -141,7 +141,7 @@ export class EventManager {
|
|||
/**
|
||||
* Update an existing event
|
||||
*/
|
||||
public updateEvent(id: string, updates: Partial<CalendarEvent>): CalendarEvent | null {
|
||||
public updateEvent(id: string, updates: Partial<ICalendarEvent>): ICalendarEvent | null {
|
||||
const eventIndex = this.events.findIndex(event => event.id === id);
|
||||
if (eventIndex === -1) return null;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,26 +13,26 @@
|
|||
* @see stacking-visualization.html for visual examples
|
||||
*/
|
||||
|
||||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
|
||||
export interface StackLink {
|
||||
export interface IStackLink {
|
||||
prev?: string; // Event ID of previous event in stack
|
||||
next?: string; // Event ID of next event in stack
|
||||
stackLevel: number; // Position in stack (0 = base, 1 = first offset, etc.)
|
||||
}
|
||||
|
||||
export interface EventGroup {
|
||||
events: CalendarEvent[];
|
||||
export interface IEventGroup {
|
||||
events: ICalendarEvent[];
|
||||
containerType: 'NONE' | 'GRID' | 'STACKING';
|
||||
startTime: Date;
|
||||
}
|
||||
|
||||
export class EventStackManager {
|
||||
private static readonly STACK_OFFSET_PX = 15;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
|
||||
constructor(config: CalendarConfig) {
|
||||
constructor(config: Configuration) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ export class EventStackManager {
|
|||
* 1. They start within ±threshold minutes of each other (start-to-start)
|
||||
* 2. One event starts within threshold minutes before another ends (end-to-start conflict)
|
||||
*/
|
||||
public groupEventsByStartTime(events: CalendarEvent[]): EventGroup[] {
|
||||
public groupEventsByStartTime(events: ICalendarEvent[]): IEventGroup[] {
|
||||
if (events.length === 0) return [];
|
||||
|
||||
// Get threshold from config
|
||||
|
|
@ -57,7 +57,7 @@ export class EventStackManager {
|
|||
// Sort events by start time
|
||||
const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
|
||||
|
||||
const groups: EventGroup[] = [];
|
||||
const groups: IEventGroup[] = [];
|
||||
|
||||
for (const event of sorted) {
|
||||
// Find existing group that this event conflicts with
|
||||
|
|
@ -112,7 +112,7 @@ export class EventStackManager {
|
|||
* even if they overlap each other. This provides better visual indication that
|
||||
* events start at the same time.
|
||||
*/
|
||||
public decideContainerType(group: EventGroup): 'NONE' | 'GRID' | 'STACKING' {
|
||||
public decideContainerType(group: IEventGroup): 'NONE' | 'GRID' | 'STACKING' {
|
||||
if (group.events.length === 1) {
|
||||
return 'NONE';
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ export class EventStackManager {
|
|||
/**
|
||||
* Check if two events overlap in time
|
||||
*/
|
||||
public doEventsOverlap(event1: CalendarEvent, event2: CalendarEvent): boolean {
|
||||
public doEventsOverlap(event1: ICalendarEvent, event2: ICalendarEvent): boolean {
|
||||
return event1.start < event2.end && event1.end > event2.start;
|
||||
}
|
||||
|
||||
|
|
@ -139,8 +139,8 @@ export class EventStackManager {
|
|||
/**
|
||||
* Create optimized stack links (events share levels when possible)
|
||||
*/
|
||||
public createOptimizedStackLinks(events: CalendarEvent[]): Map<string, StackLink> {
|
||||
const stackLinks = new Map<string, StackLink>();
|
||||
public createOptimizedStackLinks(events: ICalendarEvent[]): Map<string, IStackLink> {
|
||||
const stackLinks = new Map<string, IStackLink>();
|
||||
|
||||
if (events.length === 0) return stackLinks;
|
||||
|
||||
|
|
@ -218,14 +218,14 @@ export class EventStackManager {
|
|||
/**
|
||||
* Serialize stack link to JSON string
|
||||
*/
|
||||
public serializeStackLink(stackLink: StackLink): string {
|
||||
public serializeStackLink(stackLink: IStackLink): string {
|
||||
return JSON.stringify(stackLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize JSON string to stack link
|
||||
*/
|
||||
public deserializeStackLink(json: string): StackLink | null {
|
||||
public deserializeStackLink(json: string): IStackLink | null {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) {
|
||||
|
|
@ -236,14 +236,14 @@ export class EventStackManager {
|
|||
/**
|
||||
* Apply stack link to DOM element
|
||||
*/
|
||||
public applyStackLinkToElement(element: HTMLElement, stackLink: StackLink): void {
|
||||
public applyStackLinkToElement(element: HTMLElement, stackLink: IStackLink): void {
|
||||
element.dataset.stackLink = this.serializeStackLink(stackLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stack link from DOM element
|
||||
*/
|
||||
public getStackLinkFromElement(element: HTMLElement): StackLink | null {
|
||||
public getStackLinkFromElement(element: HTMLElement): IStackLink | null {
|
||||
const data = element.dataset.stackLink;
|
||||
if (!data) return null;
|
||||
return this.deserializeStackLink(data);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { eventBus } from '../core/EventBus';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { IHeaderRenderer, HeaderRenderContext } from '../renderers/DateHeaderRenderer';
|
||||
import { DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from '../types/EventTypes';
|
||||
import { IHeaderRenderer, IHeaderRenderContext } from '../renderers/DateHeaderRenderer';
|
||||
import { IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IHeaderReadyEventPayload } from '../types/EventTypes';
|
||||
import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
||||
|
||||
/**
|
||||
|
|
@ -12,9 +12,9 @@ import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils';
|
|||
*/
|
||||
export class HeaderManager {
|
||||
private headerRenderer: IHeaderRenderer;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
|
||||
constructor(headerRenderer: IHeaderRenderer, config: CalendarConfig) {
|
||||
constructor(headerRenderer: IHeaderRenderer, config: Configuration) {
|
||||
this.headerRenderer = headerRenderer;
|
||||
this.config = config;
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ export class HeaderManager {
|
|||
*/
|
||||
private handleDragMouseEnterHeader(event: Event): void {
|
||||
const { targetColumn: targetDate, mousePosition, originalElement, draggedClone: cloneElement } =
|
||||
(event as CustomEvent<DragMouseEnterHeaderEventPayload>).detail;
|
||||
(event as CustomEvent<IDragMouseEnterHeaderEventPayload>).detail;
|
||||
|
||||
console.log('🎯 HeaderManager: Received drag:mouseenter-header', {
|
||||
targetDate,
|
||||
|
|
@ -58,7 +58,7 @@ export class HeaderManager {
|
|||
*/
|
||||
private handleDragMouseLeaveHeader(event: Event): void {
|
||||
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } =
|
||||
(event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
||||
(event as CustomEvent<IDragMouseLeaveHeaderEventPayload>).detail;
|
||||
|
||||
console.log('🚪 HeaderManager: Received drag:mouseleave-header', {
|
||||
targetDate,
|
||||
|
|
@ -109,7 +109,7 @@ export class HeaderManager {
|
|||
calendarHeader.innerHTML = '';
|
||||
|
||||
// Render new header content using injected renderer
|
||||
const context: HeaderRenderContext = {
|
||||
const context: IHeaderRenderContext = {
|
||||
currentWeek: currentDate,
|
||||
config: this.config
|
||||
};
|
||||
|
|
@ -120,7 +120,7 @@ export class HeaderManager {
|
|||
this.setupHeaderDragListeners();
|
||||
|
||||
// Notify other managers that header is ready with period data
|
||||
const payload: HeaderReadyEventPayload = {
|
||||
const payload: IHeaderReadyEventPayload = {
|
||||
headerElements: ColumnDetectionUtils.getHeaderColumns(),
|
||||
};
|
||||
eventBus.emit('header:ready', payload);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { eventBus } from '../core/EventBus';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ResizeEndEventPayload } from '../types/EventTypes';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { IResizeEndEventPayload } from '../types/EventTypes';
|
||||
|
||||
type SwpEventEl = HTMLElement & { updateHeight?: (h: number) => void };
|
||||
|
||||
|
|
@ -29,9 +29,9 @@ export class ResizeHandleManager {
|
|||
private unsubscribers: Array<() => void> = [];
|
||||
private pointerCaptured = false;
|
||||
private prevZ?: string;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
|
||||
constructor(config: CalendarConfig) {
|
||||
constructor(config: Configuration) {
|
||||
this.config = config;
|
||||
const grid = this.config.getGridSettings();
|
||||
this.hourHeightPx = grid.hourHeight;
|
||||
|
|
@ -237,7 +237,7 @@ export class ResizeHandleManager {
|
|||
|
||||
// Emit resize:end event for re-stacking
|
||||
const eventId = this.targetEl.dataset.eventId || '';
|
||||
const resizeEndPayload: ResizeEndEventPayload = {
|
||||
const resizeEndPayload: IResizeEndEventPayload = {
|
||||
eventId,
|
||||
element: this.targetEl,
|
||||
finalHeight
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { CalendarView, IEventBus } from '../types/CalendarTypes';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
|
||||
|
||||
export class ViewManager {
|
||||
private eventBus: IEventBus;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private currentView: CalendarView = 'week';
|
||||
private buttonListeners: Map<Element, EventListener> = new Map();
|
||||
|
||||
constructor(eventBus: IEventBus, config: CalendarConfig) {
|
||||
constructor(eventBus: IEventBus, config: Configuration) {
|
||||
this.eventBus = eventBus;
|
||||
this.config = config;
|
||||
this.setupEventListeners();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// Work hours management for per-column scheduling
|
||||
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
|
||||
/**
|
||||
* Work hours for a specific day
|
||||
*/
|
||||
export interface DayWorkHours {
|
||||
export interface IDayWorkHours {
|
||||
start: number; // Hour (0-23)
|
||||
end: number; // Hour (0-23)
|
||||
}
|
||||
|
|
@ -15,18 +15,18 @@ export interface DayWorkHours {
|
|||
/**
|
||||
* Work schedule configuration
|
||||
*/
|
||||
export interface WorkScheduleConfig {
|
||||
export interface IWorkScheduleConfig {
|
||||
weeklyDefault: {
|
||||
monday: DayWorkHours | 'off';
|
||||
tuesday: DayWorkHours | 'off';
|
||||
wednesday: DayWorkHours | 'off';
|
||||
thursday: DayWorkHours | 'off';
|
||||
friday: DayWorkHours | 'off';
|
||||
saturday: DayWorkHours | 'off';
|
||||
sunday: DayWorkHours | 'off';
|
||||
monday: IDayWorkHours | 'off';
|
||||
tuesday: IDayWorkHours | 'off';
|
||||
wednesday: IDayWorkHours | 'off';
|
||||
thursday: IDayWorkHours | 'off';
|
||||
friday: IDayWorkHours | 'off';
|
||||
saturday: IDayWorkHours | 'off';
|
||||
sunday: IDayWorkHours | 'off';
|
||||
};
|
||||
dateOverrides: {
|
||||
[dateString: string]: DayWorkHours | 'off'; // YYYY-MM-DD format
|
||||
[dateString: string]: IDayWorkHours | 'off'; // YYYY-MM-DD format
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -35,11 +35,11 @@ export interface WorkScheduleConfig {
|
|||
*/
|
||||
export class WorkHoursManager {
|
||||
private dateService: DateService;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private positionUtils: PositionUtils;
|
||||
private workSchedule: WorkScheduleConfig;
|
||||
private workSchedule: IWorkScheduleConfig;
|
||||
|
||||
constructor(dateService: DateService, config: CalendarConfig, positionUtils: PositionUtils) {
|
||||
constructor(dateService: DateService, config: Configuration, positionUtils: PositionUtils) {
|
||||
this.dateService = dateService;
|
||||
this.config = config;
|
||||
this.positionUtils = positionUtils;
|
||||
|
|
@ -66,7 +66,7 @@ export class WorkHoursManager {
|
|||
/**
|
||||
* Get work hours for a specific date
|
||||
*/
|
||||
getWorkHoursForDate(date: Date): DayWorkHours | 'off' {
|
||||
getWorkHoursForDate(date: Date): IDayWorkHours | 'off' {
|
||||
const dateString = this.dateService.formatISODate(date);
|
||||
|
||||
// Check for date-specific override first
|
||||
|
|
@ -82,8 +82,8 @@ export class WorkHoursManager {
|
|||
/**
|
||||
* Get work hours for multiple dates (used by GridManager)
|
||||
*/
|
||||
getWorkHoursForDateRange(dates: Date[]): Map<string, DayWorkHours | 'off'> {
|
||||
const workHoursMap = new Map<string, DayWorkHours | 'off'>();
|
||||
getWorkHoursForDateRange(dates: Date[]): Map<string, IDayWorkHours | 'off'> {
|
||||
const workHoursMap = new Map<string, IDayWorkHours | 'off'>();
|
||||
|
||||
dates.forEach(date => {
|
||||
const dateString = this.dateService.formatISODate(date);
|
||||
|
|
@ -97,7 +97,7 @@ export class WorkHoursManager {
|
|||
/**
|
||||
* Calculate CSS custom properties for non-work hour overlays using PositionUtils
|
||||
*/
|
||||
calculateNonWorkHoursStyle(workHours: DayWorkHours | 'off'): { beforeWorkHeight: number; afterWorkTop: number } | null {
|
||||
calculateNonWorkHoursStyle(workHours: IDayWorkHours | 'off'): { beforeWorkHeight: number; afterWorkTop: number } | null {
|
||||
if (workHours === 'off') {
|
||||
return null; // Full day will be colored via CSS background
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ export class WorkHoursManager {
|
|||
/**
|
||||
* Calculate CSS custom properties for work hours overlay using PositionUtils
|
||||
*/
|
||||
calculateWorkHoursStyle(workHours: DayWorkHours | 'off'): { top: number; height: number } | null {
|
||||
calculateWorkHoursStyle(workHours: IDayWorkHours | 'off'): { top: number; height: number } | null {
|
||||
if (workHours === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -139,22 +139,22 @@ export class WorkHoursManager {
|
|||
/**
|
||||
* Load work schedule from JSON (future implementation)
|
||||
*/
|
||||
async loadWorkSchedule(jsonData: WorkScheduleConfig): Promise<void> {
|
||||
async loadWorkSchedule(jsonData: IWorkScheduleConfig): Promise<void> {
|
||||
this.workSchedule = jsonData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current work schedule configuration
|
||||
*/
|
||||
getWorkSchedule(): WorkScheduleConfig {
|
||||
getWorkSchedule(): IWorkScheduleConfig {
|
||||
return this.workSchedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Date to day name key
|
||||
*/
|
||||
private getDayName(date: Date): keyof WorkScheduleConfig['weeklyDefault'] {
|
||||
const dayNames: (keyof WorkScheduleConfig['weeklyDefault'])[] = [
|
||||
private getDayName(date: Date): keyof IWorkScheduleConfig['weeklyDefault'] {
|
||||
const dayNames: (keyof IWorkScheduleConfig['weeklyDefault'])[] = [
|
||||
'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'
|
||||
];
|
||||
return dayNames[date.getDay()];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { SwpAllDayEventElement } from '../elements/SwpEventElement';
|
||||
import { EventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { IEventLayout } from '../utils/AllDayLayoutEngine';
|
||||
import { IColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { EventManager } from '../managers/EventManager';
|
||||
import { DragStartEventPayload } from '../types/EventTypes';
|
||||
import { IDragStartEventPayload } from '../types/EventTypes';
|
||||
import { IEventRenderer } from './EventRenderer';
|
||||
|
||||
export class AllDayEventRenderer {
|
||||
|
|
@ -38,7 +38,7 @@ export class AllDayEventRenderer {
|
|||
/**
|
||||
* Handle drag start for all-day events
|
||||
*/
|
||||
public handleDragStart(payload: DragStartEventPayload): void {
|
||||
public handleDragStart(payload: IDragStartEventPayload): void {
|
||||
|
||||
this.originalEvent = payload.originalElement;;
|
||||
this.draggedClone = payload.draggedClone;
|
||||
|
|
@ -70,8 +70,8 @@ export class AllDayEventRenderer {
|
|||
* Render an all-day event with pre-calculated layout
|
||||
*/
|
||||
private renderAllDayEventWithLayout(
|
||||
event: CalendarEvent,
|
||||
layout: EventLayout
|
||||
event: ICalendarEvent,
|
||||
layout: IEventLayout
|
||||
) {
|
||||
const container = this.getContainer();
|
||||
if (!container) return null;
|
||||
|
|
@ -109,7 +109,7 @@ export class AllDayEventRenderer {
|
|||
/**
|
||||
* Render all-day events for specific period using AllDayEventRenderer
|
||||
*/
|
||||
public renderAllDayEventsForPeriod(eventLayouts: EventLayout[]): void {
|
||||
public renderAllDayEventsForPeriod(eventLayouts: IEventLayout[]): void {
|
||||
this.clearAllDayEvents();
|
||||
|
||||
eventLayouts.forEach(layout => {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
// Column rendering strategy interface and implementations
|
||||
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { WorkHoursManager } from '../managers/WorkHoursManager';
|
||||
|
||||
/**
|
||||
* Interface for column rendering strategies
|
||||
*/
|
||||
export interface ColumnRenderer {
|
||||
render(columnContainer: HTMLElement, context: ColumnRenderContext): void;
|
||||
export interface IColumnRenderer {
|
||||
render(columnContainer: HTMLElement, context: IColumnRenderContext): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context for column rendering
|
||||
*/
|
||||
export interface ColumnRenderContext {
|
||||
export interface IColumnRenderContext {
|
||||
currentWeek: Date;
|
||||
config: CalendarConfig;
|
||||
config: Configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date-based column renderer (original functionality)
|
||||
*/
|
||||
export class DateColumnRenderer implements ColumnRenderer {
|
||||
export class DateColumnRenderer implements IColumnRenderer {
|
||||
private dateService: DateService;
|
||||
private workHoursManager: WorkHoursManager;
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export class DateColumnRenderer implements ColumnRenderer {
|
|||
this.workHoursManager = workHoursManager;
|
||||
}
|
||||
|
||||
render(columnContainer: HTMLElement, context: ColumnRenderContext): void {
|
||||
render(columnContainer: HTMLElement, context: IColumnRenderContext): void {
|
||||
const { currentWeek, config } = context;
|
||||
|
||||
const workWeekSettings = config.getWorkWeekSettings();
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
// Header rendering strategy interface and implementations
|
||||
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { DateService } from '../utils/DateService';
|
||||
|
||||
/**
|
||||
* Interface for header rendering strategies
|
||||
*/
|
||||
export interface IHeaderRenderer {
|
||||
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void;
|
||||
render(calendarHeader: HTMLElement, context: IHeaderRenderContext): void;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Context for header rendering
|
||||
*/
|
||||
export interface HeaderRenderContext {
|
||||
export interface IHeaderRenderContext {
|
||||
currentWeek: Date;
|
||||
config: CalendarConfig;
|
||||
config: Configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,7 +25,7 @@ export interface HeaderRenderContext {
|
|||
export class DateHeaderRenderer implements IHeaderRenderer {
|
||||
private dateService!: DateService;
|
||||
|
||||
render(calendarHeader: HTMLElement, context: HeaderRenderContext): void {
|
||||
render(calendarHeader: HTMLElement, context: IHeaderRenderContext): void {
|
||||
const { currentWeek, config } = context;
|
||||
|
||||
// FIRST: Always create all-day container as part of standard header structure
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
// Event rendering strategy interface and implementations
|
||||
|
||||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||
import { PositionUtils } from '../utils/PositionUtils';
|
||||
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { DragColumnChangeEventPayload, DragMoveEventPayload, DragStartEventPayload, DragMouseEnterColumnEventPayload } from '../types/EventTypes';
|
||||
import { IColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { IDragColumnChangeEventPayload, IDragMoveEventPayload, IDragStartEventPayload, IDragMouseEnterColumnEventPayload } from '../types/EventTypes';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { EventStackManager } from '../managers/EventStackManager';
|
||||
import { EventLayoutCoordinator, GridGroupLayout, StackedEventLayout } from '../managers/EventLayoutCoordinator';
|
||||
import { EventLayoutCoordinator, IGridGroupLayout, IStackedEventLayout } from '../managers/EventLayoutCoordinator';
|
||||
|
||||
/**
|
||||
* Interface for event rendering strategies
|
||||
*/
|
||||
export interface IEventRenderer {
|
||||
renderEvents(events: CalendarEvent[], container: HTMLElement): void;
|
||||
renderEvents(events: ICalendarEvent[], container: HTMLElement): void;
|
||||
clearEvents(container?: HTMLElement): void;
|
||||
handleDragStart?(payload: DragStartEventPayload): void;
|
||||
handleDragMove?(payload: DragMoveEventPayload): void;
|
||||
handleDragStart?(payload: IDragStartEventPayload): void;
|
||||
handleDragMove?(payload: IDragMoveEventPayload): void;
|
||||
handleDragAutoScroll?(eventId: string, snappedY: number): void;
|
||||
handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: ColumnBounds, finalY: number): void;
|
||||
handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void;
|
||||
handleEventClick?(eventId: string, originalElement: HTMLElement): void;
|
||||
handleColumnChange?(payload: DragColumnChangeEventPayload): void;
|
||||
handleColumnChange?(payload: IDragColumnChangeEventPayload): void;
|
||||
handleNavigationCompleted?(): void;
|
||||
handleConvertAllDayToTimed?(payload: DragMouseEnterColumnEventPayload): void;
|
||||
handleConvertAllDayToTimed?(payload: IDragMouseEnterColumnEventPayload): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +34,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
private dateService: DateService;
|
||||
private stackManager: EventStackManager;
|
||||
private layoutCoordinator: EventLayoutCoordinator;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
private positionUtils: PositionUtils;
|
||||
private draggedClone: HTMLElement | null = null;
|
||||
private originalEvent: HTMLElement | null = null;
|
||||
|
|
@ -43,7 +43,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
dateService: DateService,
|
||||
stackManager: EventStackManager,
|
||||
layoutCoordinator: EventLayoutCoordinator,
|
||||
config: CalendarConfig,
|
||||
config: Configuration,
|
||||
positionUtils: PositionUtils
|
||||
) {
|
||||
this.dateService = dateService;
|
||||
|
|
@ -63,7 +63,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Handle drag start event
|
||||
*/
|
||||
public handleDragStart(payload: DragStartEventPayload): void {
|
||||
public handleDragStart(payload: IDragStartEventPayload): void {
|
||||
|
||||
this.originalEvent = payload.originalElement;;
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Handle drag move event
|
||||
*/
|
||||
public handleDragMove(payload: DragMoveEventPayload): void {
|
||||
public handleDragMove(payload: IDragMoveEventPayload): void {
|
||||
|
||||
const swpEvent = payload.draggedClone as SwpEventElement;
|
||||
const columnDate = this.dateService.parseISO(payload.columnBounds!!.date);
|
||||
|
|
@ -108,7 +108,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Handle column change during drag
|
||||
*/
|
||||
public handleColumnChange(payload: DragColumnChangeEventPayload): void {
|
||||
public handleColumnChange(payload: IDragColumnChangeEventPayload): void {
|
||||
|
||||
const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer');
|
||||
if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) {
|
||||
|
|
@ -125,7 +125,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Handle conversion of all-day event to timed event
|
||||
*/
|
||||
public handleConvertAllDayToTimed(payload: DragMouseEnterColumnEventPayload): void {
|
||||
public handleConvertAllDayToTimed(payload: IDragMouseEnterColumnEventPayload): void {
|
||||
|
||||
console.log('🎯 DateEventRenderer: Converting all-day to timed event', {
|
||||
eventId: payload.calendarEvent.id,
|
||||
|
|
@ -165,7 +165,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Handle drag end event
|
||||
*/
|
||||
public handleDragEnd(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: ColumnBounds, finalY: number): void {
|
||||
public handleDragEnd(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void {
|
||||
if (!draggedClone || !originalElement) {
|
||||
console.warn('Missing draggedClone or originalElement');
|
||||
return;
|
||||
|
|
@ -209,7 +209,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
}
|
||||
|
||||
|
||||
renderEvents(events: CalendarEvent[], container: HTMLElement): void {
|
||||
renderEvents(events: ICalendarEvent[], container: HTMLElement): void {
|
||||
// Filter out all-day events - they should be handled by AllDayEventRenderer
|
||||
const timedEvents = events.filter(event => !event.allDay);
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Render events in a column using combined stacking + grid algorithm
|
||||
*/
|
||||
private renderColumnEvents(columnEvents: CalendarEvent[], eventsLayer: HTMLElement): void {
|
||||
private renderColumnEvents(columnEvents: ICalendarEvent[], eventsLayer: HTMLElement): void {
|
||||
if (columnEvents.length === 0) return;
|
||||
|
||||
// Get layout from coordinator
|
||||
|
|
@ -251,7 +251,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Render events in a grid container (side-by-side with column sharing)
|
||||
*/
|
||||
private renderGridGroup(gridGroup: GridGroupLayout, eventsLayer: HTMLElement): void {
|
||||
private renderGridGroup(gridGroup: IGridGroupLayout, eventsLayer: HTMLElement): void {
|
||||
const groupElement = document.createElement('swp-event-group');
|
||||
|
||||
// Add grid column class based on number of columns (not events)
|
||||
|
|
@ -275,7 +275,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
|
||||
// Render each column
|
||||
const earliestEvent = gridGroup.events[0];
|
||||
gridGroup.columns.forEach(columnEvents => {
|
||||
gridGroup.columns.forEach((columnEvents: ICalendarEvent[]) => {
|
||||
const columnContainer = this.renderGridColumn(columnEvents, earliestEvent.start);
|
||||
groupElement.appendChild(columnContainer);
|
||||
});
|
||||
|
|
@ -287,7 +287,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
* Render a single column within a grid group
|
||||
* Column may contain multiple events that don't overlap
|
||||
*/
|
||||
private renderGridColumn(columnEvents: CalendarEvent[], containerStart: Date): HTMLElement {
|
||||
private renderGridColumn(columnEvents: ICalendarEvent[], containerStart: Date): HTMLElement {
|
||||
const columnContainer = document.createElement('div');
|
||||
columnContainer.style.position = 'relative';
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
/**
|
||||
* Render event within a grid container (absolute positioning within column)
|
||||
*/
|
||||
private renderEventInGrid(event: CalendarEvent, containerStart: Date): HTMLElement {
|
||||
private renderEventInGrid(event: ICalendarEvent, containerStart: Date): HTMLElement {
|
||||
const element = SwpEventElement.fromCalendarEvent(event);
|
||||
|
||||
// Calculate event height
|
||||
|
|
@ -326,7 +326,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
}
|
||||
|
||||
|
||||
private renderEvent(event: CalendarEvent): HTMLElement {
|
||||
private renderEvent(event: ICalendarEvent): HTMLElement {
|
||||
const element = SwpEventElement.fromCalendarEvent(event);
|
||||
|
||||
// Apply positioning (moved from SwpEventElement.applyPositioning)
|
||||
|
|
@ -340,7 +340,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
return element;
|
||||
}
|
||||
|
||||
protected calculateEventPosition(event: CalendarEvent): { top: number; height: number } {
|
||||
protected calculateEventPosition(event: ICalendarEvent): { top: number; height: number } {
|
||||
// Delegate to PositionUtils for centralized position calculation
|
||||
return this.positionUtils.calculateEventPosition(event.start, event.end);
|
||||
}
|
||||
|
|
@ -366,7 +366,7 @@ export class DateEventRenderer implements IEventRenderer {
|
|||
return Array.from(columns) as HTMLElement[];
|
||||
}
|
||||
|
||||
protected getEventsForColumn(column: HTMLElement, events: CalendarEvent[]): CalendarEvent[] {
|
||||
protected getEventsForColumn(column: HTMLElement, events: ICalendarEvent[]): ICalendarEvent[] {
|
||||
const columnDate = column.dataset.date;
|
||||
if (!columnDate) {
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { EventBus } from '../core/EventBus';
|
||||
import { IEventBus, CalendarEvent, RenderContext } from '../types/CalendarTypes';
|
||||
import { IEventBus, ICalendarEvent, IRenderContext } from '../types/CalendarTypes';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
import { EventManager } from '../managers/EventManager';
|
||||
import { IEventRenderer } from './EventRenderer';
|
||||
import { SwpEventElement } from '../elements/SwpEventElement';
|
||||
import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, DragMouseEnterColumnEventPayload, DragColumnChangeEventPayload, HeaderReadyEventPayload, ResizeEndEventPayload } from '../types/EventTypes';
|
||||
import { IDragStartEventPayload, IDragMoveEventPayload, IDragEndEventPayload, IDragMouseEnterHeaderEventPayload, IDragMouseLeaveHeaderEventPayload, IDragMouseEnterColumnEventPayload, IDragColumnChangeEventPayload, IHeaderReadyEventPayload, IResizeEndEventPayload } from '../types/EventTypes';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { ColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
import { IColumnBounds } from '../utils/ColumnDetectionUtils';
|
||||
/**
|
||||
* EventRenderingService - Render events i DOM med positionering using Strategy Pattern
|
||||
* Håndterer event positioning og overlap detection
|
||||
|
|
@ -36,7 +36,7 @@ export class EventRenderingService {
|
|||
/**
|
||||
* Render events in a specific container for a given period
|
||||
*/
|
||||
public renderEvents(context: RenderContext): void {
|
||||
public renderEvents(context: IRenderContext): void {
|
||||
// Clear existing events in the specific container first
|
||||
this.strategy.clearEvents(context.container);
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ export class EventRenderingService {
|
|||
|
||||
private setupDragStartListener(): void {
|
||||
this.eventBus.on('drag:start', (event: Event) => {
|
||||
const dragStartPayload = (event as CustomEvent<DragStartEventPayload>).detail;
|
||||
const dragStartPayload = (event as CustomEvent<IDragStartEventPayload>).detail;
|
||||
|
||||
if (dragStartPayload.originalElement.hasAttribute('data-allday')) {
|
||||
return;
|
||||
|
|
@ -147,7 +147,7 @@ export class EventRenderingService {
|
|||
|
||||
private setupDragMoveListener(): void {
|
||||
this.eventBus.on('drag:move', (event: Event) => {
|
||||
let dragEvent = (event as CustomEvent<DragMoveEventPayload>).detail;
|
||||
let dragEvent = (event as CustomEvent<IDragMoveEventPayload>).detail;
|
||||
|
||||
if (dragEvent.draggedClone.hasAttribute('data-allday')) {
|
||||
return;
|
||||
|
|
@ -161,7 +161,7 @@ export class EventRenderingService {
|
|||
private setupDragEndListener(): void {
|
||||
this.eventBus.on('drag:end', (event: Event) => {
|
||||
|
||||
const { originalElement: draggedElement, sourceColumn, finalPosition, target } = (event as CustomEvent<DragEndEventPayload>).detail;
|
||||
const { originalElement: draggedElement, sourceColumn, finalPosition, target } = (event as CustomEvent<IDragEndEventPayload>).detail;
|
||||
const finalColumn = finalPosition.column;
|
||||
const finalY = finalPosition.snappedY;
|
||||
const eventId = draggedElement.dataset.eventId || '';
|
||||
|
|
@ -207,7 +207,7 @@ export class EventRenderingService {
|
|||
|
||||
private setupDragColumnChangeListener(): void {
|
||||
this.eventBus.on('drag:column-change', (event: Event) => {
|
||||
let columnChangeEvent = (event as CustomEvent<DragColumnChangeEventPayload>).detail;
|
||||
let columnChangeEvent = (event as CustomEvent<IDragColumnChangeEventPayload>).detail;
|
||||
|
||||
// Filter: Only handle events where clone is NOT an all-day event (normal timed events)
|
||||
if (columnChangeEvent.draggedClone && columnChangeEvent.draggedClone.hasAttribute('data-allday')) {
|
||||
|
|
@ -223,7 +223,7 @@ export class EventRenderingService {
|
|||
private setupDragMouseLeaveHeaderListener(): void {
|
||||
|
||||
this.dragMouseLeaveHeaderListener = (event: Event) => {
|
||||
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<DragMouseLeaveHeaderEventPayload>).detail;
|
||||
const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = (event as CustomEvent<IDragMouseLeaveHeaderEventPayload>).detail;
|
||||
|
||||
if (cloneElement)
|
||||
cloneElement.style.display = '';
|
||||
|
|
@ -241,7 +241,7 @@ export class EventRenderingService {
|
|||
|
||||
private setupDragMouseEnterColumnListener(): void {
|
||||
this.eventBus.on('drag:mouseenter-column', (event: Event) => {
|
||||
const payload = (event as CustomEvent<DragMouseEnterColumnEventPayload>).detail;
|
||||
const payload = (event as CustomEvent<IDragMouseEnterColumnEventPayload>).detail;
|
||||
|
||||
// Only handle if clone is an all-day event
|
||||
if (!payload.draggedClone.hasAttribute('data-allday')) {
|
||||
|
|
@ -263,7 +263,7 @@ export class EventRenderingService {
|
|||
|
||||
private setupResizeEndListener(): void {
|
||||
this.eventBus.on('resize:end', (event: Event) => {
|
||||
const { eventId, element } = (event as CustomEvent<ResizeEndEventPayload>).detail;
|
||||
const { eventId, element } = (event as CustomEvent<IResizeEndEventPayload>).detail;
|
||||
|
||||
// Update event data in EventManager with new end time from resized element
|
||||
const swpEvent = element as SwpEventElement;
|
||||
|
|
@ -306,7 +306,7 @@ export class EventRenderingService {
|
|||
/**
|
||||
* Re-render affected columns after drag to recalculate stacking/grouping
|
||||
*/
|
||||
private reRenderAffectedColumns(sourceColumn: ColumnBounds | null, targetColumn: ColumnBounds | null): void {
|
||||
private reRenderAffectedColumns(sourceColumn: IColumnBounds | null, targetColumn: IColumnBounds | null): void {
|
||||
const columnsToRender = new Set<string>();
|
||||
|
||||
// Add source column if exists
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { CalendarView } from '../types/CalendarTypes';
|
||||
import { ColumnRenderer, ColumnRenderContext } from './ColumnRenderer';
|
||||
import { IColumnRenderer, IColumnRenderContext } from './ColumnRenderer';
|
||||
import { eventBus } from '../core/EventBus';
|
||||
import { DateService } from '../utils/DateService';
|
||||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
|
|
@ -82,13 +82,13 @@ export class GridRenderer {
|
|||
private cachedGridContainer: HTMLElement | null = null;
|
||||
private cachedTimeAxis: HTMLElement | null = null;
|
||||
private dateService: DateService;
|
||||
private columnRenderer: ColumnRenderer;
|
||||
private config: CalendarConfig;
|
||||
private columnRenderer: IColumnRenderer;
|
||||
private config: Configuration;
|
||||
|
||||
constructor(
|
||||
columnRenderer: ColumnRenderer,
|
||||
columnRenderer: IColumnRenderer,
|
||||
dateService: DateService,
|
||||
config: CalendarConfig
|
||||
config: Configuration
|
||||
) {
|
||||
this.dateService = dateService;
|
||||
this.columnRenderer = columnRenderer;
|
||||
|
|
@ -255,7 +255,7 @@ export class GridRenderer {
|
|||
currentDate: Date,
|
||||
view: CalendarView
|
||||
): void {
|
||||
const context: ColumnRenderContext = {
|
||||
const context: IColumnRenderContext = {
|
||||
currentWeek: currentDate, // ColumnRenderer expects currentWeek property
|
||||
config: this.config
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
|
||||
/**
|
||||
* IEventRepository - Interface for event data loading
|
||||
|
|
@ -13,8 +13,8 @@ import { CalendarEvent } from '../types/CalendarTypes';
|
|||
export interface IEventRepository {
|
||||
/**
|
||||
* Load all calendar events from the data source
|
||||
* @returns Promise resolving to array of CalendarEvent objects
|
||||
* @returns Promise resolving to array of ICalendarEvent objects
|
||||
* @throws Error if loading fails
|
||||
*/
|
||||
loadEvents(): Promise<CalendarEvent[]>;
|
||||
loadEvents(): Promise<ICalendarEvent[]>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
import { IEventRepository } from './IEventRepository';
|
||||
|
||||
interface RawEventData {
|
||||
|
|
@ -23,7 +23,7 @@ interface RawEventData {
|
|||
export class MockEventRepository implements IEventRepository {
|
||||
private readonly dataUrl = 'data/mock-events.json';
|
||||
|
||||
public async loadEvents(): Promise<CalendarEvent[]> {
|
||||
public async loadEvents(): Promise<ICalendarEvent[]> {
|
||||
try {
|
||||
const response = await fetch(this.dataUrl);
|
||||
|
||||
|
|
@ -40,8 +40,8 @@ export class MockEventRepository implements IEventRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private processCalendarData(data: RawEventData[]): CalendarEvent[] {
|
||||
return data.map((event): CalendarEvent => ({
|
||||
private processCalendarData(data: RawEventData[]): ICalendarEvent[] {
|
||||
return data.map((event): ICalendarEvent => ({
|
||||
...event,
|
||||
start: new Date(event.start),
|
||||
end: new Date(event.end),
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ export type CalendarView = ViewPeriod;
|
|||
|
||||
export type SyncStatus = 'synced' | 'pending' | 'error';
|
||||
|
||||
export interface RenderContext {
|
||||
export interface IRenderContext {
|
||||
container: HTMLElement;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
|
||||
export interface CalendarEvent {
|
||||
export interface ICalendarEvent {
|
||||
id: string;
|
||||
title: string;
|
||||
start: Date;
|
||||
|
|
@ -55,13 +55,13 @@ export interface ICalendarConfig {
|
|||
maxEventDuration: number; // Minutes
|
||||
}
|
||||
|
||||
export interface EventLogEntry {
|
||||
export interface IEventLogEntry {
|
||||
type: string;
|
||||
detail: unknown;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface ListenerEntry {
|
||||
export interface IListenerEntry {
|
||||
eventType: string;
|
||||
handler: EventListener;
|
||||
options?: AddEventListenerOptions;
|
||||
|
|
@ -72,6 +72,6 @@ export interface IEventBus {
|
|||
once(eventType: string, handler: EventListener): () => void;
|
||||
off(eventType: string, handler: EventListener): void;
|
||||
emit(eventType: string, detail?: unknown): boolean;
|
||||
getEventLog(eventType?: string): EventLogEntry[];
|
||||
getEventLog(eventType?: string): IEventLogEntry[];
|
||||
setDebug(enabled: boolean): void;
|
||||
}
|
||||
|
|
@ -2,46 +2,46 @@
|
|||
* Type definitions for drag and drop functionality
|
||||
*/
|
||||
|
||||
export interface MousePosition {
|
||||
export interface IMousePosition {
|
||||
x: number;
|
||||
y: number;
|
||||
clientX?: number;
|
||||
clientY?: number;
|
||||
}
|
||||
|
||||
export interface DragOffset {
|
||||
export interface IDragOffset {
|
||||
x: number;
|
||||
y: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
||||
export interface DragState {
|
||||
export interface IDragState {
|
||||
isDragging: boolean;
|
||||
draggedElement: HTMLElement | null;
|
||||
draggedClone: HTMLElement | null;
|
||||
eventId: string | null;
|
||||
startColumn: string | null;
|
||||
currentColumn: string | null;
|
||||
mouseOffset: DragOffset;
|
||||
mouseOffset: IDragOffset;
|
||||
}
|
||||
|
||||
export interface DragEndPosition {
|
||||
export interface IDragEndPosition {
|
||||
column: string;
|
||||
y: number;
|
||||
snappedY: number;
|
||||
time?: Date;
|
||||
}
|
||||
|
||||
export interface StackLinkData {
|
||||
export interface IStackLinkData {
|
||||
prev?: string;
|
||||
next?: string;
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
}
|
||||
|
||||
export interface DragEventHandlers {
|
||||
handleDragStart?(originalElement: HTMLElement, eventId: string, mouseOffset: DragOffset, column: string): void;
|
||||
handleDragMove?(eventId: string, snappedY: number, column: string, mouseOffset: DragOffset): void;
|
||||
export interface IDragEventHandlers {
|
||||
handleDragStart?(originalElement: HTMLElement, eventId: string, mouseOffset: IDragOffset, column: string): void;
|
||||
handleDragMove?(eventId: string, snappedY: number, column: string, mouseOffset: IDragOffset): void;
|
||||
handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: string, finalY: number): void;
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
* Type definitions for calendar events and drag operations
|
||||
*/
|
||||
|
||||
import { ColumnBounds } from "../utils/ColumnDetectionUtils";
|
||||
import { CalendarEvent } from "./CalendarTypes";
|
||||
import { IColumnBounds } from "../utils/ColumnDetectionUtils";
|
||||
import { ICalendarEvent } from "./CalendarTypes";
|
||||
|
||||
/**
|
||||
* Drag Event Payload Interfaces
|
||||
|
|
@ -11,89 +11,89 @@ import { CalendarEvent } from "./CalendarTypes";
|
|||
*/
|
||||
|
||||
// Common position interface
|
||||
export interface MousePosition {
|
||||
export interface IMousePosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
// Drag start event payload
|
||||
export interface DragStartEventPayload {
|
||||
export interface IDragStartEventPayload {
|
||||
originalElement: HTMLElement;
|
||||
draggedClone: HTMLElement | null;
|
||||
mousePosition: MousePosition;
|
||||
mouseOffset: MousePosition;
|
||||
columnBounds: ColumnBounds | null;
|
||||
mousePosition: IMousePosition;
|
||||
mouseOffset: IMousePosition;
|
||||
columnBounds: IColumnBounds | null;
|
||||
}
|
||||
|
||||
// Drag move event payload
|
||||
export interface DragMoveEventPayload {
|
||||
export interface IDragMoveEventPayload {
|
||||
originalElement: HTMLElement;
|
||||
draggedClone: HTMLElement;
|
||||
mousePosition: MousePosition;
|
||||
mouseOffset: MousePosition;
|
||||
columnBounds: ColumnBounds | null;
|
||||
mousePosition: IMousePosition;
|
||||
mouseOffset: IMousePosition;
|
||||
columnBounds: IColumnBounds | null;
|
||||
snappedY: number;
|
||||
}
|
||||
|
||||
// Drag end event payload
|
||||
export interface DragEndEventPayload {
|
||||
export interface IDragEndEventPayload {
|
||||
originalElement: HTMLElement;
|
||||
draggedClone: HTMLElement | null;
|
||||
mousePosition: MousePosition;
|
||||
sourceColumn: ColumnBounds;
|
||||
mousePosition: IMousePosition;
|
||||
sourceColumn: IColumnBounds;
|
||||
finalPosition: {
|
||||
column: ColumnBounds | null; // Where drag ended
|
||||
column: IColumnBounds | null; // Where drag ended
|
||||
snappedY: number;
|
||||
};
|
||||
target: 'swp-day-column' | 'swp-day-header' | null;
|
||||
}
|
||||
|
||||
// Drag mouse enter header event payload
|
||||
export interface DragMouseEnterHeaderEventPayload {
|
||||
targetColumn: ColumnBounds;
|
||||
mousePosition: MousePosition;
|
||||
export interface IDragMouseEnterHeaderEventPayload {
|
||||
targetColumn: IColumnBounds;
|
||||
mousePosition: IMousePosition;
|
||||
originalElement: HTMLElement | null;
|
||||
draggedClone: HTMLElement;
|
||||
calendarEvent: CalendarEvent;
|
||||
calendarEvent: ICalendarEvent;
|
||||
replaceClone: (newClone: HTMLElement) => void;
|
||||
}
|
||||
|
||||
// Drag mouse leave header event payload
|
||||
export interface DragMouseLeaveHeaderEventPayload {
|
||||
export interface IDragMouseLeaveHeaderEventPayload {
|
||||
targetDate: string | null;
|
||||
mousePosition: MousePosition;
|
||||
mousePosition: IMousePosition;
|
||||
originalElement: HTMLElement| null;
|
||||
draggedClone: HTMLElement| null;
|
||||
}
|
||||
|
||||
// Drag mouse enter column event payload
|
||||
export interface DragMouseEnterColumnEventPayload {
|
||||
targetColumn: ColumnBounds;
|
||||
mousePosition: MousePosition;
|
||||
export interface IDragMouseEnterColumnEventPayload {
|
||||
targetColumn: IColumnBounds;
|
||||
mousePosition: IMousePosition;
|
||||
snappedY: number;
|
||||
originalElement: HTMLElement | null;
|
||||
draggedClone: HTMLElement;
|
||||
calendarEvent: CalendarEvent;
|
||||
calendarEvent: ICalendarEvent;
|
||||
replaceClone: (newClone: HTMLElement) => void;
|
||||
}
|
||||
|
||||
// Drag column change event payload
|
||||
export interface DragColumnChangeEventPayload {
|
||||
export interface IDragColumnChangeEventPayload {
|
||||
originalElement: HTMLElement;
|
||||
draggedClone: HTMLElement;
|
||||
previousColumn: ColumnBounds | null;
|
||||
newColumn: ColumnBounds;
|
||||
mousePosition: MousePosition;
|
||||
previousColumn: IColumnBounds | null;
|
||||
newColumn: IColumnBounds;
|
||||
mousePosition: IMousePosition;
|
||||
}
|
||||
|
||||
// Header ready event payload
|
||||
export interface HeaderReadyEventPayload {
|
||||
headerElements: ColumnBounds[];
|
||||
export interface IHeaderReadyEventPayload {
|
||||
headerElements: IColumnBounds[];
|
||||
|
||||
}
|
||||
|
||||
// Resize end event payload
|
||||
export interface ResizeEndEventPayload {
|
||||
export interface IResizeEndEventPayload {
|
||||
eventId: string;
|
||||
element: HTMLElement;
|
||||
finalHeight: number;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import { IEventBus, CalendarEvent, CalendarView } from './CalendarTypes';
|
||||
import { IEventBus, ICalendarEvent, CalendarView } from './CalendarTypes';
|
||||
|
||||
/**
|
||||
* Complete type definition for all managers returned by ManagerFactory
|
||||
*/
|
||||
export interface CalendarManagers {
|
||||
eventManager: EventManager;
|
||||
eventRenderer: EventRenderingService;
|
||||
gridManager: GridManager;
|
||||
scrollManager: ScrollManager;
|
||||
export interface ICalendarManagers {
|
||||
eventManager: IEventManager;
|
||||
eventRenderer: IEventRenderingService;
|
||||
gridManager: IGridManager;
|
||||
scrollManager: IScrollManager;
|
||||
navigationManager: unknown; // Avoid interface conflicts
|
||||
viewManager: ViewManager;
|
||||
calendarManager: CalendarManager;
|
||||
viewManager: IViewManager;
|
||||
calendarManager: ICalendarManager;
|
||||
dragDropManager: unknown; // Avoid interface conflicts
|
||||
allDayManager: unknown; // Avoid interface conflicts
|
||||
resizeHandleManager: ResizeHandleManager;
|
||||
resizeHandleManager: IResizeHandleManager;
|
||||
edgeScrollManager: unknown; // Avoid interface conflicts
|
||||
dragHoverManager: unknown; // Avoid interface conflicts
|
||||
headerManager: unknown; // Avoid interface conflicts
|
||||
|
|
@ -27,50 +27,50 @@ interface IManager {
|
|||
refresh?(): void;
|
||||
}
|
||||
|
||||
export interface EventManager extends IManager {
|
||||
export interface IEventManager extends IManager {
|
||||
loadData(): Promise<void>;
|
||||
getEvents(): CalendarEvent[];
|
||||
getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[];
|
||||
getEvents(): ICalendarEvent[];
|
||||
getEventsForPeriod(startDate: Date, endDate: Date): ICalendarEvent[];
|
||||
navigateToEvent(eventId: string): boolean;
|
||||
}
|
||||
|
||||
export interface EventRenderingService extends IManager {
|
||||
export interface IEventRenderingService extends IManager {
|
||||
// EventRenderingService doesn't have a render method in current implementation
|
||||
}
|
||||
|
||||
export interface GridManager extends IManager {
|
||||
export interface IGridManager extends IManager {
|
||||
render(): Promise<void>;
|
||||
getDisplayDates(): Date[];
|
||||
}
|
||||
|
||||
export interface ScrollManager extends IManager {
|
||||
export interface IScrollManager extends IManager {
|
||||
scrollTo(scrollTop: number): void;
|
||||
scrollToHour(hour: number): void;
|
||||
}
|
||||
|
||||
// Use a more flexible interface that matches actual implementation
|
||||
export interface NavigationManager extends IManager {
|
||||
export interface INavigationManager extends IManager {
|
||||
[key: string]: unknown; // Allow any properties from actual implementation
|
||||
}
|
||||
|
||||
export interface ViewManager extends IManager {
|
||||
export interface IViewManager extends IManager {
|
||||
// ViewManager doesn't have setView in current implementation
|
||||
getCurrentView?(): CalendarView;
|
||||
}
|
||||
|
||||
export interface CalendarManager extends IManager {
|
||||
export interface ICalendarManager extends IManager {
|
||||
setView(view: CalendarView): void;
|
||||
setCurrentDate(date: Date): void;
|
||||
}
|
||||
|
||||
export interface DragDropManager extends IManager {
|
||||
export interface IDragDropManager extends IManager {
|
||||
// DragDropManager has different interface in current implementation
|
||||
}
|
||||
|
||||
export interface AllDayManager extends IManager {
|
||||
export interface IAllDayManager extends IManager {
|
||||
[key: string]: unknown; // Allow any properties from actual implementation
|
||||
}
|
||||
|
||||
export interface ResizeHandleManager extends IManager {
|
||||
export interface IResizeHandleManager extends IManager {
|
||||
// ResizeHandleManager handles hover effects for resize handles
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { CalendarEvent } from '../types/CalendarTypes';
|
||||
import { ICalendarEvent } from '../types/CalendarTypes';
|
||||
|
||||
export interface EventLayout {
|
||||
calenderEvent: CalendarEvent;
|
||||
export interface IEventLayout {
|
||||
calenderEvent: ICalendarEvent;
|
||||
gridArea: string; // "row-start / col-start / row-end / col-end"
|
||||
startColumn: number;
|
||||
endColumn: number;
|
||||
|
|
@ -21,9 +21,9 @@ export class AllDayLayoutEngine {
|
|||
/**
|
||||
* Calculate layout for all events using clean day-based logic
|
||||
*/
|
||||
public calculateLayout(events: CalendarEvent[]): EventLayout[] {
|
||||
public calculateLayout(events: ICalendarEvent[]): IEventLayout[] {
|
||||
|
||||
let layouts: EventLayout[] = [];
|
||||
let layouts: IEventLayout[] = [];
|
||||
// Reset tracks for new calculation
|
||||
this.tracks = [new Array(this.weekDates.length).fill(false)];
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ export class AllDayLayoutEngine {
|
|||
this.tracks[track][day] = true;
|
||||
}
|
||||
|
||||
const layout: EventLayout = {
|
||||
const layout: IEventLayout = {
|
||||
calenderEvent: event,
|
||||
gridArea: `${track + 1} / ${startDay} / ${track + 2} / ${endDay + 1}`,
|
||||
startColumn: startDay,
|
||||
|
|
@ -89,7 +89,7 @@ export class AllDayLayoutEngine {
|
|||
/**
|
||||
* Get start day index for event (1-based, 0 if not visible)
|
||||
*/
|
||||
private getEventStartDay(event: CalendarEvent): number {
|
||||
private getEventStartDay(event: ICalendarEvent): number {
|
||||
const eventStartDate = this.formatDate(event.start);
|
||||
const firstVisibleDate = this.weekDates[0];
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ export class AllDayLayoutEngine {
|
|||
/**
|
||||
* Get end day index for event (1-based, 0 if not visible)
|
||||
*/
|
||||
private getEventEndDay(event: CalendarEvent): number {
|
||||
private getEventEndDay(event: ICalendarEvent): number {
|
||||
const eventEndDate = this.formatDate(event.end);
|
||||
const lastVisibleDate = this.weekDates[this.weekDates.length - 1];
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ export class AllDayLayoutEngine {
|
|||
/**
|
||||
* Check if event is visible in the current date range
|
||||
*/
|
||||
private isEventVisible(event: CalendarEvent): boolean {
|
||||
private isEventVisible(event: ICalendarEvent): boolean {
|
||||
if (this.weekDates.length === 0) return false;
|
||||
|
||||
const eventStartDate = this.formatDate(event.start);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
* Used by both DragDropManager and AllDayManager for consistent column detection
|
||||
*/
|
||||
|
||||
import { MousePosition } from "../types/DragDropTypes";
|
||||
import { IMousePosition } from "../types/DragDropTypes";
|
||||
|
||||
|
||||
export interface ColumnBounds {
|
||||
export interface IColumnBounds {
|
||||
date: string;
|
||||
left: number;
|
||||
right: number;
|
||||
|
|
@ -16,7 +16,7 @@ export interface ColumnBounds {
|
|||
}
|
||||
|
||||
export class ColumnDetectionUtils {
|
||||
private static columnBoundsCache: ColumnBounds[] = [];
|
||||
private static columnBoundsCache: IColumnBounds[] = [];
|
||||
|
||||
/**
|
||||
* Update column bounds cache for coordinate-based column detection
|
||||
|
|
@ -52,7 +52,7 @@ export class ColumnDetectionUtils {
|
|||
/**
|
||||
* Get column date from X coordinate using cached bounds
|
||||
*/
|
||||
public static getColumnBounds(position: MousePosition): ColumnBounds | null{
|
||||
public static getColumnBounds(position: IMousePosition): IColumnBounds | null{
|
||||
if (this.columnBoundsCache.length === 0) {
|
||||
this.updateColumnBoundsCache();
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ export class ColumnDetectionUtils {
|
|||
/**
|
||||
* Get column bounds by Date
|
||||
*/
|
||||
public static getColumnBoundsByDate(date: Date): ColumnBounds | null {
|
||||
public static getColumnBoundsByDate(date: Date): IColumnBounds | null {
|
||||
if (this.columnBoundsCache.length === 0) {
|
||||
this.updateColumnBoundsCache();
|
||||
}
|
||||
|
|
@ -84,12 +84,12 @@ export class ColumnDetectionUtils {
|
|||
}
|
||||
|
||||
|
||||
public static getColumns(): ColumnBounds[] {
|
||||
public static getColumns(): IColumnBounds[] {
|
||||
return [...this.columnBoundsCache];
|
||||
}
|
||||
public static getHeaderColumns(): ColumnBounds[] {
|
||||
public static getHeaderColumns(): IColumnBounds[] {
|
||||
|
||||
let dayHeaders: ColumnBounds[] = [];
|
||||
let dayHeaders: IColumnBounds[] = [];
|
||||
|
||||
const dayColumns = document.querySelectorAll('swp-calendar-header swp-day-header');
|
||||
let index = 1;
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ import {
|
|||
fromZonedTime,
|
||||
formatInTimeZone
|
||||
} from 'date-fns-tz';
|
||||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
|
||||
export class DateService {
|
||||
private timezone: string;
|
||||
|
||||
constructor(config: CalendarConfig) {
|
||||
constructor(config: Configuration) {
|
||||
this.timezone = config.getTimezone();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { CalendarConfig } from '../core/CalendarConfig';
|
||||
import { ColumnBounds } from './ColumnDetectionUtils';
|
||||
import { Configuration } from '../configuration/CalendarConfig';
|
||||
import { IColumnBounds } from './ColumnDetectionUtils';
|
||||
import { DateService } from './DateService';
|
||||
import { TimeFormatter } from './TimeFormatter';
|
||||
|
||||
|
|
@ -11,9 +11,9 @@ import { TimeFormatter } from './TimeFormatter';
|
|||
*/
|
||||
export class PositionUtils {
|
||||
private dateService: DateService;
|
||||
private config: CalendarConfig;
|
||||
private config: Configuration;
|
||||
|
||||
constructor(dateService: DateService, config: CalendarConfig) {
|
||||
constructor(dateService: DateService, config: Configuration) {
|
||||
this.dateService = dateService;
|
||||
this.config = config;
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ export class PositionUtils {
|
|||
/**
|
||||
* Beregn Y position fra mouse/touch koordinat
|
||||
*/
|
||||
public getPositionFromCoordinate(clientY: number, column: ColumnBounds): number {
|
||||
public getPositionFromCoordinate(clientY: number, column: IColumnBounds): number {
|
||||
|
||||
const relativeY = clientY - column.boundingClientRect.top;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import { DateService } from './DateService';
|
||||
|
||||
export interface TimeFormatSettings {
|
||||
export interface ITimeFormatSettings {
|
||||
timezone: string;
|
||||
use24HourFormat: boolean;
|
||||
locale: string;
|
||||
|
|
@ -19,7 +19,7 @@ export interface TimeFormatSettings {
|
|||
}
|
||||
|
||||
export class TimeFormatter {
|
||||
private static settings: TimeFormatSettings = {
|
||||
private static settings: ITimeFormatSettings = {
|
||||
timezone: 'Europe/Copenhagen', // Default to Denmark
|
||||
use24HourFormat: true, // 24-hour format standard in Denmark
|
||||
locale: 'da-DK', // Danish locale
|
||||
|
|
@ -44,7 +44,7 @@ export class TimeFormatter {
|
|||
/**
|
||||
* Configure time formatting settings
|
||||
*/
|
||||
static configure(settings: Partial<TimeFormatSettings>): void {
|
||||
static configure(settings: Partial<ITimeFormatSettings>): void {
|
||||
TimeFormatter.settings = { ...TimeFormatter.settings, ...settings };
|
||||
// Reset DateService to pick up new timezone
|
||||
TimeFormatter.dateService = null;
|
||||
|
|
|
|||
87
wwwroot/data/calendar-config.json
Normal file
87
wwwroot/data/calendar-config.json
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"gridSettings": {
|
||||
"hourHeight": 80,
|
||||
"dayStartHour": 6,
|
||||
"dayEndHour": 22,
|
||||
"workStartHour": 8,
|
||||
"workEndHour": 17,
|
||||
"snapInterval": 15,
|
||||
"gridStartThresholdMinutes": 30,
|
||||
"showCurrentTime": true,
|
||||
"showWorkHours": true,
|
||||
"fitToWidth": false,
|
||||
"scrollToHour": 8
|
||||
},
|
||||
"dateViewSettings": {
|
||||
"period": "week",
|
||||
"weekDays": 7,
|
||||
"firstDayOfWeek": 1,
|
||||
"showAllDay": true
|
||||
},
|
||||
"timeFormatConfig": {
|
||||
"timezone": "Europe/Copenhagen",
|
||||
"use24HourFormat": true,
|
||||
"locale": "da-DK",
|
||||
"dateFormat": "technical",
|
||||
"showSeconds": false
|
||||
},
|
||||
"workWeekPresets": {
|
||||
"standard": {
|
||||
"id": "standard",
|
||||
"workDays": [1, 2, 3, 4, 5],
|
||||
"totalDays": 5,
|
||||
"firstWorkDay": 1
|
||||
},
|
||||
"compressed": {
|
||||
"id": "compressed",
|
||||
"workDays": [1, 2, 3, 4],
|
||||
"totalDays": 4,
|
||||
"firstWorkDay": 1
|
||||
},
|
||||
"midweek": {
|
||||
"id": "midweek",
|
||||
"workDays": [3, 4, 5],
|
||||
"totalDays": 3,
|
||||
"firstWorkDay": 3
|
||||
},
|
||||
"weekend": {
|
||||
"id": "weekend",
|
||||
"workDays": [6, 7],
|
||||
"totalDays": 2,
|
||||
"firstWorkDay": 6
|
||||
},
|
||||
"fullweek": {
|
||||
"id": "fullweek",
|
||||
"workDays": [1, 2, 3, 4, 5, 6, 7],
|
||||
"totalDays": 7,
|
||||
"firstWorkDay": 1
|
||||
}
|
||||
},
|
||||
"currentWorkWeek": "standard",
|
||||
"scrollbar": {
|
||||
"width": 16,
|
||||
"color": "#666",
|
||||
"trackColor": "#f0f0f0",
|
||||
"hoverColor": "#b53f7aff",
|
||||
"borderRadius": 6
|
||||
},
|
||||
"interaction": {
|
||||
"allowDrag": true,
|
||||
"allowResize": true,
|
||||
"allowCreate": true
|
||||
},
|
||||
"api": {
|
||||
"endpoint": "/api/events",
|
||||
"dateFormat": "YYYY-MM-DD",
|
||||
"timeFormat": "HH:mm"
|
||||
},
|
||||
"features": {
|
||||
"enableSearch": true,
|
||||
"enableTouch": true
|
||||
},
|
||||
"eventDefaults": {
|
||||
"defaultEventDuration": 60,
|
||||
"minEventDuration": 15,
|
||||
"maxEventDuration": 480
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="calendar-wrapper">
|
||||
<swp-calendar data-view="week" data-week-days="7" data-snap-interval="15" data-day-start-hour="6" data-day-end-hour="22" data-hour-height="80">
|
||||
<swp-calendar>
|
||||
<!-- Navigation Bar -->
|
||||
<swp-calendar-nav>
|
||||
<swp-nav-group>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue