Some ignored filles was missing

This commit is contained in:
Janus C. H. Knudsen 2026-02-03 00:02:25 +01:00
parent 7db22245e2
commit fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions

26
wwwroot/js/calendar-min.js vendored Normal file

File diff suppressed because one or more lines are too long

1631
wwwroot/js/calendar-v2.js Normal file

File diff suppressed because one or more lines are too long

1664
wwwroot/js/calendar.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,63 @@
import { IEventBus } from '../types/CalendarTypes';
import { DateService } from '../utils/DateService';
import { Configuration } from '../configurations/CalendarConfig';
/**
* NavigationButtons - Manages navigation button UI and navigation logic
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-nav-group> UI element
* and performs the actual navigation calculations.
*
* RESPONSIBILITIES:
* - Handles button clicks on swp-nav-button elements
* - Validates navigation actions (prev, next, today)
* - Calculates next/previous dates based on current view
* - Emits NAVIGATION_COMPLETED events with new date
* - Manages button UI listeners
*
* EVENT FLOW:
* ===========
* User clicks button calculateNewDate() emit NAVIGATION_COMPLETED GridManager re-renders
*/
export declare class NavigationButtons {
private eventBus;
private buttonListeners;
private dateService;
private config;
private currentDate;
private currentView;
constructor(eventBus: IEventBus, dateService: DateService, config: Configuration);
/**
* Subscribe to events
*/
private subscribeToEvents;
/**
* Setup click listeners on all navigation buttons
*/
private setupButtonListeners;
/**
* Handle navigation action
*/
private handleNavigation;
/**
* Navigate in specified direction
*/
private navigate;
/**
* Navigate to next period
*/
private navigateNext;
/**
* Navigate to previous period
*/
private navigatePrevious;
/**
* Navigate to today
*/
private navigateToday;
/**
* Validate if string is a valid navigation action
*/
private isValidAction;
}

View file

@ -0,0 +1,131 @@
import { CoreEvents } from '../constants/CoreEvents';
/**
* NavigationButtons - Manages navigation button UI and navigation logic
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-nav-group> UI element
* and performs the actual navigation calculations.
*
* RESPONSIBILITIES:
* - Handles button clicks on swp-nav-button elements
* - Validates navigation actions (prev, next, today)
* - Calculates next/previous dates based on current view
* - Emits NAVIGATION_COMPLETED events with new date
* - Manages button UI listeners
*
* EVENT FLOW:
* ===========
* User clicks button calculateNewDate() emit NAVIGATION_COMPLETED GridManager re-renders
*/
export class NavigationButtons {
constructor(eventBus, dateService, config) {
this.buttonListeners = new Map();
this.currentDate = new Date();
this.currentView = 'week';
this.eventBus = eventBus;
this.dateService = dateService;
this.config = config;
this.setupButtonListeners();
this.subscribeToEvents();
}
/**
* Subscribe to events
*/
subscribeToEvents() {
// Listen for view changes
this.eventBus.on(CoreEvents.VIEW_CHANGED, (e) => {
const detail = e.detail;
this.currentView = detail.currentView;
});
}
/**
* Setup click listeners on all navigation buttons
*/
setupButtonListeners() {
const buttons = document.querySelectorAll('swp-nav-button[data-action]');
buttons.forEach(button => {
const clickHandler = (event) => {
event.preventDefault();
const action = button.getAttribute('data-action');
if (action && this.isValidAction(action)) {
this.handleNavigation(action);
}
};
button.addEventListener('click', clickHandler);
this.buttonListeners.set(button, clickHandler);
});
}
/**
* Handle navigation action
*/
handleNavigation(action) {
switch (action) {
case 'prev':
this.navigatePrevious();
break;
case 'next':
this.navigateNext();
break;
case 'today':
this.navigateToday();
break;
}
}
/**
* Navigate in specified direction
*/
navigate(direction) {
const offset = direction === 'next' ? 1 : -1;
let newDate;
switch (this.currentView) {
case 'week':
newDate = this.dateService.addWeeks(this.currentDate, offset);
break;
case 'month':
newDate = this.dateService.addMonths(this.currentDate, offset);
break;
case 'day':
newDate = this.dateService.addDays(this.currentDate, offset);
break;
default:
newDate = this.dateService.addWeeks(this.currentDate, offset);
}
this.currentDate = newDate;
const payload = {
direction: direction,
newDate: newDate
};
this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, payload);
}
/**
* Navigate to next period
*/
navigateNext() {
this.navigate('next');
}
/**
* Navigate to previous period
*/
navigatePrevious() {
this.navigate('previous');
}
/**
* Navigate to today
*/
navigateToday() {
this.currentDate = new Date();
const payload = {
direction: 'today',
newDate: this.currentDate
};
this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, payload);
}
/**
* Validate if string is a valid navigation action
*/
isValidAction(action) {
return ['prev', 'next', 'today'].includes(action);
}
}
//# sourceMappingURL=NavigationButtons.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"NavigationButtons.js","sourceRoot":"","sources":["../../../src/components/NavigationButtons.ts"],"names":[],"mappings":"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"}

70
wwwroot/js/components/ViewSelector.d.ts vendored Normal file
View file

@ -0,0 +1,70 @@
import { IEventBus } from '../types/CalendarTypes';
import { Configuration } from '../configurations/CalendarConfig';
/**
* ViewSelectorManager - Manages view selector UI and state
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-view-selector> UI element.
* It follows the principle that each functional UI element has its own manager.
*
* RESPONSIBILITIES:
* - Handles button clicks on swp-view-button elements
* - Manages current view state (day/week/month)
* - Validates view values
* - Emits VIEW_CHANGED and VIEW_RENDERED events
* - Updates button UI states (data-active attributes)
*
* EVENT FLOW:
* ===========
* User clicks button changeView() validate update state emit event update UI
*
* IMPLEMENTATION STATUS:
* ======================
* - Week view: FULLY IMPLEMENTED
* - Day view: NOT IMPLEMENTED (button exists but no rendering)
* - Month view: NOT IMPLEMENTED (button exists but no rendering)
*
* SUBSCRIBERS:
* ============
* - GridRenderer: Uses view parameter (currently only supports 'week')
* - Future: DayRenderer, MonthRenderer when implemented
*/
export declare class ViewSelector {
private eventBus;
private config;
private buttonListeners;
constructor(eventBus: IEventBus, config: Configuration);
/**
* Setup click listeners on all view selector buttons
*/
private setupButtonListeners;
/**
* Setup event bus listeners
*/
private setupEventListeners;
/**
* Change the active view
*/
private changeView;
/**
* Update button states (data-active attributes)
*/
private updateButtonStates;
/**
* Initialize view on INITIALIZED event
*/
private initializeView;
/**
* Emit VIEW_RENDERED event
*/
private emitViewRendered;
/**
* Refresh current view on DATE_CHANGED event
*/
private refreshCurrentView;
/**
* Validate if string is a valid CalendarView type
*/
private isValidView;
}

View file

@ -0,0 +1,130 @@
import { CoreEvents } from '../constants/CoreEvents';
/**
* ViewSelectorManager - Manages view selector UI and state
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-view-selector> UI element.
* It follows the principle that each functional UI element has its own manager.
*
* RESPONSIBILITIES:
* - Handles button clicks on swp-view-button elements
* - Manages current view state (day/week/month)
* - Validates view values
* - Emits VIEW_CHANGED and VIEW_RENDERED events
* - Updates button UI states (data-active attributes)
*
* EVENT FLOW:
* ===========
* User clicks button changeView() validate update state emit event update UI
*
* IMPLEMENTATION STATUS:
* ======================
* - Week view: FULLY IMPLEMENTED
* - Day view: NOT IMPLEMENTED (button exists but no rendering)
* - Month view: NOT IMPLEMENTED (button exists but no rendering)
*
* SUBSCRIBERS:
* ============
* - GridRenderer: Uses view parameter (currently only supports 'week')
* - Future: DayRenderer, MonthRenderer when implemented
*/
export class ViewSelector {
constructor(eventBus, config) {
this.buttonListeners = new Map();
this.eventBus = eventBus;
this.config = config;
this.setupButtonListeners();
this.setupEventListeners();
}
/**
* Setup click listeners on all view selector buttons
*/
setupButtonListeners() {
const buttons = document.querySelectorAll('swp-view-button[data-view]');
buttons.forEach(button => {
const clickHandler = (event) => {
event.preventDefault();
const view = button.getAttribute('data-view');
if (view && this.isValidView(view)) {
this.changeView(view);
}
};
button.addEventListener('click', clickHandler);
this.buttonListeners.set(button, clickHandler);
});
// Initialize button states
this.updateButtonStates();
}
/**
* Setup event bus listeners
*/
setupEventListeners() {
this.eventBus.on(CoreEvents.INITIALIZED, () => {
this.initializeView();
});
this.eventBus.on(CoreEvents.DATE_CHANGED, () => {
this.refreshCurrentView();
});
}
/**
* Change the active view
*/
changeView(newView) {
if (newView === this.config.currentView) {
return; // No change
}
const previousView = this.config.currentView;
this.config.currentView = newView;
// Update button UI states
this.updateButtonStates();
// Emit event for subscribers
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
previousView,
currentView: newView
});
}
/**
* Update button states (data-active attributes)
*/
updateButtonStates() {
const buttons = document.querySelectorAll('swp-view-button[data-view]');
buttons.forEach(button => {
const buttonView = button.getAttribute('data-view');
if (buttonView === this.config.currentView) {
button.setAttribute('data-active', 'true');
}
else {
button.removeAttribute('data-active');
}
});
}
/**
* Initialize view on INITIALIZED event
*/
initializeView() {
this.updateButtonStates();
this.emitViewRendered();
}
/**
* Emit VIEW_RENDERED event
*/
emitViewRendered() {
this.eventBus.emit(CoreEvents.VIEW_RENDERED, {
view: this.config.currentView
});
}
/**
* Refresh current view on DATE_CHANGED event
*/
refreshCurrentView() {
this.emitViewRendered();
}
/**
* Validate if string is a valid CalendarView type
*/
isValidView(view) {
return ['day', 'week', 'month'].includes(view);
}
}
//# sourceMappingURL=ViewSelector.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ViewSelector.js","sourceRoot":"","sources":["../../../src/components/ViewSelector.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,47 @@
import { IEventBus } from '../types/CalendarTypes';
import { Configuration } from '../configurations/CalendarConfig';
/**
* WorkweekPresetsManager - Manages workweek preset UI and state
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-workweek-presets> UI element.
* It follows the principle that each functional UI element has its own manager.
*
* RESPONSIBILITIES:
* - Owns WORK_WEEK_PRESETS data
* - Handles button clicks on swp-preset-button elements
* - Manages current workweek preset state
* - Validates preset IDs
* - Emits WORKWEEK_CHANGED events
* - Updates button UI states (data-active attributes)
*
* EVENT FLOW:
* ===========
* User clicks button changePreset() validate update state emit event update UI
*
* SUBSCRIBERS:
* ============
* - ConfigManager: Updates CSS variables (--grid-columns)
* - GridManager: Re-renders grid with new column count
* - CalendarManager: Relays to header update (via workweek:header-update)
* - HeaderManager: Updates date headers
*/
export declare class WorkweekPresets {
private eventBus;
private config;
private buttonListeners;
constructor(eventBus: IEventBus, config: Configuration);
/**
* Setup click listeners on all workweek preset buttons
*/
private setupButtonListeners;
/**
* Change the active workweek preset
*/
private changePreset;
/**
* Update button states (data-active attributes)
*/
private updateButtonStates;
}

View file

@ -0,0 +1,95 @@
import { CoreEvents } from '../constants/CoreEvents';
import { WORK_WEEK_PRESETS } from '../configurations/CalendarConfig';
/**
* WorkweekPresetsManager - Manages workweek preset UI and state
*
* RESPONSIBILITY:
* ===============
* This manager owns all logic related to the <swp-workweek-presets> UI element.
* It follows the principle that each functional UI element has its own manager.
*
* RESPONSIBILITIES:
* - Owns WORK_WEEK_PRESETS data
* - Handles button clicks on swp-preset-button elements
* - Manages current workweek preset state
* - Validates preset IDs
* - Emits WORKWEEK_CHANGED events
* - Updates button UI states (data-active attributes)
*
* EVENT FLOW:
* ===========
* User clicks button changePreset() validate update state emit event update UI
*
* SUBSCRIBERS:
* ============
* - ConfigManager: Updates CSS variables (--grid-columns)
* - GridManager: Re-renders grid with new column count
* - CalendarManager: Relays to header update (via workweek:header-update)
* - HeaderManager: Updates date headers
*/
export class WorkweekPresets {
constructor(eventBus, config) {
this.buttonListeners = new Map();
this.eventBus = eventBus;
this.config = config;
this.setupButtonListeners();
}
/**
* Setup click listeners on all workweek preset buttons
*/
setupButtonListeners() {
const buttons = document.querySelectorAll('swp-preset-button[data-workweek]');
buttons.forEach(button => {
const clickHandler = (event) => {
event.preventDefault();
const presetId = button.getAttribute('data-workweek');
if (presetId) {
this.changePreset(presetId);
}
};
button.addEventListener('click', clickHandler);
this.buttonListeners.set(button, clickHandler);
});
// Initialize button states
this.updateButtonStates();
}
/**
* Change the active workweek preset
*/
changePreset(presetId) {
if (!WORK_WEEK_PRESETS[presetId]) {
console.warn(`Invalid preset ID "${presetId}"`);
return;
}
if (presetId === this.config.currentWorkWeek) {
return; // No change
}
const previousPresetId = this.config.currentWorkWeek;
this.config.currentWorkWeek = presetId;
const settings = WORK_WEEK_PRESETS[presetId];
// Update button UI states
this.updateButtonStates();
// Emit event for subscribers
this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, {
workWeekId: presetId,
previousWorkWeekId: previousPresetId,
settings: settings
});
}
/**
* Update button states (data-active attributes)
*/
updateButtonStates() {
const buttons = document.querySelectorAll('swp-preset-button[data-workweek]');
buttons.forEach(button => {
const buttonPresetId = button.getAttribute('data-workweek');
if (buttonPresetId === this.config.currentWorkWeek) {
button.setAttribute('data-active', 'true');
}
else {
button.removeAttribute('data-active');
}
});
}
}
//# sourceMappingURL=WorkweekPresets.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"WorkweekPresets.js","sourceRoot":"","sources":["../../../src/components/WorkweekPresets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAiB,MAAM,kCAAkC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,eAAe;IAK1B,YAAY,QAAmB,EAAE,MAAqB;QAF9C,oBAAe,GAAgC,IAAI,GAAG,EAAE,CAAC;QAG/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,YAAY,GAAG,CAAC,KAAY,EAAE,EAAE;gBACpC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,QAAgB;QACnC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,sBAAsB,QAAQ,GAAG,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,OAAO,CAAC,YAAY;QACtB,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,QAAQ,CAAC;QAEvC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE7C,0BAA0B;QAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,6BAA6B;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE;YAC9C,UAAU,EAAE,QAAQ;YACpB,kBAAkB,EAAE,gBAAgB;YACpC,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;YAE5D,IAAI,cAAc,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;gBACnD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CAEF"}

View file

@ -0,0 +1,44 @@
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 declare const ALL_DAY_CONSTANTS: {
readonly EVENT_HEIGHT: 22;
readonly EVENT_GAP: 2;
readonly CONTAINER_PADDING: 4;
readonly MAX_COLLAPSED_ROWS: 4;
readonly SINGLE_ROW_HEIGHT: number;
};
/**
* Work week presets
*/
export declare const WORK_WEEK_PRESETS: {
[key: string]: IWorkWeekSettings;
};
/**
* Configuration - DTO container for all configuration
* Pure data object loaded from JSON via ConfigManager
*/
export declare class Configuration {
private static _instance;
config: ICalendarConfig;
gridSettings: IGridSettings;
dateViewSettings: IDateViewSettings;
timeFormatConfig: ITimeFormatConfig;
currentWorkWeek: string;
selectedDate: Date;
constructor(config: ICalendarConfig, gridSettings: IGridSettings, dateViewSettings: IDateViewSettings, timeFormatConfig: ITimeFormatConfig, currentWorkWeek: string, selectedDate?: Date);
/**
* Get the current Configuration instance
* Used by web components that can't use dependency injection
*/
static getInstance(): Configuration;
getWorkWeekSettings(): IWorkWeekSettings;
setWorkWeek(workWeekId: string): void;
setSelectedDate(date: Date): void;
}
export { Configuration as CalendarConfig };

View file

@ -0,0 +1,90 @@
/**
* 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
}
};
/**
* Work week presets
*/
export const WORK_WEEK_PRESETS = {
'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 {
constructor(config, gridSettings, dateViewSettings, timeFormatConfig, currentWorkWeek, selectedDate = 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
*/
static getInstance() {
if (!Configuration._instance) {
throw new Error('Configuration has not been initialized. Call ConfigManager.load() first.');
}
return Configuration._instance;
}
// Helper methods
getWorkWeekSettings() {
return WORK_WEEK_PRESETS[this.currentWorkWeek] || WORK_WEEK_PRESETS['standard'];
}
setWorkWeek(workWeekId) {
if (WORK_WEEK_PRESETS[workWeekId]) {
this.currentWorkWeek = workWeekId;
this.dateViewSettings.weekDays = WORK_WEEK_PRESETS[workWeekId].totalDays;
}
}
setSelectedDate(date) {
this.selectedDate = date;
}
}
Configuration._instance = null;
// Backward compatibility alias
export { Configuration as CalendarConfig };
//# sourceMappingURL=CalendarConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/CalendarConfig.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,11 @@
import { Configuration } from './CalendarConfig';
/**
* ConfigManager - Static configuration loader
* Loads JSON and creates Configuration instance
*/
export declare class ConfigManager {
/**
* Load configuration from JSON and create Configuration instance
*/
static load(): Promise<Configuration>;
}

View file

@ -0,0 +1,43 @@
import { Configuration } from './CalendarConfig';
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() {
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 = {
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;
}
}
//# sourceMappingURL=ConfigManager.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../../src/configuration/ConfigManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI;QACf,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,oBAAoB;QACpB,MAAM,UAAU,GAAoB;YAClC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YACpC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YACpC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YAC9C,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YAC9C,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY;YAClD,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;YACzC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;YACzC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;YAC9B,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU;YAC/B,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU;YAC/B,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY;YACxC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW;YACtC,oBAAoB,EAAE,IAAI,CAAC,aAAa,CAAC,oBAAoB;YAC7D,gBAAgB,EAAE,IAAI,CAAC,YAAY,CAAC,YAAY;YAChD,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC,gBAAgB;SACtD,CAAC;QAEF,gCAAgC;QAChC,MAAM,MAAM,GAAG,IAAI,aAAa,CAC9B,UAAU,EACV,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,eAAe,CACrB,CAAC;QAEF,0BAA0B;QAC1B,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAEjD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}

View file

@ -0,0 +1,10 @@
import { ViewPeriod } from '../types/CalendarTypes';
/**
* View settings for date-based calendar mode
*/
export interface IDateViewSettings {
period: ViewPeriod;
weekDays: number;
firstDayOfWeek: number;
showAllDay: boolean;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=DateViewSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configuration/DateViewSettings.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,22 @@
/**
* 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;
}
/**
* Grid settings utility functions
*/
export declare namespace GridSettingsUtils {
function isValidSnapInterval(interval: number): boolean;
}

View file

@ -0,0 +1,11 @@
/**
* Grid settings utility functions
*/
export var GridSettingsUtils;
(function (GridSettingsUtils) {
function isValidSnapInterval(interval) {
return [5, 10, 15, 30, 60].includes(interval);
}
GridSettingsUtils.isValidSnapInterval = isValidSnapInterval;
})(GridSettingsUtils || (GridSettingsUtils = {}));
//# sourceMappingURL=GridSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"GridSettings.js","sourceRoot":"","sources":["../../../src/configuration/GridSettings.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,KAAW,iBAAiB,CAIjC;AAJD,WAAiB,iBAAiB;IAChC,SAAgB,mBAAmB,CAAC,QAAgB;QAClD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAFe,qCAAmB,sBAElC,CAAA;AACH,CAAC,EAJgB,iBAAiB,KAAjB,iBAAiB,QAIjC"}

View file

@ -0,0 +1,21 @@
/**
* Main calendar configuration interface
*/
export interface ICalendarConfig {
scrollbarWidth: number;
scrollbarColor: string;
scrollbarTrackColor: string;
scrollbarHoverColor: string;
scrollbarBorderRadius: number;
allowDrag: boolean;
allowResize: boolean;
allowCreate: boolean;
apiEndpoint: string;
dateFormat: string;
timeFormat: string;
enableSearch: boolean;
enableTouch: boolean;
defaultEventDuration: number;
minEventDuration: number;
maxEventDuration: number;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=ICalendarConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/ICalendarConfig.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,10 @@
/**
* Time format configuration settings
*/
export interface ITimeFormatConfig {
timezone: string;
use24HourFormat: boolean;
locale: string;
dateFormat: 'locale' | 'technical';
showSeconds: boolean;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=TimeFormatConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configuration/TimeFormatConfig.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,9 @@
/**
* Work week configuration settings
*/
export interface IWorkWeekSettings {
id: string;
workDays: number[];
totalDays: number;
firstWorkDay: number;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=WorkWeekSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"WorkWeekSettings.js","sourceRoot":"","sources":["../../../src/configuration/WorkWeekSettings.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,46 @@
import { ICalendarConfig } from './ICalendarConfig';
import { IGridSettings } from './GridSettings';
import { IDateViewSettings } from './DateViewSettings';
import { ITimeFormatConfig } from './TimeFormatConfig';
import { IWorkWeekSettings } from './WorkWeekSettings';
import { CalendarView } from '../types/CalendarTypes';
/**
* All-day event layout constants
*/
export declare const ALL_DAY_CONSTANTS: {
readonly EVENT_HEIGHT: 22;
readonly EVENT_GAP: 2;
readonly CONTAINER_PADDING: 4;
readonly MAX_COLLAPSED_ROWS: 4;
readonly SINGLE_ROW_HEIGHT: number;
};
/**
* Work week presets - Configuration data
*/
export declare const WORK_WEEK_PRESETS: {
[key: string]: IWorkWeekSettings;
};
/**
* Configuration - DTO container for all configuration
* Pure data object loaded from JSON via ConfigManager
*/
export declare class Configuration {
private static _instance;
config: ICalendarConfig;
gridSettings: IGridSettings;
dateViewSettings: IDateViewSettings;
timeFormatConfig: ITimeFormatConfig;
currentWorkWeek: string;
currentView: CalendarView;
selectedDate: Date;
apiEndpoint: string;
constructor(config: ICalendarConfig, gridSettings: IGridSettings, dateViewSettings: IDateViewSettings, timeFormatConfig: ITimeFormatConfig, currentWorkWeek: string, currentView: CalendarView, selectedDate?: Date);
/**
* Get the current Configuration instance
* Used by web components that can't use dependency injection
*/
static getInstance(): Configuration;
setSelectedDate(date: Date): void;
getWorkWeekSettings(): IWorkWeekSettings;
}
export { Configuration as CalendarConfig };

View file

@ -0,0 +1,85 @@
/**
* 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
}
};
/**
* Work week presets - Configuration data
*/
export const WORK_WEEK_PRESETS = {
'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 {
constructor(config, gridSettings, dateViewSettings, timeFormatConfig, currentWorkWeek, currentView, selectedDate = new Date()) {
this.apiEndpoint = '/api';
this.config = config;
this.gridSettings = gridSettings;
this.dateViewSettings = dateViewSettings;
this.timeFormatConfig = timeFormatConfig;
this.currentWorkWeek = currentWorkWeek;
this.currentView = currentView;
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
*/
static getInstance() {
if (!Configuration._instance) {
throw new Error('Configuration has not been initialized. Call ConfigManager.load() first.');
}
return Configuration._instance;
}
setSelectedDate(date) {
this.selectedDate = date;
}
getWorkWeekSettings() {
return WORK_WEEK_PRESETS[this.currentWorkWeek] || WORK_WEEK_PRESETS['standard'];
}
}
Configuration._instance = null;
// Backward compatibility alias
export { Configuration as CalendarConfig };
//# sourceMappingURL=CalendarConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/configurations/CalendarConfig.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,CAAC;IACZ,iBAAiB,EAAE,CAAC;IACpB,kBAAkB,EAAE,CAAC;IACrB,IAAI,iBAAiB;QACnB,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO;IACpD,CAAC;CACO,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAyC;IACrE,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACzB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,YAAY;QAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAChB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/B,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,aAAa;IAYxB,YACE,MAAuB,EACvB,YAA2B,EAC3B,gBAAmC,EACnC,gBAAmC,EACnC,eAAuB,EACvB,WAAyB,EACzB,eAAqB,IAAI,IAAI,EAAE;QAT1B,gBAAW,GAAW,MAAM,CAAC;QAWlC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QAEjC,iDAAiD;QACjD,aAAa,CAAC,SAAS,GAAG,IAAI,CAAC;IACjC,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,aAAa,CAAC,SAAS,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,IAAU;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,mBAAmB;QACjB,OAAO,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAClF,CAAC;;AAjDc,uBAAS,GAAyB,IAAI,AAA7B,CAA8B;AAoDxD,+BAA+B;AAC/B,OAAO,EAAE,aAAa,IAAI,cAAc,EAAE,CAAC"}

View file

@ -0,0 +1,28 @@
import { Configuration } from './CalendarConfig';
import { IEventBus } from '../types/CalendarTypes';
/**
* ConfigManager - Configuration loader and CSS property manager
* Loads JSON and creates Configuration instance
* Listens to events and manages CSS custom properties for dynamic styling
*/
export declare class ConfigManager {
private eventBus;
private config;
constructor(eventBus: IEventBus, config: Configuration);
/**
* Setup event listeners for dynamic CSS updates
*/
private setupEventListeners;
/**
* Sync grid-related CSS variables from configuration
*/
private syncGridCSSVariables;
/**
* Sync workweek-related CSS variables
*/
private syncWorkweekCSSVariables;
/**
* Load configuration from JSON and create Configuration instance
*/
static load(): Promise<Configuration>;
}

View file

@ -0,0 +1,80 @@
import { Configuration } from './CalendarConfig';
import { TimeFormatter } from '../utils/TimeFormatter';
import { CoreEvents } from '../constants/CoreEvents';
/**
* ConfigManager - Configuration loader and CSS property manager
* Loads JSON and creates Configuration instance
* Listens to events and manages CSS custom properties for dynamic styling
*/
export class ConfigManager {
constructor(eventBus, config) {
this.eventBus = eventBus;
this.config = config;
this.setupEventListeners();
this.syncGridCSSVariables();
this.syncWorkweekCSSVariables();
}
/**
* Setup event listeners for dynamic CSS updates
*/
setupEventListeners() {
// Listen to workweek changes and update CSS accordingly
this.eventBus.on(CoreEvents.WORKWEEK_CHANGED, (event) => {
const { settings } = event.detail;
this.syncWorkweekCSSVariables(settings);
});
}
/**
* Sync grid-related CSS variables from configuration
*/
syncGridCSSVariables() {
const gridSettings = this.config.gridSettings;
document.documentElement.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`);
document.documentElement.style.setProperty('--day-start-hour', gridSettings.dayStartHour.toString());
document.documentElement.style.setProperty('--day-end-hour', gridSettings.dayEndHour.toString());
document.documentElement.style.setProperty('--work-start-hour', gridSettings.workStartHour.toString());
document.documentElement.style.setProperty('--work-end-hour', gridSettings.workEndHour.toString());
}
/**
* Sync workweek-related CSS variables
*/
syncWorkweekCSSVariables(workWeekSettings) {
const settings = workWeekSettings || this.config.getWorkWeekSettings();
document.documentElement.style.setProperty('--grid-columns', settings.totalDays.toString());
}
/**
* Load configuration from JSON and create Configuration instance
*/
static async load() {
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 = {
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, data.currentView || 'week');
// Configure TimeFormatter
TimeFormatter.configure(config.timeFormatConfig);
return config;
}
}
//# sourceMappingURL=ConfigManager.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../../src/configurations/ConfigManager.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,10 @@
import { ViewPeriod } from '../types/CalendarTypes';
/**
* View settings for date-based calendar mode
*/
export interface IDateViewSettings {
period: ViewPeriod;
weekDays: number;
firstDayOfWeek: number;
showAllDay: boolean;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=DateViewSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configurations/DateViewSettings.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,22 @@
/**
* 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;
}
/**
* Grid settings utility functions
*/
export declare namespace GridSettingsUtils {
function isValidSnapInterval(interval: number): boolean;
}

View file

@ -0,0 +1,11 @@
/**
* Grid settings utility functions
*/
export var GridSettingsUtils;
(function (GridSettingsUtils) {
function isValidSnapInterval(interval) {
return [5, 10, 15, 30, 60].includes(interval);
}
GridSettingsUtils.isValidSnapInterval = isValidSnapInterval;
})(GridSettingsUtils || (GridSettingsUtils = {}));
//# sourceMappingURL=GridSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"GridSettings.js","sourceRoot":"","sources":["../../../src/configurations/GridSettings.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,KAAW,iBAAiB,CAIjC;AAJD,WAAiB,iBAAiB;IAChC,SAAgB,mBAAmB,CAAC,QAAgB;QAClD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAFe,qCAAmB,sBAElC,CAAA;AACH,CAAC,EAJgB,iBAAiB,KAAjB,iBAAiB,QAIjC"}

View file

@ -0,0 +1,21 @@
/**
* Main calendar configuration interface
*/
export interface ICalendarConfig {
scrollbarWidth: number;
scrollbarColor: string;
scrollbarTrackColor: string;
scrollbarHoverColor: string;
scrollbarBorderRadius: number;
allowDrag: boolean;
allowResize: boolean;
allowCreate: boolean;
apiEndpoint: string;
dateFormat: string;
timeFormat: string;
enableSearch: boolean;
enableTouch: boolean;
defaultEventDuration: number;
minEventDuration: number;
maxEventDuration: number;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=ICalendarConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configurations/ICalendarConfig.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,10 @@
/**
* Time format configuration settings
*/
export interface ITimeFormatConfig {
timezone: string;
use24HourFormat: boolean;
locale: string;
dateFormat: 'locale' | 'technical';
showSeconds: boolean;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=TimeFormatConfig.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configurations/TimeFormatConfig.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,9 @@
/**
* Work week configuration settings
*/
export interface IWorkWeekSettings {
id: string;
workDays: number[];
totalDays: number;
firstWorkDay: number;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=WorkWeekSettings.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"WorkWeekSettings.js","sourceRoot":"","sources":["../../../src/configurations/WorkWeekSettings.ts"],"names":[],"mappings":""}

37
wwwroot/js/constants/CoreEvents.d.ts vendored Normal file
View file

@ -0,0 +1,37 @@
/**
* CoreEvents - Consolidated essential events for the calendar
* Reduces complexity from 102+ events to ~20 core events
*/
export declare const CoreEvents: {
readonly INITIALIZED: "core:initialized";
readonly READY: "core:ready";
readonly DESTROYED: "core:destroyed";
readonly VIEW_CHANGED: "view:changed";
readonly VIEW_RENDERED: "view:rendered";
readonly WORKWEEK_CHANGED: "workweek:changed";
readonly NAV_BUTTON_CLICKED: "nav:button-clicked";
readonly DATE_CHANGED: "nav:date-changed";
readonly NAVIGATION_COMPLETED: "nav:navigation-completed";
readonly NAVIGATE_TO_EVENT: "nav:navigate-to-event";
readonly DATA_LOADING: "data:loading";
readonly DATA_LOADED: "data:loaded";
readonly DATA_ERROR: "data:error";
readonly EVENTS_FILTERED: "data:events-filtered";
readonly REMOTE_UPDATE_RECEIVED: "data:remote-update";
readonly GRID_RENDERED: "grid:rendered";
readonly GRID_CLICKED: "grid:clicked";
readonly CELL_SELECTED: "grid:cell-selected";
readonly EVENT_CREATED: "event:created";
readonly EVENT_UPDATED: "event:updated";
readonly EVENT_DELETED: "event:deleted";
readonly EVENT_SELECTED: "event:selected";
readonly ERROR: "system:error";
readonly REFRESH_REQUESTED: "system:refresh";
readonly OFFLINE_MODE_CHANGED: "system:offline-mode-changed";
readonly SYNC_STARTED: "sync:started";
readonly SYNC_COMPLETED: "sync:completed";
readonly SYNC_FAILED: "sync:failed";
readonly SYNC_RETRY: "sync:retry";
readonly FILTER_CHANGED: "filter:changed";
readonly EVENTS_RENDERED: "events:rendered";
};

View file

@ -0,0 +1,48 @@
/**
* CoreEvents - Consolidated essential events for the calendar
* Reduces complexity from 102+ events to ~20 core events
*/
export const CoreEvents = {
// Lifecycle events (3)
INITIALIZED: 'core:initialized',
READY: 'core:ready',
DESTROYED: 'core:destroyed',
// View events (3)
VIEW_CHANGED: 'view:changed',
VIEW_RENDERED: 'view:rendered',
WORKWEEK_CHANGED: 'workweek:changed',
// Navigation events (4)
NAV_BUTTON_CLICKED: 'nav:button-clicked',
DATE_CHANGED: 'nav:date-changed',
NAVIGATION_COMPLETED: 'nav:navigation-completed',
NAVIGATE_TO_EVENT: 'nav:navigate-to-event',
// Data events (5)
DATA_LOADING: 'data:loading',
DATA_LOADED: 'data:loaded',
DATA_ERROR: 'data:error',
EVENTS_FILTERED: 'data:events-filtered',
REMOTE_UPDATE_RECEIVED: 'data:remote-update',
// Grid events (3)
GRID_RENDERED: 'grid:rendered',
GRID_CLICKED: 'grid:clicked',
CELL_SELECTED: 'grid:cell-selected',
// Event management (4)
EVENT_CREATED: 'event:created',
EVENT_UPDATED: 'event:updated',
EVENT_DELETED: 'event:deleted',
EVENT_SELECTED: 'event:selected',
// System events (3)
ERROR: 'system:error',
REFRESH_REQUESTED: 'system:refresh',
OFFLINE_MODE_CHANGED: 'system:offline-mode-changed',
// Sync events (4)
SYNC_STARTED: 'sync:started',
SYNC_COMPLETED: 'sync:completed',
SYNC_FAILED: 'sync:failed',
SYNC_RETRY: 'sync:retry',
// Filter events (1)
FILTER_CHANGED: 'filter:changed',
// Rendering events (1)
EVENTS_RENDERED: 'events:rendered'
};
//# sourceMappingURL=CoreEvents.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"CoreEvents.js","sourceRoot":"","sources":["../../../src/constants/CoreEvents.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,uBAAuB;IACvB,WAAW,EAAE,kBAAkB;IAC/B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,gBAAgB;IAE3B,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,eAAe;IAC9B,gBAAgB,EAAE,kBAAkB;IAEpC,wBAAwB;IACxB,kBAAkB,EAAE,oBAAoB;IACxC,YAAY,EAAE,kBAAkB;IAChC,oBAAoB,EAAE,0BAA0B;IAChD,iBAAiB,EAAE,uBAAuB;IAE1C,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,eAAe,EAAE,sBAAsB;IACvC,sBAAsB,EAAE,oBAAoB;IAE5C,kBAAkB;IAClB,aAAa,EAAE,eAAe;IAC9B,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,oBAAoB;IAEnC,uBAAuB;IACvB,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,cAAc,EAAE,gBAAgB;IAEhC,oBAAoB;IACpB,KAAK,EAAE,cAAc;IACrB,iBAAiB,EAAE,gBAAgB;IACnC,oBAAoB,EAAE,6BAA6B;IAEnD,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,cAAc,EAAE,gBAAgB;IAChC,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IAExB,oBAAoB;IACpB,cAAc,EAAE,gBAAgB;IAEhC,uBAAuB;IACvB,eAAe,EAAE,iBAAiB;CAC1B,CAAC"}

225
wwwroot/js/core/CalendarConfig.d.ts vendored Normal file
View file

@ -0,0 +1,225 @@
import { CalendarConfig as ICalendarConfig, ViewPeriod, CalendarMode } from '../types/CalendarTypes';
/**
* All-day event layout constants
*/
export declare const ALL_DAY_CONSTANTS: {
readonly EVENT_HEIGHT: 22;
readonly EVENT_GAP: 2;
readonly CONTAINER_PADDING: 4;
readonly MAX_COLLAPSED_ROWS: 4;
readonly SINGLE_ROW_HEIGHT: number;
};
/**
* Layout and timing settings for the calendar grid
*/
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;
}
/**
* View settings for date-based calendar mode
*/
interface DateViewSettings {
period: ViewPeriod;
weekDays: number;
firstDayOfWeek: number;
showAllDay: boolean;
}
/**
* Work week configuration settings
*/
interface WorkWeekSettings {
id: string;
workDays: number[];
totalDays: number;
firstWorkDay: number;
}
/**
* View settings for resource-based calendar mode
*/
interface ResourceViewSettings {
maxResources: number;
showAvatars: boolean;
avatarSize: number;
resourceNameFormat: 'full' | 'short';
showResourceDetails: boolean;
showAllDay: boolean;
}
/**
* Time format configuration settings
*/
interface TimeFormatConfig {
timezone: string;
use24HourFormat: boolean;
locale: string;
dateFormat: 'locale' | 'technical';
showSeconds: boolean;
}
/**
* Calendar configuration management
*/
export declare class CalendarConfig {
private config;
private calendarMode;
private selectedDate;
private gridSettings;
private dateViewSettings;
private resourceViewSettings;
private currentWorkWeek;
private timeFormatConfig;
constructor();
/**
* Load calendar type and date from URL parameters
*/
private loadCalendarType;
/**
* Load configuration from DOM data attributes
*/
private loadFromDOM;
/**
* Get a config value
*/
get<K extends keyof ICalendarConfig>(key: K): ICalendarConfig[K];
/**
* Set a config value
*/
set<K extends keyof ICalendarConfig>(key: K, value: ICalendarConfig[K]): void;
/**
* Update multiple config values
*/
update(updates: Partial<ICalendarConfig>): void;
/**
* Get all config
*/
getAll(): ICalendarConfig;
/**
* Calculate derived values
*/
get minuteHeight(): number;
get totalHours(): number;
get totalMinutes(): number;
get slotsPerHour(): number;
get totalSlots(): number;
get slotHeight(): number;
/**
* Validate snap interval
*/
isValidSnapInterval(interval: number): boolean;
/**
* Get grid display settings
*/
getGridSettings(): GridSettings;
/**
* Update grid display settings
*/
updateGridSettings(updates: Partial<GridSettings>): void;
/**
* Get date view settings
*/
getDateViewSettings(): DateViewSettings;
/**
* Update date view settings
*/
updateDateViewSettings(updates: Partial<DateViewSettings>): void;
/**
* Get resource view settings
*/
getResourceViewSettings(): ResourceViewSettings;
/**
* Update resource view settings
*/
updateResourceViewSettings(updates: Partial<ResourceViewSettings>): void;
/**
* Check if current mode is resource-based
*/
isResourceMode(): boolean;
/**
* Check if current mode is date-based
*/
isDateMode(): boolean;
/**
* Get calendar mode
*/
getCalendarMode(): CalendarMode;
/**
* Set calendar mode
*/
setCalendarMode(mode: CalendarMode): void;
/**
* Get selected date
*/
getSelectedDate(): Date | null;
/**
* Set selected date
* Note: Does not emit events - caller is responsible for event emission
*/
setSelectedDate(date: Date): void;
/**
* Get work week presets
*/
private getWorkWeekPresets;
/**
* Get current work week settings
*/
getWorkWeekSettings(): WorkWeekSettings;
/**
* Set work week preset
* Note: Does not emit events - caller is responsible for event emission
*/
setWorkWeek(workWeekId: string): void;
/**
* Get current work week ID
*/
getCurrentWorkWeek(): string;
/**
* Get time format settings
*/
getTimeFormatSettings(): TimeFormatConfig;
/**
* Update time format settings
*/
updateTimeFormatSettings(updates: Partial<TimeFormatConfig>): void;
/**
* Set timezone (convenience method)
*/
setTimezone(timezone: string): void;
/**
* Set 12/24 hour format (convenience method)
*/
set24HourFormat(use24Hour: boolean): void;
/**
* Get configured timezone
*/
getTimezone(): string;
/**
* Get configured locale
*/
getLocale(): string;
/**
* Check if using 24-hour format
*/
is24HourFormat(): boolean;
/**
* Set date format (convenience method)
*/
setDateFormat(format: 'locale' | 'technical'): void;
/**
* Set whether to show seconds (convenience method)
*/
setShowSeconds(show: boolean): void;
/**
* Get current date format
*/
getDateFormat(): 'locale' | 'technical';
}
export declare const calendarConfig: CalendarConfig;
export {};

View file

@ -0,0 +1,421 @@
// Calendar configuration management
import { eventBus } from './EventBus';
import { CoreEvents } from '../constants/CoreEvents';
import { TimeFormatter } 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)
get SINGLE_ROW_HEIGHT() {
return this.EVENT_HEIGHT + this.EVENT_GAP + this.CONTAINER_PADDING; // 28px
}
};
/**
* Calendar configuration management
*/
export class CalendarConfig {
constructor() {
this.calendarMode = 'date';
this.selectedDate = null;
this.currentWorkWeek = 'standard';
this.config = {
// 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
};
// Grid display settings
this.gridSettings = {
hourHeight: 60,
dayStartHour: 0,
dayEndHour: 24,
workStartHour: 8,
workEndHour: 17,
snapInterval: 15,
showCurrentTime: true,
showWorkHours: true,
fitToWidth: false,
scrollToHour: 8
};
// Date view settings
this.dateViewSettings = {
period: 'week',
weekDays: 7,
firstDayOfWeek: 1,
showAllDay: true
};
// Resource view settings
this.resourceViewSettings = {
maxResources: 10,
showAvatars: true,
avatarSize: 32,
resourceNameFormat: 'full',
showResourceDetails: true,
showAllDay: true
};
// Time format settings - default to Denmark
this.timeFormatConfig = {
timezone: 'Europe/Copenhagen',
use24HourFormat: true,
locale: 'da-DK'
};
// Set computed values
this.config.minEventDuration = this.gridSettings.snapInterval;
// Initialize TimeFormatter with default settings
TimeFormatter.configure(this.timeFormatConfig);
// Load calendar type from URL parameter
this.loadCalendarType();
// Load from data attributes
this.loadFromDOM();
}
/**
* Load calendar type and date from URL parameters
*/
loadCalendarType() {
const urlParams = new URLSearchParams(window.location.search);
const typeParam = urlParams.get('type');
const dateParam = urlParams.get('date');
// Set calendar mode
if (typeParam === 'resource' || typeParam === 'date') {
this.calendarMode = typeParam;
}
else {
this.calendarMode = 'date'; // Default
}
// Set selected date
if (dateParam) {
const parsedDate = new Date(dateParam);
if (!isNaN(parsedDate.getTime())) {
this.selectedDate = parsedDate;
}
else {
this.selectedDate = new Date();
}
}
else {
this.selectedDate = new Date(); // Default to today
}
}
/**
* Load configuration from DOM data attributes
*/
loadFromDOM() {
const calendar = document.querySelector('swp-calendar');
if (!calendar)
return;
// Read data attributes
const attrs = calendar.dataset;
// Update date view settings
if (attrs.view)
this.dateViewSettings.period = attrs.view;
if (attrs.weekDays)
this.dateViewSettings.weekDays = parseInt(attrs.weekDays);
// Update grid settings
if (attrs.snapInterval)
this.gridSettings.snapInterval = parseInt(attrs.snapInterval);
if (attrs.dayStartHour)
this.gridSettings.dayStartHour = parseInt(attrs.dayStartHour);
if (attrs.dayEndHour)
this.gridSettings.dayEndHour = parseInt(attrs.dayEndHour);
if (attrs.hourHeight)
this.gridSettings.hourHeight = parseInt(attrs.hourHeight);
if (attrs.fitToWidth !== undefined)
this.gridSettings.fitToWidth = attrs.fitToWidth === 'true';
// Update computed values
this.config.minEventDuration = this.gridSettings.snapInterval;
}
/**
* Get a config value
*/
get(key) {
return this.config[key];
}
/**
* Set a config value
*/
set(key, value) {
const oldValue = this.config[key];
this.config[key] = value;
// Update computed values handled in specific update methods
// Emit config update event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key,
value,
oldValue
});
}
/**
* Update multiple config values
*/
update(updates) {
Object.entries(updates).forEach(([key, value]) => {
this.set(key, value);
});
}
/**
* Get all config
*/
getAll() {
return { ...this.config };
}
/**
* Calculate derived values
*/
get minuteHeight() {
return this.gridSettings.hourHeight / 60;
}
get totalHours() {
return this.gridSettings.dayEndHour - this.gridSettings.dayStartHour;
}
get totalMinutes() {
return this.totalHours * 60;
}
get slotsPerHour() {
return 60 / this.gridSettings.snapInterval;
}
get totalSlots() {
return this.totalHours * this.slotsPerHour;
}
get slotHeight() {
return this.gridSettings.hourHeight / this.slotsPerHour;
}
/**
* Validate snap interval
*/
isValidSnapInterval(interval) {
return [5, 10, 15, 30, 60].includes(interval);
}
/**
* Get grid display settings
*/
getGridSettings() {
return { ...this.gridSettings };
}
/**
* Update grid display settings
*/
updateGridSettings(updates) {
this.gridSettings = { ...this.gridSettings, ...updates };
// Update computed values
if (updates.snapInterval) {
this.config.minEventDuration = updates.snapInterval;
}
// Grid settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'gridSettings',
value: this.gridSettings
});
}
/**
* Get date view settings
*/
getDateViewSettings() {
return { ...this.dateViewSettings };
}
/**
* Update date view settings
*/
updateDateViewSettings(updates) {
this.dateViewSettings = { ...this.dateViewSettings, ...updates };
// Date view settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'dateViewSettings',
value: this.dateViewSettings
});
}
/**
* Get resource view settings
*/
getResourceViewSettings() {
return { ...this.resourceViewSettings };
}
/**
* Update resource view settings
*/
updateResourceViewSettings(updates) {
this.resourceViewSettings = { ...this.resourceViewSettings, ...updates };
// Resource view settings changes trigger general refresh - avoid specific event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'resourceViewSettings',
value: this.resourceViewSettings
});
}
/**
* Check if current mode is resource-based
*/
isResourceMode() {
return this.calendarMode === 'resource';
}
/**
* Check if current mode is date-based
*/
isDateMode() {
return this.calendarMode === 'date';
}
/**
* Get calendar mode
*/
getCalendarMode() {
return this.calendarMode;
}
/**
* Set calendar mode
*/
setCalendarMode(mode) {
const oldMode = this.calendarMode;
this.calendarMode = mode;
// Emit calendar mode change event
eventBus.emit(CoreEvents.VIEW_CHANGED, {
oldType: oldMode,
newType: mode
});
}
/**
* Get selected date
*/
getSelectedDate() {
return this.selectedDate;
}
/**
* Set selected date
*/
setSelectedDate(date) {
this.selectedDate = date;
// Emit date change event
eventBus.emit(CoreEvents.DATE_CHANGED, {
date: date
});
}
/**
* Get work week presets
*/
getWorkWeekPresets() {
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
*/
getWorkWeekSettings() {
const presets = this.getWorkWeekPresets();
return presets[this.currentWorkWeek] || presets['standard'];
}
/**
* Set work week preset
*/
setWorkWeek(workWeekId) {
const presets = this.getWorkWeekPresets();
if (presets[workWeekId]) {
this.currentWorkWeek = workWeekId;
// Update dateViewSettings to match work week
this.dateViewSettings.weekDays = presets[workWeekId].totalDays;
// Emit work week change event
eventBus.emit(CoreEvents.WORKWEEK_CHANGED, {
workWeekId: workWeekId,
settings: presets[workWeekId]
});
}
}
/**
* Get current work week ID
*/
getCurrentWorkWeek() {
return this.currentWorkWeek;
}
/**
* Get time format settings
*/
getTimeFormatSettings() {
return { ...this.timeFormatConfig };
}
/**
* Update time format settings
*/
updateTimeFormatSettings(updates) {
this.timeFormatConfig = { ...this.timeFormatConfig, ...updates };
// Update TimeFormatter with new settings
TimeFormatter.configure(this.timeFormatConfig);
// Emit time format change event
eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
key: 'timeFormatSettings',
value: this.timeFormatConfig
});
}
/**
* Set timezone (convenience method)
*/
setTimezone(timezone) {
this.updateTimeFormatSettings({ timezone });
}
/**
* Set 12/24 hour format (convenience method)
*/
set24HourFormat(use24Hour) {
this.updateTimeFormatSettings({ use24HourFormat: use24Hour });
}
/**
* Get configured timezone
*/
getTimezone() {
return this.timeFormatConfig.timezone;
}
/**
* Check if using 24-hour format
*/
is24HourFormat() {
return this.timeFormatConfig.use24HourFormat;
}
}
// Create singleton instance
export const calendarConfig = new CalendarConfig();
//# sourceMappingURL=CalendarConfig.js.map

File diff suppressed because one or more lines are too long

60
wwwroot/js/core/EventBus.d.ts vendored Normal file
View file

@ -0,0 +1,60 @@
import { IEventLogEntry, IEventBus } from '../types/CalendarTypes';
/**
* Central event dispatcher for calendar using DOM CustomEvents
* Provides logging and debugging capabilities
*/
export declare class EventBus implements IEventBus {
private eventLog;
private debug;
private listeners;
private logConfig;
/**
* Subscribe to an event via DOM addEventListener
*/
on(eventType: string, handler: EventListener, options?: AddEventListenerOptions): () => void;
/**
* Subscribe to an event once
*/
once(eventType: string, handler: EventListener): () => void;
/**
* Unsubscribe from an event
*/
off(eventType: string, handler: EventListener): void;
/**
* Emit an event via DOM CustomEvent
*/
emit(eventType: string, detail?: unknown): boolean;
/**
* Log event with console grouping
*/
private logEventWithGrouping;
/**
* Extract category from event type
*/
private extractCategory;
/**
* Get styling for different categories
*/
private getCategoryStyle;
/**
* Configure logging for specific categories
*/
setLogConfig(config: {
[key: string]: boolean;
}): void;
/**
* Get current log configuration
*/
getLogConfig(): {
[key: string]: boolean;
};
/**
* Get event history
*/
getEventLog(eventType?: string): IEventLogEntry[];
/**
* Enable/disable debug mode
*/
setDebug(enabled: boolean): void;
}
export declare const eventBus: EventBus;

158
wwwroot/js/core/EventBus.js Normal file
View file

@ -0,0 +1,158 @@
/**
* Central event dispatcher for calendar using DOM CustomEvents
* Provides logging and debugging capabilities
*/
export class EventBus {
constructor() {
this.eventLog = [];
this.debug = false;
this.listeners = new Set();
// Log configuration for different categories
this.logConfig = {
calendar: true,
grid: true,
event: true,
scroll: true,
navigation: true,
view: true,
default: true
};
}
/**
* Subscribe to an event via DOM addEventListener
*/
on(eventType, handler, options) {
document.addEventListener(eventType, handler, options);
// Track for cleanup
this.listeners.add({ eventType, handler, options });
// Return unsubscribe function
return () => this.off(eventType, handler);
}
/**
* Subscribe to an event once
*/
once(eventType, handler) {
return this.on(eventType, handler, { once: true });
}
/**
* Unsubscribe from an event
*/
off(eventType, handler) {
document.removeEventListener(eventType, handler);
// Remove from tracking
for (const listener of this.listeners) {
if (listener.eventType === eventType && listener.handler === handler) {
this.listeners.delete(listener);
break;
}
}
}
/**
* Emit an event via DOM CustomEvent
*/
emit(eventType, detail = {}) {
// Validate eventType
if (!eventType) {
return false;
}
const event = new CustomEvent(eventType, {
detail: detail ?? {},
bubbles: true,
cancelable: true
});
// Log event with grouping
if (this.debug) {
this.logEventWithGrouping(eventType, detail);
}
this.eventLog.push({
type: eventType,
detail: detail ?? {},
timestamp: Date.now()
});
// Emit on document (only DOM events now)
return !document.dispatchEvent(event);
}
/**
* Log event with console grouping
*/
logEventWithGrouping(eventType, detail) {
// Extract category from event type (e.g., 'calendar:datechanged' → 'calendar')
const category = this.extractCategory(eventType);
// Only log if category is enabled
if (!this.logConfig[category]) {
return;
}
// Get category emoji and color
const { emoji, color } = this.getCategoryStyle(category);
// Use collapsed group to reduce visual noise
}
/**
* Extract category from event type
*/
extractCategory(eventType) {
if (!eventType) {
return 'unknown';
}
if (eventType.includes(':')) {
return eventType.split(':')[0];
}
// Fallback: try to detect category from event name patterns
const lowerType = eventType.toLowerCase();
if (lowerType.includes('grid') || lowerType.includes('rendered'))
return 'grid';
if (lowerType.includes('event') || lowerType.includes('sync'))
return 'event';
if (lowerType.includes('scroll'))
return 'scroll';
if (lowerType.includes('nav') || lowerType.includes('date'))
return 'navigation';
if (lowerType.includes('view'))
return 'view';
return 'default';
}
/**
* Get styling for different categories
*/
getCategoryStyle(category) {
const styles = {
calendar: { emoji: '🗓️', color: '#2196F3' },
grid: { emoji: '📊', color: '#4CAF50' },
event: { emoji: '📅', color: '#FF9800' },
scroll: { emoji: '📜', color: '#9C27B0' },
navigation: { emoji: '🧭', color: '#F44336' },
view: { emoji: '👁️', color: '#00BCD4' },
default: { emoji: '📢', color: '#607D8B' }
};
return styles[category] || styles.default;
}
/**
* Configure logging for specific categories
*/
setLogConfig(config) {
this.logConfig = { ...this.logConfig, ...config };
}
/**
* Get current log configuration
*/
getLogConfig() {
return { ...this.logConfig };
}
/**
* Get event history
*/
getEventLog(eventType) {
if (eventType) {
return this.eventLog.filter(e => e.type === eventType);
}
return this.eventLog;
}
/**
* Enable/disable debug mode
*/
setDebug(enabled) {
this.debug = enabled;
}
}
// Create singleton instance
export const eventBus = new EventBus();
//# sourceMappingURL=EventBus.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"EventBus.js","sourceRoot":"","sources":["../../../src/core/EventBus.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,55 @@
import { IColumnDataSource } from '../types/ColumnDataSource';
import { DateService } from '../utils/DateService';
import { Configuration } from '../configurations/CalendarConfig';
import { CalendarView } from '../types/CalendarTypes';
/**
* DateColumnDataSource - Provides date-based columns
*
* Calculates which dates to display based on:
* - Current date
* - Current view (day/week/month)
* - Workweek settings
*/
export declare class DateColumnDataSource implements IColumnDataSource {
private dateService;
private config;
private currentDate;
private currentView;
constructor(dateService: DateService, config: Configuration, currentDate: Date, currentView: CalendarView);
/**
* Get columns (dates) to display
*/
getColumns(): Date[];
/**
* Get type of datasource
*/
getType(): 'date' | 'resource';
/**
* Update current date
*/
setCurrentDate(date: Date): void;
/**
* Update current view
*/
setCurrentView(view: CalendarView): void;
/**
* Get dates for week view based on workweek settings
*/
private getWeekDates;
/**
* Get all dates in current month
*/
private getMonthDates;
/**
* Get ISO week start (Monday)
*/
private getISOWeekStart;
/**
* Get month start
*/
private getMonthStart;
/**
* Get month end
*/
private getMonthEnd;
}

View file

@ -0,0 +1,94 @@
/**
* DateColumnDataSource - Provides date-based columns
*
* Calculates which dates to display based on:
* - Current date
* - Current view (day/week/month)
* - Workweek settings
*/
export class DateColumnDataSource {
constructor(dateService, config, currentDate, currentView) {
this.dateService = dateService;
this.config = config;
this.currentDate = currentDate;
this.currentView = currentView;
}
/**
* Get columns (dates) to display
*/
getColumns() {
switch (this.currentView) {
case 'week':
return this.getWeekDates();
case 'month':
return this.getMonthDates();
case 'day':
return [this.currentDate];
default:
return this.getWeekDates();
}
}
/**
* Get type of datasource
*/
getType() {
return 'date';
}
/**
* Update current date
*/
setCurrentDate(date) {
this.currentDate = date;
}
/**
* Update current view
*/
setCurrentView(view) {
this.currentView = view;
}
/**
* Get dates for week view based on workweek settings
*/
getWeekDates() {
const weekStart = this.getISOWeekStart(this.currentDate);
const workWeekSettings = this.config.getWorkWeekSettings();
return this.dateService.getWorkWeekDates(weekStart, workWeekSettings.workDays);
}
/**
* Get all dates in current month
*/
getMonthDates() {
const dates = [];
const monthStart = this.getMonthStart(this.currentDate);
const monthEnd = this.getMonthEnd(this.currentDate);
const totalDays = Math.ceil((monthEnd.getTime() - monthStart.getTime()) / (1000 * 60 * 60 * 24)) + 1;
for (let i = 0; i < totalDays; i++) {
dates.push(this.dateService.addDays(monthStart, i));
}
return dates;
}
/**
* Get ISO week start (Monday)
*/
getISOWeekStart(date) {
const weekBounds = this.dateService.getWeekBounds(date);
return this.dateService.startOfDay(weekBounds.start);
}
/**
* Get month start
*/
getMonthStart(date) {
const year = date.getFullYear();
const month = date.getMonth();
return this.dateService.startOfDay(new Date(year, month, 1));
}
/**
* Get month end
*/
getMonthEnd(date) {
const nextMonth = this.dateService.addMonths(date, 1);
const firstOfNextMonth = this.getMonthStart(nextMonth);
return this.dateService.endOfDay(this.dateService.addDays(firstOfNextMonth, -1));
}
}
//# sourceMappingURL=DateColumnDataSource.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"DateColumnDataSource.js","sourceRoot":"","sources":["../../../src/datasources/DateColumnDataSource.ts"],"names":[],"mappings":"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"}

6489
wwwroot/js/demo.js Normal file

File diff suppressed because one or more lines are too long

104
wwwroot/js/edge-scroll.js Normal file
View file

@ -0,0 +1,104 @@
// edge-scroll.js - med timeout + tidsbaseret scroll
(function() {
'use strict';
const OUTER_ZONE = 100; // px fra kant (langsom zone)
const INNER_ZONE = 50; // px fra kant (hurtig zone)
const SLOW_SPEED_PXS = 800; // px/sek i outer zone
const FAST_SPEED_PXS = 2400; // px/sek i inner zone
let scrollableContent = null;
let scrollRAF = null;
let mouseY = 0;
let haveMouse = false;
let lastTs = 0;
let rect = null;
function init() {
console.log('edge-scroll.js: waiting 1000ms before setup...');
setTimeout(setup, 1000);
}
function setup() {
console.log('edge-scroll.js: setup() called');
scrollableContent = document.querySelector('swp-scrollable-content');
if (!scrollableContent) {
console.error('edge-scroll.js: swp-scrollable-content NOT FOUND');
return;
}
console.log('edge-scroll.js: found scrollableContent:', scrollableContent);
// slå smooth scroll fra, så autoscroll er øjeblikkelig
scrollableContent.style.scrollBehavior = 'auto';
scrollableContent.addEventListener('mousemove', handleMouseMove, { passive: true });
scrollableContent.addEventListener('mouseleave', handleMouseLeave, { passive: true });
console.log('edge-scroll.js: ✅ listeners attached');
}
function handleMouseMove(e) {
haveMouse = true;
mouseY = e.clientY;
if (scrollRAF == null) {
lastTs = performance.now();
scrollRAF = requestAnimationFrame(scrollTick);
}
}
function handleMouseLeave() {
haveMouse = false;
stopScrolling();
}
function stopScrolling() {
if (scrollRAF != null) {
cancelAnimationFrame(scrollRAF);
scrollRAF = null;
}
lastTs = 0;
}
function scrollTick(ts) {
const dt = lastTs ? (ts - lastTs) / 1000 : 0;
lastTs = ts;
if (!rect) rect = scrollableContent.getBoundingClientRect();
let vy = 0;
if (haveMouse) {
const distTop = mouseY - rect.top;
const distBot = rect.bottom - mouseY;
// Check top edge
if (distTop < INNER_ZONE) {
// Inner zone (0-50px) - fast speed
vy = -FAST_SPEED_PXS;
} else if (distTop < OUTER_ZONE) {
// Outer zone (50-100px) - slow speed
vy = -SLOW_SPEED_PXS;
}
// Check bottom edge
else if (distBot < INNER_ZONE) {
// Inner zone (0-50px) - fast speed
vy = FAST_SPEED_PXS;
} else if (distBot < OUTER_ZONE) {
// Outer zone (50-100px) - slow speed
vy = SLOW_SPEED_PXS;
}
}
if (vy !== 0) {
scrollableContent.scrollTop += vy * dt;
rect = null; // mål kun én gang pr. frame
scrollRAF = requestAnimationFrame(scrollTick);
} else {
stopScrolling();
}
}
// start init
init();
})();

View file

@ -0,0 +1,98 @@
import { ICalendarEvent } from '../types/CalendarTypes';
import { Configuration } from '../configurations/CalendarConfig';
import { DateService } from '../utils/DateService';
/**
* Base class for event elements
*/
export declare abstract class BaseSwpEventElement extends HTMLElement {
protected dateService: DateService;
protected config: Configuration;
constructor();
/**
* Create a clone for drag operations
* Must be implemented by subclasses
*/
abstract createClone(): HTMLElement;
get eventId(): string;
set eventId(value: string);
get start(): Date;
set start(value: Date);
get end(): Date;
set end(value: Date);
get title(): string;
set title(value: string);
get description(): string;
set description(value: string);
get type(): string;
set type(value: string);
}
/**
* Web Component for timed calendar events (Light DOM)
*/
export declare class SwpEventElement extends BaseSwpEventElement {
/**
* Observed attributes - changes trigger attributeChangedCallback
*/
static get observedAttributes(): string[];
/**
* Called when element is added to DOM
*/
connectedCallback(): void;
/**
* Called when observed attribute changes
*/
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
/**
* Update event position during drag
* @param columnDate - The date of the column
* @param snappedY - The Y position in pixels
*/
updatePosition(columnDate: Date, snappedY: number): void;
/**
* Update event height during resize
* @param newHeight - The new height in pixels
*/
updateHeight(newHeight: number): void;
/**
* Create a clone for drag operations
*/
createClone(): SwpEventElement;
/**
* Render inner HTML structure
*/
private render;
/**
* Update time display when attributes change
*/
private updateDisplay;
/**
* Calculate start/end minutes from Y position
*/
private calculateTimesFromPosition;
/**
* Create SwpEventElement from ICalendarEvent
*/
static fromCalendarEvent(event: ICalendarEvent): SwpEventElement;
/**
* Extract ICalendarEvent from DOM element
*/
static extractCalendarEventFromElement(element: HTMLElement): ICalendarEvent;
}
/**
* Web Component for all-day calendar events
*/
export declare class SwpAllDayEventElement extends BaseSwpEventElement {
connectedCallback(): void;
/**
* Create a clone for drag operations
*/
createClone(): SwpAllDayEventElement;
/**
* Apply CSS grid positioning
*/
applyGridPositioning(row: number, startColumn: number, endColumn: number): void;
/**
* Create from ICalendarEvent
*/
static fromCalendarEvent(event: ICalendarEvent): SwpAllDayEventElement;
}

View file

@ -0,0 +1,303 @@
import { Configuration } from '../configurations/CalendarConfig';
import { TimeFormatter } from '../utils/TimeFormatter';
import { DateService } from '../utils/DateService';
/**
* Base class for event elements
*/
export class BaseSwpEventElement extends HTMLElement {
constructor() {
super();
// Get singleton instance for web components (can't use DI)
this.config = Configuration.getInstance();
this.dateService = new DateService(this.config);
}
// ============================================
// Common Getters/Setters
// ============================================
get eventId() {
return this.dataset.eventId || '';
}
set eventId(value) {
this.dataset.eventId = value;
}
get start() {
return new Date(this.dataset.start || '');
}
set start(value) {
this.dataset.start = this.dateService.toUTC(value);
}
get end() {
return new Date(this.dataset.end || '');
}
set end(value) {
this.dataset.end = this.dateService.toUTC(value);
}
get title() {
return this.dataset.title || '';
}
set title(value) {
this.dataset.title = value;
}
get description() {
return this.dataset.description || '';
}
set description(value) {
this.dataset.description = value;
}
get type() {
return this.dataset.type || 'work';
}
set type(value) {
this.dataset.type = value;
}
}
/**
* Web Component for timed calendar events (Light DOM)
*/
export class SwpEventElement extends BaseSwpEventElement {
/**
* Observed attributes - changes trigger attributeChangedCallback
*/
static get observedAttributes() {
return ['data-start', 'data-end', 'data-title', 'data-description', 'data-type'];
}
/**
* Called when element is added to DOM
*/
connectedCallback() {
if (!this.hasChildNodes()) {
this.render();
}
}
/**
* Called when observed attribute changes
*/
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue && this.isConnected) {
this.updateDisplay();
}
}
// ============================================
// Public Methods
// ============================================
/**
* Update event position during drag
* @param columnDate - The date of the column
* @param snappedY - The Y position in pixels
*/
updatePosition(columnDate, snappedY) {
// 1. Update visual position
this.style.top = `${snappedY + 1}px`;
// 2. Calculate new timestamps
const { startMinutes, endMinutes } = this.calculateTimesFromPosition(snappedY);
// 3. Update data attributes (triggers attributeChangedCallback)
const startDate = this.dateService.createDateAtTime(columnDate, startMinutes);
let endDate = this.dateService.createDateAtTime(columnDate, endMinutes);
// Handle cross-midnight events
if (endMinutes >= 1440) {
const extraDays = Math.floor(endMinutes / 1440);
endDate = this.dateService.addDays(endDate, extraDays);
}
this.start = startDate;
this.end = endDate;
}
/**
* Update event height during resize
* @param newHeight - The new height in pixels
*/
updateHeight(newHeight) {
// 1. Update visual height
this.style.height = `${newHeight}px`;
// 2. Calculate new end time based on height
const gridSettings = this.config.gridSettings;
const { hourHeight, snapInterval } = gridSettings;
// Get current start time
const start = this.start;
// Calculate duration from height
const rawDurationMinutes = (newHeight / hourHeight) * 60;
// Snap duration to grid interval (like drag & drop)
const snappedDurationMinutes = Math.round(rawDurationMinutes / snapInterval) * snapInterval;
// Calculate new end time by adding snapped duration to start (using DateService for timezone safety)
const endDate = this.dateService.addMinutes(start, snappedDurationMinutes);
// 3. Update end attribute (triggers attributeChangedCallback → updateDisplay)
this.end = endDate;
}
/**
* Create a clone for drag operations
*/
createClone() {
const clone = this.cloneNode(true);
// Apply "clone-" prefix to ID
clone.dataset.eventId = `clone-${this.eventId}`;
// Disable pointer events on clone so it doesn't interfere with hover detection
clone.style.pointerEvents = 'none';
// Cache original duration
const timeEl = this.querySelector('swp-event-time');
if (timeEl) {
const duration = timeEl.getAttribute('data-duration');
if (duration) {
clone.dataset.originalDuration = duration;
}
}
// Set height from original
clone.style.height = this.style.height || `${this.getBoundingClientRect().height}px`;
return clone;
}
// ============================================
// Private Methods
// ============================================
/**
* Render inner HTML structure
*/
render() {
const start = this.start;
const end = this.end;
const timeRange = TimeFormatter.formatTimeRange(start, end);
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
this.innerHTML = `
<swp-event-time data-duration="${durationMinutes}">${timeRange}</swp-event-time>
<swp-event-title>${this.title}</swp-event-title>
${this.description ? `<swp-event-description>${this.description}</swp-event-description>` : ''}
`;
}
/**
* Update time display when attributes change
*/
updateDisplay() {
const timeEl = this.querySelector('swp-event-time');
const titleEl = this.querySelector('swp-event-title');
const descEl = this.querySelector('swp-event-description');
if (timeEl && this.dataset.start && this.dataset.end) {
const start = new Date(this.dataset.start);
const end = new Date(this.dataset.end);
const timeRange = TimeFormatter.formatTimeRange(start, end);
timeEl.textContent = timeRange;
// Update duration attribute
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
timeEl.setAttribute('data-duration', durationMinutes.toString());
}
if (titleEl && this.dataset.title) {
titleEl.textContent = this.dataset.title;
}
if (this.dataset.description) {
if (descEl) {
descEl.textContent = this.dataset.description;
}
else if (this.description) {
// Add description element if it doesn't exist
const newDescEl = document.createElement('swp-event-description');
newDescEl.textContent = this.description;
this.appendChild(newDescEl);
}
}
else if (descEl) {
// Remove description element if description is empty
descEl.remove();
}
}
/**
* Calculate start/end minutes from Y position
*/
calculateTimesFromPosition(snappedY) {
const gridSettings = this.config.gridSettings;
const { hourHeight, dayStartHour, snapInterval } = gridSettings;
// Get original duration
const originalDuration = parseInt(this.dataset.originalDuration ||
this.dataset.duration ||
'60');
// Calculate snapped start minutes
const minutesFromGridStart = (snappedY / hourHeight) * 60;
const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart;
const snappedStartMinutes = Math.round(actualStartMinutes / snapInterval) * snapInterval;
// Calculate end minutes
const endMinutes = snappedStartMinutes + originalDuration;
return { startMinutes: snappedStartMinutes, endMinutes };
}
// ============================================
// Static Factory Methods
// ============================================
/**
* Create SwpEventElement from ICalendarEvent
*/
static fromCalendarEvent(event) {
const element = document.createElement('swp-event');
const config = Configuration.getInstance();
const dateService = new DateService(config);
element.dataset.eventId = event.id;
element.dataset.title = event.title;
element.dataset.description = event.description || '';
element.dataset.start = dateService.toUTC(event.start);
element.dataset.end = dateService.toUTC(event.end);
element.dataset.type = event.type;
element.dataset.duration = event.metadata?.duration?.toString() || '60';
return element;
}
/**
* Extract ICalendarEvent from DOM element
*/
static extractCalendarEventFromElement(element) {
return {
id: element.dataset.eventId || '',
title: element.dataset.title || '',
description: element.dataset.description || undefined,
start: new Date(element.dataset.start || ''),
end: new Date(element.dataset.end || ''),
type: element.dataset.type || 'work',
allDay: false,
syncStatus: 'synced',
metadata: {
duration: element.dataset.duration
}
};
}
}
/**
* Web Component for all-day calendar events
*/
export class SwpAllDayEventElement extends BaseSwpEventElement {
connectedCallback() {
if (!this.textContent) {
this.textContent = this.dataset.title || 'Untitled';
}
}
/**
* Create a clone for drag operations
*/
createClone() {
const clone = this.cloneNode(true);
// Apply "clone-" prefix to ID
clone.dataset.eventId = `clone-${this.eventId}`;
// Disable pointer events on clone so it doesn't interfere with hover detection
clone.style.pointerEvents = 'none';
// Preserve full opacity during drag
clone.style.opacity = '1';
return clone;
}
/**
* Apply CSS grid positioning
*/
applyGridPositioning(row, startColumn, endColumn) {
const gridArea = `${row} / ${startColumn} / ${row + 1} / ${endColumn + 1}`;
this.style.gridArea = gridArea;
}
/**
* Create from ICalendarEvent
*/
static fromCalendarEvent(event) {
const element = document.createElement('swp-allday-event');
const config = Configuration.getInstance();
const dateService = new DateService(config);
element.dataset.eventId = event.id;
element.dataset.title = event.title;
element.dataset.start = dateService.toUTC(event.start);
element.dataset.end = dateService.toUTC(event.end);
element.dataset.type = event.type;
element.dataset.allday = 'true';
element.textContent = event.title;
return element;
}
}
// Register custom elements
customElements.define('swp-event', SwpEventElement);
customElements.define('swp-allday-event', SwpAllDayEventElement);
//# sourceMappingURL=SwpEventElement.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,55 @@
import { CalendarMode } from '../types/CalendarTypes';
import { HeaderRenderer } from '../renderers/HeaderRenderer';
import { ColumnRenderer } from '../renderers/ColumnRenderer';
import { EventRendererStrategy } from '../renderers/EventRenderer';
/**
* Renderer configuration for a calendar type
*/
export interface RendererConfig {
headerRenderer: HeaderRenderer;
columnRenderer: ColumnRenderer;
eventRenderer: EventRendererStrategy;
}
/**
* Factory for creating calendar type-specific renderers
*/
export declare class CalendarTypeFactory {
private static renderers;
private static isInitialized;
/**
* Initialize the factory with default renderers (only runs once)
*/
static initialize(): void;
/**
* Register renderers for a calendar type
*/
static registerRenderers(type: CalendarMode, config: RendererConfig): void;
/**
* Get renderers for a calendar type
*/
static getRenderers(type: CalendarMode): RendererConfig;
/**
* Get header renderer for a calendar type
*/
static getHeaderRenderer(type: CalendarMode): HeaderRenderer;
/**
* Get column renderer for a calendar type
*/
static getColumnRenderer(type: CalendarMode): ColumnRenderer;
/**
* Get event renderer for a calendar type
*/
static getEventRenderer(type: CalendarMode): EventRendererStrategy;
/**
* Check if a calendar type is supported
*/
static isSupported(type: CalendarMode): boolean;
/**
* Get all supported calendar types
*/
static getSupportedTypes(): CalendarMode[];
/**
* Clear all registered renderers (useful for testing)
*/
static clear(): void;
}

View file

@ -0,0 +1,84 @@
// Factory for creating calendar type-specific renderers
import { DateHeaderRenderer, ResourceHeaderRenderer } from '../renderers/HeaderRenderer';
import { DateColumnRenderer, ResourceColumnRenderer } from '../renderers/ColumnRenderer';
import { DateEventRenderer, ResourceEventRenderer } from '../renderers/EventRenderer';
/**
* Factory for creating calendar type-specific renderers
*/
export class CalendarTypeFactory {
/**
* Initialize the factory with default renderers (only runs once)
*/
static initialize() {
if (this.isInitialized) {
return;
}
// Register default renderers
this.registerRenderers('date', {
headerRenderer: new DateHeaderRenderer(),
columnRenderer: new DateColumnRenderer(),
eventRenderer: new DateEventRenderer()
});
this.registerRenderers('resource', {
headerRenderer: new ResourceHeaderRenderer(),
columnRenderer: new ResourceColumnRenderer(),
eventRenderer: new ResourceEventRenderer()
});
this.isInitialized = true;
}
/**
* Register renderers for a calendar type
*/
static registerRenderers(type, config) {
this.renderers.set(type, config);
}
/**
* Get renderers for a calendar type
*/
static getRenderers(type) {
const renderers = this.renderers.get(type);
if (!renderers) {
return this.renderers.get('date');
}
return renderers;
}
/**
* Get header renderer for a calendar type
*/
static getHeaderRenderer(type) {
return this.getRenderers(type).headerRenderer;
}
/**
* Get column renderer for a calendar type
*/
static getColumnRenderer(type) {
return this.getRenderers(type).columnRenderer;
}
/**
* Get event renderer for a calendar type
*/
static getEventRenderer(type) {
return this.getRenderers(type).eventRenderer;
}
/**
* Check if a calendar type is supported
*/
static isSupported(type) {
return this.renderers.has(type);
}
/**
* Get all supported calendar types
*/
static getSupportedTypes() {
return Array.from(this.renderers.keys());
}
/**
* Clear all registered renderers (useful for testing)
*/
static clear() {
this.renderers.clear();
}
}
CalendarTypeFactory.renderers = new Map();
CalendarTypeFactory.isInitialized = false;
//# sourceMappingURL=CalendarTypeFactory.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"CalendarTypeFactory.js","sourceRoot":"","sources":["../../../src/factories/CalendarTypeFactory.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAkB,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACzG,OAAO,EAAkB,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACzG,OAAO,EAAyB,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAY7G;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAI9B;;OAEG;IACH,MAAM,CAAC,UAAU;QACf,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,6BAA6B;QAC7B,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE;YAC7B,cAAc,EAAE,IAAI,kBAAkB,EAAE;YACxC,cAAc,EAAE,IAAI,kBAAkB,EAAE;YACxC,aAAa,EAAE,IAAI,iBAAiB,EAAE;SACvC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE;YACjC,cAAc,EAAE,IAAI,sBAAsB,EAAE;YAC5C,cAAc,EAAE,IAAI,sBAAsB,EAAE;YAC5C,aAAa,EAAE,IAAI,qBAAqB,EAAE;SAC3C,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB,EAAE,MAAsB;QACjE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAkB;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE3C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QACrC,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAkB;QACxC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAkB;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB;QACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK;QACV,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;;AAvFc,6BAAS,GAAsC,IAAI,GAAG,EAAE,CAAC;AACzD,iCAAa,GAAY,KAAK,CAAC"}

View file

@ -0,0 +1,18 @@
import { IEventBus } from '../types/CalendarTypes';
import { CalendarManagers } from '../types/ManagerTypes';
/**
* Factory for creating and managing calendar managers with proper dependency injection
*/
export declare class ManagerFactory {
private static instance;
private constructor();
static getInstance(): ManagerFactory;
/**
* Create all managers with proper dependency injection
*/
createManagers(eventBus: IEventBus): CalendarManagers;
/**
* Initialize all managers in the correct order
*/
initializeManagers(managers: CalendarManagers): Promise<void>;
}

View file

@ -0,0 +1,60 @@
import { EventManager } from '../managers/EventManager';
import { EventRenderingService } from '../renderers/EventRendererManager';
import { GridManager } from '../managers/GridManager';
import { ScrollManager } from '../managers/ScrollManager';
import { NavigationManager } from '../managers/NavigationManager';
import { ViewManager } from '../managers/ViewManager';
import { CalendarManager } from '../managers/CalendarManager';
import { DragDropManager } from '../managers/DragDropManager';
import { AllDayManager } from '../managers/AllDayManager';
/**
* Factory for creating and managing calendar managers with proper dependency injection
*/
export class ManagerFactory {
constructor() { }
static getInstance() {
if (!ManagerFactory.instance) {
ManagerFactory.instance = new ManagerFactory();
}
return ManagerFactory.instance;
}
/**
* Create all managers with proper dependency injection
*/
createManagers(eventBus) {
// Create managers in dependency order
const eventManager = new EventManager(eventBus);
const eventRenderer = new EventRenderingService(eventBus, eventManager);
const gridManager = new GridManager();
const scrollManager = new ScrollManager();
const navigationManager = new NavigationManager(eventBus, eventRenderer);
const viewManager = new ViewManager(eventBus);
const dragDropManager = new DragDropManager(eventBus);
const allDayManager = new AllDayManager();
// CalendarManager depends on all other managers
const calendarManager = new CalendarManager(eventBus, eventManager, gridManager, eventRenderer, scrollManager);
return {
eventManager,
eventRenderer,
gridManager,
scrollManager,
navigationManager,
viewManager,
calendarManager,
dragDropManager,
allDayManager
};
}
/**
* Initialize all managers in the correct order
*/
async initializeManagers(managers) {
try {
await managers.calendarManager.initialize?.();
}
catch (error) {
throw error;
}
}
}
//# sourceMappingURL=ManagerFactory.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ManagerFactory.js","sourceRoot":"","sources":["../../../src/factories/ManagerFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1D;;GAEG;AACH,MAAM,OAAO,cAAc;IAGzB,gBAAuB,CAAC;IAEjB,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;YAC7B,cAAc,CAAC,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;QACjD,CAAC;QACD,OAAO,cAAc,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,QAAmB;QAEvC,sCAAsC;QACtC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,IAAI,qBAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACxE,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;QAC1C,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACzE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;QAE1C,gDAAgD;QAChD,MAAM,eAAe,GAAG,IAAI,eAAe,CACzC,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,aAAa,EACb,aAAa,CACd,CAAC;QAGF,OAAO;YACL,YAAY;YACZ,aAAa;YACb,WAAW;YACX,aAAa;YACb,iBAAiB;YACjB,WAAW;YACX,eAAe;YACf,eAAe;YACf,aAAa;SACd,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,kBAAkB,CAAC,QAA0B;QAExD,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,eAAe,CAAC,UAAU,EAAE,EAAE,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}

View file

@ -0,0 +1,45 @@
/**
* AllDayCollapseService - Manages collapse/expand UI for all-day events
*
* STATELESS SERVICE - Reads expanded state from DOM via AllDayDomReader
* - No persistent state
* - Reads expanded state from DOM CSS class
* - Updates chevron button and overflow indicators
* - Controls event visibility based on row number
*/
import { AllDayHeightService } from './AllDayHeightService';
export declare class AllDayCollapseService {
private heightService;
constructor(heightService: AllDayHeightService);
/**
* Toggle between expanded and collapsed state
* Reads current state from DOM, toggles it, and updates UI
*/
toggleExpanded(): void;
/**
* Update all UI elements based on current DOM state
*/
private updateUI;
/**
* Update event visibility based on expanded state
*/
private updateEventVisibility;
/**
* Update chevron button visibility and state
*/
private updateChevronButton;
/**
* Update overflow indicators for collapsed state
* Shows "+X more" indicators in columns with overflow
*/
private updateOverflowIndicators;
/**
* Clear all overflow indicators
*/
private clearOverflowIndicators;
/**
* Initialize collapse/expand UI based on current DOM state
* Called after events are rendered
*/
initializeUI(): void;
}

View file

@ -0,0 +1,168 @@
/**
* AllDayCollapseService - Manages collapse/expand UI for all-day events
*
* STATELESS SERVICE - Reads expanded state from DOM via AllDayDomReader
* - No persistent state
* - Reads expanded state from DOM CSS class
* - Updates chevron button and overflow indicators
* - Controls event visibility based on row number
*/
import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig';
import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils';
import { AllDayDomReader } from './AllDayDomReader';
export class AllDayCollapseService {
constructor(heightService) {
this.heightService = heightService;
}
/**
* Toggle between expanded and collapsed state
* Reads current state from DOM, toggles it, and updates UI
*/
toggleExpanded() {
const container = AllDayDomReader.getAllDayContainer();
if (!container)
return;
// Read current state from DOM
const isCurrentlyExpanded = container.classList.contains('expanded');
// Toggle state in DOM
if (isCurrentlyExpanded) {
container.classList.remove('expanded');
}
else {
container.classList.add('expanded');
}
// Update UI based on new state
this.updateUI();
}
/**
* Update all UI elements based on current DOM state
*/
updateUI() {
const isExpanded = AllDayDomReader.isExpanded();
const maxRows = AllDayDomReader.getMaxRowFromEvents();
// Update chevron button
if (maxRows > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) {
this.updateChevronButton(true, isExpanded);
if (isExpanded) {
this.clearOverflowIndicators();
}
else {
this.updateOverflowIndicators();
}
}
else {
this.updateChevronButton(false, isExpanded);
this.clearOverflowIndicators();
}
// Update event visibility
this.updateEventVisibility(isExpanded);
// Calculate height based on expanded state
// When collapsed, show max MAX_COLLAPSED_ROWS, when expanded show all rows
const targetRows = isExpanded ? maxRows : Math.min(maxRows, ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS);
this.heightService.animateToRows(targetRows);
}
/**
* Update event visibility based on expanded state
*/
updateEventVisibility(isExpanded) {
const events = AllDayDomReader.getEventElements();
events.forEach(event => {
const row = AllDayDomReader.getGridRow(event);
if (row > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) {
if (isExpanded) {
event.classList.remove('max-event-overflow-hide');
event.classList.add('max-event-overflow-show');
}
else {
event.classList.remove('max-event-overflow-show');
event.classList.add('max-event-overflow-hide');
}
}
});
}
/**
* Update chevron button visibility and state
*/
updateChevronButton(show, isExpanded) {
const headerSpacer = AllDayDomReader.getHeaderSpacer();
if (!headerSpacer)
return;
let chevron = headerSpacer.querySelector('.allday-chevron');
if (show && !chevron) {
// Create chevron button
chevron = document.createElement('button');
chevron.className = 'allday-chevron collapsed';
chevron.innerHTML = `
<svg width="12" height="8" viewBox="0 0 12 8">
<path d="M1 1.5L6 6.5L11 1.5" stroke="currentColor" stroke-width="2" fill="none"/>
</svg>
`;
chevron.onclick = () => this.toggleExpanded();
headerSpacer.appendChild(chevron);
}
else if (!show && chevron) {
// Remove chevron button
chevron.remove();
}
else if (chevron) {
// Update chevron state
chevron.classList.toggle('collapsed', !isExpanded);
chevron.classList.toggle('expanded', isExpanded);
}
}
/**
* Update overflow indicators for collapsed state
* Shows "+X more" indicators in columns with overflow
*/
updateOverflowIndicators() {
const container = AllDayDomReader.getAllDayContainer();
if (!container)
return;
const columns = ColumnDetectionUtils.getColumns();
columns.forEach((columnBounds) => {
const totalEventsInColumn = AllDayDomReader.countEventsInColumn(columnBounds.index);
const overflowCount = totalEventsInColumn - ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS;
if (overflowCount > 0) {
// Check if indicator already exists
let existingIndicator = container.querySelector(`.max-event-indicator[data-column="${columnBounds.index}"]`);
if (existingIndicator) {
// Update existing indicator
existingIndicator.innerHTML = `<span>+${overflowCount + 1} more</span>`;
}
else {
// Create new overflow indicator
const overflowElement = document.createElement('swp-allday-event');
overflowElement.className = 'max-event-indicator';
overflowElement.setAttribute('data-column', columnBounds.index.toString());
overflowElement.style.gridRow = ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS.toString();
overflowElement.style.gridColumn = columnBounds.index.toString();
overflowElement.innerHTML = `<span>+${overflowCount + 1} more</span>`;
overflowElement.onclick = (e) => {
e.stopPropagation();
this.toggleExpanded();
};
container.appendChild(overflowElement);
}
}
});
}
/**
* Clear all overflow indicators
*/
clearOverflowIndicators() {
const container = AllDayDomReader.getAllDayContainer();
if (!container)
return;
container.querySelectorAll('.max-event-indicator').forEach((element) => {
element.remove();
});
}
/**
* Initialize collapse/expand UI based on current DOM state
* Called after events are rendered
*/
initializeUI() {
this.updateUI();
}
}
//# sourceMappingURL=AllDayCollapseService.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"AllDayCollapseService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayCollapseService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAiB,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAEvF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,OAAO,qBAAqB;IAGhC,YAAY,aAAkC;QAC5C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED;;;OAGG;IACI,cAAc;QACnB,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,EAAE,CAAC;QACvD,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,8BAA8B;QAC9B,MAAM,mBAAmB,GAAG,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAErE,sBAAsB;QACtB,IAAI,mBAAmB,EAAE,CAAC;YACxB,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,QAAQ;QACd,MAAM,UAAU,GAAG,eAAe,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,eAAe,CAAC,mBAAmB,EAAE,CAAC;QAEtD,wBAAwB;QACxB,IAAI,OAAO,GAAG,iBAAiB,CAAC,kBAAkB,EAAE,CAAC;YACnD,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAE3C,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAClC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;QAED,0BAA0B;QAC1B,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAEvC,2CAA2C;QAC3C,2EAA2E;QAC3E,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;QAClG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,UAAmB;QAC/C,MAAM,MAAM,GAAG,eAAe,CAAC,gBAAgB,EAAE,CAAC;QAElD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACrB,MAAM,GAAG,GAAG,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAE9C,IAAI,GAAG,GAAG,iBAAiB,CAAC,kBAAkB,EAAE,CAAC;gBAC/C,IAAI,UAAU,EAAE,CAAC;oBACf,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;oBAClD,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;gBACjD,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;oBAClD,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,IAAa,EAAE,UAAmB;QAC5D,MAAM,YAAY,GAAG,eAAe,CAAC,eAAe,EAAE,CAAC;QACvD,IAAI,CAAC,YAAY;YAAE,OAAO;QAE1B,IAAI,OAAO,GAAG,YAAY,CAAC,aAAa,CAAC,iBAAiB,CAAgB,CAAC;QAE3E,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,wBAAwB;YACxB,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC3C,OAAO,CAAC,SAAS,GAAG,0BAA0B,CAAC;YAC/C,OAAO,CAAC,SAAS,GAAG;;;;OAInB,CAAC;YACF,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC9C,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;aAAM,IAAI,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;YAC5B,wBAAwB;YACxB,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,CAAC;aAAM,IAAI,OAAO,EAAE,CAAC;YACnB,uBAAuB;YACvB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,wBAAwB;QAC9B,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,EAAE,CAAC;QACvD,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,OAAO,GAAG,oBAAoB,CAAC,UAAU,EAAE,CAAC;QAElD,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;YAC/B,MAAM,mBAAmB,GAAG,eAAe,CAAC,mBAAmB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACpF,MAAM,aAAa,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,kBAAkB,CAAC;YAEjF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,oCAAoC;gBACpC,IAAI,iBAAiB,GAAG,SAAS,CAAC,aAAa,CAC7C,qCAAqC,YAAY,CAAC,KAAK,IAAI,CAC7C,CAAC;gBAEjB,IAAI,iBAAiB,EAAE,CAAC;oBACtB,4BAA4B;oBAC5B,iBAAiB,CAAC,SAAS,GAAG,UAAU,aAAa,GAAG,CAAC,cAAc,CAAC;gBAC1E,CAAC;qBAAM,CAAC;oBACN,gCAAgC;oBAChC,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;oBACnE,eAAe,CAAC,SAAS,GAAG,qBAAqB,CAAC;oBAClD,eAAe,CAAC,YAAY,CAAC,aAAa,EAAE,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC3E,eAAe,CAAC,KAAK,CAAC,OAAO,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,CAAC;oBAChF,eAAe,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACjE,eAAe,CAAC,SAAS,GAAG,UAAU,aAAa,GAAG,CAAC,cAAc,CAAC;oBACtE,eAAe,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE;wBAC9B,CAAC,CAAC,eAAe,EAAE,CAAC;wBACpB,IAAI,CAAC,cAAc,EAAE,CAAC;oBACxB,CAAC,CAAC;oBAEF,SAAS,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC7B,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,EAAE,CAAC;QACvD,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,SAAS,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAgB,EAAE,EAAE;YAC9E,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,YAAY;QACjB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;CACF"}

View file

@ -0,0 +1,45 @@
/**
* AllDayCoordinator - Orchestrates all-day event functionality
*
* NO STATE - Only coordinates between services
* - Listens to EventBus events
* - Delegates to specialized services
* - Manages service lifecycle
*/
import { AllDayEventRenderer } from '../../renderers/AllDayEventRenderer';
import { EventManager } from '../../managers/EventManager';
import { DateService } from '../../utils/DateService';
import { AllDayHeightService } from './AllDayHeightService';
import { AllDayCollapseService } from './AllDayCollapseService';
import { AllDayDragService } from './AllDayDragService';
/**
* AllDayCoordinator - Orchestrates all-day event functionality
* Replaces the monolithic AllDayManager with a coordinated service architecture
*/
export declare class AllDayCoordinator {
private allDayEventRenderer;
private eventManager;
private dateService;
private heightService;
private collapseService;
private dragService;
constructor(eventManager: EventManager, allDayEventRenderer: AllDayEventRenderer, dateService: DateService, heightService: AllDayHeightService, collapseService: AllDayCollapseService, dragService: AllDayDragService);
/**
* Setup event listeners and delegate to services
*/
private setupEventListeners;
/**
* Calculate layout for ALL all-day events using AllDayLayoutEngine
*/
private calculateAllDayEventsLayout;
/**
* Recalculate layouts and update height
* Called after events are added/removed/moved in all-day area
* Uses AllDayLayoutEngine to optimally reorganize all events
*/
private recalculateLayoutsAndHeight;
/**
* Public API for collapsing all-day row
*/
collapseAllDayRow(): void;
}

View file

@ -0,0 +1,168 @@
/**
* AllDayCoordinator - Orchestrates all-day event functionality
*
* NO STATE - Only coordinates between services
* - Listens to EventBus events
* - Delegates to specialized services
* - Manages service lifecycle
*/
import { eventBus } from '../../core/EventBus';
import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig';
import { AllDayLayoutEngine } from '../../utils/AllDayLayoutEngine';
import { CoreEvents } from '../../constants/CoreEvents';
import { AllDayDomReader } from './AllDayDomReader';
import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils';
/**
* AllDayCoordinator - Orchestrates all-day event functionality
* Replaces the monolithic AllDayManager with a coordinated service architecture
*/
export class AllDayCoordinator {
constructor(eventManager, allDayEventRenderer, dateService, heightService, collapseService, dragService) {
this.eventManager = eventManager;
this.allDayEventRenderer = allDayEventRenderer;
this.dateService = dateService;
this.heightService = heightService;
this.collapseService = collapseService;
this.dragService = dragService;
// Sync CSS variable with TypeScript constant
document.documentElement.style.setProperty('--single-row-height', `${ALL_DAY_CONSTANTS.EVENT_HEIGHT}px`);
this.setupEventListeners();
}
/**
* Setup event listeners and delegate to services
*/
setupEventListeners() {
// Timed → All-day conversion
eventBus.on('drag:mouseenter-header', (event) => {
const payload = event.detail;
if (payload.draggedClone.hasAttribute('data-allday'))
return;
console.log('🔄 AllDayCoordinator: Received drag:mouseenter-header', {
targetDate: payload.targetColumn,
originalElementId: payload.originalElement?.dataset?.eventId,
originalElementTag: payload.originalElement?.tagName
});
this.dragService.handleConvertToAllDay(payload);
// Recalculate layouts and height after timed → all-day conversion
this.recalculateLayoutsAndHeight();
});
eventBus.on('drag:mouseleave-header', (event) => {
const { originalElement } = event.detail;
console.log('🚪 AllDayCoordinator: Received drag:mouseleave-header', {
originalElementId: originalElement?.dataset?.eventId
});
});
// All-day drag start
eventBus.on('drag:start', (event) => {
const payload = event.detail;
if (!payload.draggedClone?.hasAttribute('data-allday'))
return;
this.allDayEventRenderer.handleDragStart(payload);
});
// All-day column change
eventBus.on('drag:column-change', (event) => {
const payload = event.detail;
if (!payload.draggedClone?.hasAttribute('data-allday'))
return;
this.dragService.handleColumnChange(payload);
});
// Drag end
eventBus.on('drag:end', (event) => {
const dragEndPayload = event.detail;
console.log('🎯 AllDayCoordinator: drag:end received', {
target: dragEndPayload.target,
originalElementTag: dragEndPayload.originalElement?.tagName,
hasAllDayAttribute: dragEndPayload.originalElement?.hasAttribute('data-allday'),
eventId: dragEndPayload.originalElement?.dataset.eventId
});
// Handle all-day → all-day drops (within header)
if (dragEndPayload.target === 'swp-day-header') {
console.log('✅ AllDayCoordinator: Handling all-day → all-day drop');
this.dragService.handleDragEnd(dragEndPayload);
// Recalculate layouts and height after all-day → all-day repositioning
this.recalculateLayoutsAndHeight();
return;
}
// Handle all-day → timed conversion (dropped in column)
if (dragEndPayload.target === 'swp-day-column' &&
dragEndPayload.originalElement?.hasAttribute('data-allday')) {
const eventId = dragEndPayload.originalElement.dataset.eventId;
console.log('🔄 AllDayCoordinator: All-day → timed conversion', {
eventId
});
// Remove event element from DOM
const container = AllDayDomReader.getAllDayContainer();
const eventElement = container?.querySelector(`[data-event-id="${eventId}"]`);
if (eventElement) {
eventElement.remove();
}
// Recalculate layouts and height after event removal
this.recalculateLayoutsAndHeight();
}
});
// Drag cancelled
eventBus.on('drag:cancelled', (event) => {
const { draggedElement, reason } = event.detail;
console.log('🚫 AllDayCoordinator: Drag cancelled', {
eventId: draggedElement?.dataset?.eventId,
reason
});
});
// Header ready - render all-day events
eventBus.on('header:ready', async (event) => {
const headerReadyEventPayload = event.detail;
const startDate = new Date(headerReadyEventPayload.headerElements.at(0).date);
const endDate = new Date(headerReadyEventPayload.headerElements.at(-1).date);
const events = await this.eventManager.getEventsForPeriod(startDate, endDate);
// Filter for all-day events
const allDayEvents = events.filter(event => event.allDay);
// Calculate layouts
const layouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements);
// Render events
this.allDayEventRenderer.renderAllDayEventsForPeriod(layouts);
// Initialize collapse/expand UI and calculate height
this.collapseService.initializeUI();
});
// View changed
eventBus.on(CoreEvents.VIEW_CHANGED, (event) => {
this.allDayEventRenderer.handleViewChanged(event);
});
}
/**
* Calculate layout for ALL all-day events using AllDayLayoutEngine
*/
calculateAllDayEventsLayout(events, dayHeaders) {
// Initialize layout engine with provided week dates
const layoutEngine = new AllDayLayoutEngine(dayHeaders.map(column => column.date));
// Calculate layout for all events together
return layoutEngine.calculateLayout(events);
}
/**
* Recalculate layouts and update height
* Called after events are added/removed/moved in all-day area
* Uses AllDayLayoutEngine to optimally reorganize all events
*/
recalculateLayoutsAndHeight() {
// 1. Read current events from DOM
const events = AllDayDomReader.getEventsAsData();
const weekDates = ColumnDetectionUtils.getColumns();
// 2. Calculate optimal layouts using greedy algorithm
const layouts = this.calculateAllDayEventsLayout(events, weekDates);
// 3. Apply layouts to DOM
this.dragService.applyLayoutUpdates(layouts);
// 4. Calculate max row from NEW layouts
const maxRow = layouts.length > 0 ? Math.max(...layouts.map(l => l.row)) : 0;
// 5. Check if collapsed state should be maintained
const isExpanded = AllDayDomReader.isExpanded();
const targetRows = isExpanded ? maxRow : Math.min(maxRow, ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS);
// 6. Animate height to target
this.heightService.animateToRows(targetRows);
}
/**
* Public API for collapsing all-day row
*/
collapseAllDayRow() {
this.heightService.collapseAllDayRow();
}
}
//# sourceMappingURL=AllDayCoordinator.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,74 @@
import { ICalendarEvent } from '../../types/CalendarTypes';
/**
* AllDayDomReader - Centralized DOM reading utilities for all-day services
*
* STATELESS UTILITY - Pure functions for reading DOM state
* - Consistent selectors across all services
* - Unified computed style approach (not inline styles)
* - Type-safe return values
* - Single source of truth for DOM queries
*/
export declare class AllDayDomReader {
/**
* Get the all-day events container element
*/
static getAllDayContainer(): HTMLElement | null;
/**
* Get the calendar header element
*/
static getCalendarHeader(): HTMLElement | null;
/**
* Get the header spacer element
*/
static getHeaderSpacer(): HTMLElement | null;
/**
* Get all all-day event elements (excluding overflow indicators)
* Returns raw HTMLElements for DOM manipulation
*/
static getEventElements(): HTMLElement[];
/**
* Get all-day events as ICalendarEvent objects
* Returns parsed data for business logic
*/
static getEventsAsData(): ICalendarEvent[];
/**
* Get grid row from element using computed style
* Always uses computed style for consistency
*/
static getGridRow(element: HTMLElement): number;
/**
* Get grid column range from element using computed style
*/
static getGridColumnRange(element: HTMLElement): {
start: number;
end: number;
};
/**
* Get grid area from element using computed style
*/
static getGridArea(element: HTMLElement): string;
/**
* Calculate max row number from all events
* Uses computed styles for accurate reading
*/
static getMaxRowFromEvents(): number;
/**
* Check if all-day container is expanded
*/
static isExpanded(): boolean;
/**
* Get current all-day height from CSS variable
*/
static getCurrentHeight(): number;
/**
* Count events in specific column
*/
static countEventsInColumn(columnIndex: number): number;
/**
* Get current layouts from DOM elements
* Returns map of eventId layout info for comparison
*/
static getCurrentLayouts(): Map<string, {
gridArea: string;
}>;
}

View file

@ -0,0 +1,175 @@
/**
* AllDayDomReader - Centralized DOM reading utilities for all-day services
*
* STATELESS UTILITY - Pure functions for reading DOM state
* - Consistent selectors across all services
* - Unified computed style approach (not inline styles)
* - Type-safe return values
* - Single source of truth for DOM queries
*/
export class AllDayDomReader {
// ============================================
// CONTAINER GETTERS
// ============================================
/**
* Get the all-day events container element
*/
static getAllDayContainer() {
return document.querySelector('swp-calendar-header swp-allday-container');
}
/**
* Get the calendar header element
*/
static getCalendarHeader() {
return document.querySelector('swp-calendar-header');
}
/**
* Get the header spacer element
*/
static getHeaderSpacer() {
return document.querySelector('swp-header-spacer');
}
// ============================================
// EVENT ELEMENT GETTERS
// ============================================
/**
* Get all all-day event elements (excluding overflow indicators)
* Returns raw HTMLElements for DOM manipulation
*/
static getEventElements() {
const container = this.getAllDayContainer();
if (!container)
return [];
return Array.from(container.querySelectorAll('swp-allday-event:not(.max-event-indicator)'));
}
/**
* Get all-day events as ICalendarEvent objects
* Returns parsed data for business logic
*/
static getEventsAsData() {
const elements = this.getEventElements();
return elements
.map(element => {
const eventId = element.dataset.eventId;
const startStr = element.dataset.start;
const endStr = element.dataset.end;
// Validate required fields
if (!eventId || !startStr || !endStr) {
console.warn('AllDayDomReader: Invalid event data in DOM:', element);
return null;
}
const start = new Date(startStr);
const end = new Date(endStr);
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
console.warn('AllDayDomReader: Invalid event dates:', { startStr, endStr });
return null;
}
return {
id: eventId,
title: element.dataset.title || '',
start,
end,
type: element.dataset.type || 'task',
allDay: true,
syncStatus: (element.dataset.syncStatus || 'synced')
};
})
.filter((event) => event !== null);
}
// ============================================
// GRID POSITION READERS
// ============================================
/**
* Get grid row from element using computed style
* Always uses computed style for consistency
*/
static getGridRow(element) {
const computedStyle = window.getComputedStyle(element);
return parseInt(computedStyle.gridRowStart) || 0;
}
/**
* Get grid column range from element using computed style
*/
static getGridColumnRange(element) {
const computedStyle = window.getComputedStyle(element);
return {
start: parseInt(computedStyle.gridColumnStart) || 0,
end: parseInt(computedStyle.gridColumnEnd) || 0
};
}
/**
* Get grid area from element using computed style
*/
static getGridArea(element) {
const computedStyle = window.getComputedStyle(element);
return computedStyle.gridArea;
}
/**
* Calculate max row number from all events
* Uses computed styles for accurate reading
*/
static getMaxRowFromEvents() {
const events = this.getEventElements();
if (events.length === 0)
return 0;
let maxRow = 0;
events.forEach(event => {
const row = this.getGridRow(event);
maxRow = Math.max(maxRow, row);
});
return maxRow;
}
// ============================================
// STATE READERS
// ============================================
/**
* Check if all-day container is expanded
*/
static isExpanded() {
const container = this.getAllDayContainer();
return container?.classList.contains('expanded') || false;
}
/**
* Get current all-day height from CSS variable
*/
static getCurrentHeight() {
const root = document.documentElement;
const heightStr = root.style.getPropertyValue('--all-day-row-height') || '0px';
return parseInt(heightStr) || 0;
}
/**
* Count events in specific column
*/
static countEventsInColumn(columnIndex) {
const events = this.getEventElements();
let count = 0;
events.forEach((event) => {
const { start, end } = this.getGridColumnRange(event);
if (start <= columnIndex && end > columnIndex) {
count++;
}
});
return count;
}
// ============================================
// LAYOUT READERS
// ============================================
/**
* Get current layouts from DOM elements
* Returns map of eventId layout info for comparison
*/
static getCurrentLayouts() {
const layoutsMap = new Map();
const events = this.getEventElements();
events.forEach(event => {
const eventId = event.dataset.eventId;
if (eventId) {
layoutsMap.set(eventId, {
gridArea: this.getGridArea(event)
});
}
});
return layoutsMap;
}
}
//# sourceMappingURL=AllDayDomReader.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"AllDayDomReader.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayDomReader.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,50 @@
/**
* AllDayDragService - Manages drag and drop operations for all-day events
*
* STATELESS SERVICE - Reads all data from DOM via AllDayDomReader
* - No persistent state
* - Handles timed all-day conversion
* - Handles all-day all-day repositioning
* - Handles column changes during drag
* - Calculates layouts on-demand from DOM
*/
import { IEventLayout } from '../../utils/AllDayLayoutEngine';
import { IDragMouseEnterHeaderEventPayload, IDragColumnChangeEventPayload, IDragEndEventPayload } from '../../types/EventTypes';
import { EventManager } from '../../managers/EventManager';
import { AllDayEventRenderer } from '../../renderers/AllDayEventRenderer';
import { DateService } from '../../utils/DateService';
export declare class AllDayDragService {
private eventManager;
private allDayEventRenderer;
private dateService;
constructor(eventManager: EventManager, allDayEventRenderer: AllDayEventRenderer, dateService: DateService);
/**
* Handle conversion from timed event to all-day event
* Called when dragging a timed event into the header
*/
handleConvertToAllDay(payload: IDragMouseEnterHeaderEventPayload): void;
/**
* Handle column change during drag of all-day event
* Updates grid position while maintaining event span
*/
handleColumnChange(payload: IDragColumnChangeEventPayload): void;
/**
* Handle drag end for all-day all-day drops
* Recalculates layouts and updates event positions
*/
handleDragEnd(dragEndEvent: IDragEndEventPayload): Promise<void>;
/**
* Calculate layouts for events using AllDayLayoutEngine
*/
private calculateLayouts;
/**
* Apply layout updates to DOM elements
* Only updates elements that have changed position
* Public so AllDayCoordinator can use it for full recalculation
*/
applyLayoutUpdates(newLayouts: IEventLayout[]): void;
/**
* Fade out and remove element
*/
private fadeOutAndRemove;
}

View file

@ -0,0 +1,183 @@
/**
* AllDayDragService - Manages drag and drop operations for all-day events
*
* STATELESS SERVICE - Reads all data from DOM via AllDayDomReader
* - No persistent state
* - Handles timed all-day conversion
* - Handles all-day all-day repositioning
* - Handles column changes during drag
* - Calculates layouts on-demand from DOM
*/
import { SwpAllDayEventElement } from '../../elements/SwpEventElement';
import { AllDayLayoutEngine } from '../../utils/AllDayLayoutEngine';
import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils';
import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig';
import { AllDayDomReader } from './AllDayDomReader';
export class AllDayDragService {
constructor(eventManager, allDayEventRenderer, dateService) {
this.eventManager = eventManager;
this.allDayEventRenderer = allDayEventRenderer;
this.dateService = dateService;
}
/**
* Handle conversion from timed event to all-day event
* Called when dragging a timed event into the header
*/
handleConvertToAllDay(payload) {
const allDayContainer = AllDayDomReader.getAllDayContainer();
if (!allDayContainer)
return;
// Create SwpAllDayEventElement from ICalendarEvent
const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent);
// Apply grid positioning
allDayElement.style.gridRow = '1';
allDayElement.style.gridColumn = payload.targetColumn.index.toString();
// Remove old swp-event clone
payload.draggedClone.remove();
// Call delegate to update DragDropManager's draggedClone reference
payload.replaceClone(allDayElement);
// Append to container
allDayContainer.appendChild(allDayElement);
ColumnDetectionUtils.updateColumnBoundsCache();
}
/**
* Handle column change during drag of all-day event
* Updates grid position while maintaining event span
*/
handleColumnChange(payload) {
const allDayContainer = AllDayDomReader.getAllDayContainer();
if (!allDayContainer)
return;
const targetColumn = ColumnDetectionUtils.getColumnBounds(payload.mousePosition);
if (!targetColumn || !payload.draggedClone)
return;
// Calculate event span from original grid positioning
const { start: gridColumnStart, end: gridColumnEnd } = AllDayDomReader.getGridColumnRange(payload.draggedClone);
const span = gridColumnEnd - gridColumnStart;
// Update clone position maintaining the span
const newStartColumn = targetColumn.index;
const newEndColumn = newStartColumn + span;
payload.draggedClone.style.gridColumn = `${newStartColumn} / ${newEndColumn}`;
}
/**
* Handle drag end for all-day all-day drops
* Recalculates layouts and updates event positions
*/
async handleDragEnd(dragEndEvent) {
if (!dragEndEvent.draggedClone)
return;
// Normalize clone ID
dragEndEvent.draggedClone.dataset.eventId = dragEndEvent.draggedClone.dataset.eventId?.replace('clone-', '');
dragEndEvent.draggedClone.style.pointerEvents = ''; // Re-enable pointer events
dragEndEvent.originalElement.dataset.eventId += '_';
const eventId = dragEndEvent.draggedClone.dataset.eventId;
const eventDate = dragEndEvent.finalPosition.column?.date;
const eventType = dragEndEvent.draggedClone.dataset.type;
if (!eventDate || !eventId || !eventType)
return;
// Get original dates to preserve time
const originalStartDate = new Date(dragEndEvent.draggedClone.dataset.start);
const originalEndDate = new Date(dragEndEvent.draggedClone.dataset.end);
// Calculate actual duration in milliseconds (preserves hours/minutes/seconds)
const durationMs = originalEndDate.getTime() - originalStartDate.getTime();
// Create new start date with the new day but preserve original time
const newStartDate = new Date(eventDate);
newStartDate.setHours(originalStartDate.getHours(), originalStartDate.getMinutes(), originalStartDate.getSeconds(), originalStartDate.getMilliseconds());
// Create new end date by adding duration in milliseconds
const newEndDate = new Date(newStartDate.getTime() + durationMs);
// Update data attributes with new dates (convert to UTC)
dragEndEvent.draggedClone.dataset.start = this.dateService.toUTC(newStartDate);
dragEndEvent.draggedClone.dataset.end = this.dateService.toUTC(newEndDate);
const droppedEvent = {
id: eventId,
title: dragEndEvent.draggedClone.dataset.title || '',
start: newStartDate,
end: newEndDate,
type: eventType,
allDay: true,
syncStatus: 'synced'
};
// Get all events from DOM and recalculate layouts
const allEventsFromDOM = AllDayDomReader.getEventsAsData();
const weekDates = ColumnDetectionUtils.getColumns();
// Replace old event with dropped event
const updatedEvents = [
...allEventsFromDOM.filter(event => event.id !== eventId),
droppedEvent
];
// Calculate new layouts for ALL events
const newLayouts = this.calculateLayouts(updatedEvents, weekDates);
// Apply layout updates to DOM
this.applyLayoutUpdates(newLayouts);
// Clean up drag styles from the dropped clone
dragEndEvent.draggedClone.classList.remove('dragging');
dragEndEvent.draggedClone.style.zIndex = '';
dragEndEvent.draggedClone.style.cursor = '';
dragEndEvent.draggedClone.style.opacity = '';
// Apply highlight class to show the dropped event with highlight color
dragEndEvent.draggedClone.classList.add('highlight');
// Update event in repository to mark as allDay=true
await this.eventManager.updateEvent(eventId, {
start: newStartDate,
end: newEndDate,
allDay: true
});
this.fadeOutAndRemove(dragEndEvent.originalElement);
}
/**
* Calculate layouts for events using AllDayLayoutEngine
*/
calculateLayouts(events, weekDates) {
const layoutEngine = new AllDayLayoutEngine(weekDates.map(column => column.date));
return layoutEngine.calculateLayout(events);
}
/**
* Apply layout updates to DOM elements
* Only updates elements that have changed position
* Public so AllDayCoordinator can use it for full recalculation
*/
applyLayoutUpdates(newLayouts) {
const container = AllDayDomReader.getAllDayContainer();
if (!container)
return;
// Read current layouts from DOM
const currentLayoutsMap = AllDayDomReader.getCurrentLayouts();
newLayouts.forEach((layout) => {
const currentLayout = currentLayoutsMap.get(layout.calenderEvent.id);
// Only update if layout changed
if (currentLayout?.gridArea !== layout.gridArea) {
const element = container.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`);
if (element) {
element.classList.add('transitioning');
element.style.gridArea = layout.gridArea;
element.style.gridRow = layout.row.toString();
element.style.gridColumn = `${layout.startColumn} / ${layout.endColumn + 1}`;
// Update overflow classes based on row
element.classList.remove('max-event-overflow-hide', 'max-event-overflow-show');
if (layout.row > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) {
const isExpanded = AllDayDomReader.isExpanded();
if (isExpanded) {
element.classList.add('max-event-overflow-show');
}
else {
element.classList.add('max-event-overflow-hide');
}
}
// Remove transition class after animation
setTimeout(() => element.classList.remove('transitioning'), 200);
}
}
});
}
/**
* Fade out and remove element
*/
fadeOutAndRemove(element) {
element.style.transition = 'opacity 0.3s ease-out';
element.style.opacity = '0';
setTimeout(() => {
element.remove();
}, 300);
}
}
//# sourceMappingURL=AllDayDragService.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,26 @@
/**
* AllDayHeightService - Manages all-day row height calculations and animations
*
* STATELESS SERVICE - Reads all data from DOM via AllDayDomReader
* - No persistent state
* - Calculates required rows by reading DOM elements
* - Animates header height based on DOM state
*/
export declare class AllDayHeightService {
/**
* Main entry point - recalculate and animate header height based on DOM
*/
recalculateAndAnimate(): void;
/**
* Animate all-day container to specific number of rows
*/
animateToRows(targetRows: number): void;
/**
* Calculate all-day height based on number of rows
*/
private calculateAllDayHeight;
/**
* Collapse all-day row (animate to 0 rows)
*/
collapseAllDayRow(): void;
}

View file

@ -0,0 +1,85 @@
/**
* AllDayHeightService - Manages all-day row height calculations and animations
*
* STATELESS SERVICE - Reads all data from DOM via AllDayDomReader
* - No persistent state
* - Calculates required rows by reading DOM elements
* - Animates header height based on DOM state
*/
import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig';
import { eventBus } from '../../core/EventBus';
import { AllDayDomReader } from './AllDayDomReader';
export class AllDayHeightService {
/**
* Main entry point - recalculate and animate header height based on DOM
*/
recalculateAndAnimate() {
const requiredRows = AllDayDomReader.getMaxRowFromEvents();
this.animateToRows(requiredRows);
}
/**
* Animate all-day container to specific number of rows
*/
animateToRows(targetRows) {
const { targetHeight, currentHeight, heightDifference } = this.calculateAllDayHeight(targetRows);
if (targetHeight === currentHeight)
return; // No animation needed
console.log(`🎬 All-day height animation: ${currentHeight}px → ${targetHeight}px (${Math.ceil(currentHeight / ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT)}${targetRows} rows)`);
// Get elements
const calendarHeader = AllDayDomReader.getCalendarHeader();
const headerSpacer = AllDayDomReader.getHeaderSpacer();
const allDayContainer = AllDayDomReader.getAllDayContainer();
if (!calendarHeader || !allDayContainer)
return;
// Get current parent height for animation
const currentParentHeight = parseFloat(getComputedStyle(calendarHeader).height);
const targetParentHeight = currentParentHeight + heightDifference;
const animations = [
calendarHeader.animate([
{ height: `${currentParentHeight}px` },
{ height: `${targetParentHeight}px` }
], {
duration: 150,
easing: 'ease-out',
fill: 'forwards'
})
];
// Add spacer animation if spacer exists
if (headerSpacer) {
const root = document.documentElement;
const headerHeightStr = root.style.getPropertyValue('--header-height');
const headerHeight = parseInt(headerHeightStr);
const currentSpacerHeight = headerHeight + currentHeight;
const targetSpacerHeight = headerHeight + targetHeight;
animations.push(headerSpacer.animate([
{ height: `${currentSpacerHeight}px` },
{ height: `${targetSpacerHeight}px` }
], {
duration: 150,
easing: 'ease-out'
}));
}
// Update CSS variable after animation
Promise.all(animations.map(anim => anim.finished)).then(() => {
const root = document.documentElement;
root.style.setProperty('--all-day-row-height', `${targetHeight}px`);
eventBus.emit('header:height-changed');
});
}
/**
* Calculate all-day height based on number of rows
*/
calculateAllDayHeight(targetRows) {
const targetHeight = targetRows * ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT;
const currentHeight = AllDayDomReader.getCurrentHeight();
const heightDifference = targetHeight - currentHeight;
return { targetHeight, currentHeight, heightDifference };
}
/**
* Collapse all-day row (animate to 0 rows)
*/
collapseAllDayRow() {
this.animateToRows(0);
}
}
//# sourceMappingURL=AllDayHeightService.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"AllDayHeightService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayHeightService.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,9 @@
/**
* All-day feature barrel export
*
* Exports all public APIs from the all-day feature
*/
export { AllDayCoordinator } from './AllDayCoordinator';
export { AllDayHeightService } from './AllDayHeightService';
export { AllDayCollapseService } from './AllDayCollapseService';
export { AllDayDragService } from './AllDayDragService';

View file

@ -0,0 +1,10 @@
/**
* All-day feature barrel export
*
* Exports all public APIs from the all-day feature
*/
export { AllDayCoordinator } from './AllDayCoordinator';
export { AllDayHeightService } from './AllDayHeightService';
export { AllDayCollapseService } from './AllDayCollapseService';
export { AllDayDragService } from './AllDayDragService';
//# sourceMappingURL=index.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/features/all-day/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}

View file

@ -0,0 +1,74 @@
import { ICalendarEvent } from '../../../types/CalendarTypes';
/**
* AllDayDomReader - Centralized DOM reading utilities for all-day services
*
* STATELESS UTILITY - Pure functions for reading DOM state
* - Consistent selectors across all services
* - Unified computed style approach (not inline styles)
* - Type-safe return values
* - Single source of truth for DOM queries
*/
export declare class AllDayDomReader {
/**
* Get the all-day events container element
*/
static getAllDayContainer(): HTMLElement | null;
/**
* Get the calendar header element
*/
static getCalendarHeader(): HTMLElement | null;
/**
* Get the header spacer element
*/
static getHeaderSpacer(): HTMLElement | null;
/**
* Get all all-day event elements (excluding overflow indicators)
* Returns raw HTMLElements for DOM manipulation
*/
static getEventElements(): HTMLElement[];
/**
* Get all-day events as ICalendarEvent objects
* Returns parsed data for business logic
*/
static getEventsAsData(): ICalendarEvent[];
/**
* Get grid row from element using computed style
* Always uses computed style for consistency
*/
static getGridRow(element: HTMLElement): number;
/**
* Get grid column range from element using computed style
*/
static getGridColumnRange(element: HTMLElement): {
start: number;
end: number;
};
/**
* Get grid area from element using computed style
*/
static getGridArea(element: HTMLElement): string;
/**
* Calculate max row number from all events
* Uses computed styles for accurate reading
*/
static getMaxRowFromEvents(): number;
/**
* Check if all-day container is expanded
*/
static isExpanded(): boolean;
/**
* Get current all-day height from CSS variable
*/
static getCurrentHeight(): number;
/**
* Count events in specific column
*/
static countEventsInColumn(columnIndex: number): number;
/**
* Get current layouts from DOM elements
* Returns map of eventId layout info for comparison
*/
static getCurrentLayouts(): Map<string, {
gridArea: string;
}>;
}

View file

@ -0,0 +1,175 @@
/**
* AllDayDomReader - Centralized DOM reading utilities for all-day services
*
* STATELESS UTILITY - Pure functions for reading DOM state
* - Consistent selectors across all services
* - Unified computed style approach (not inline styles)
* - Type-safe return values
* - Single source of truth for DOM queries
*/
export class AllDayDomReader {
// ============================================
// CONTAINER GETTERS
// ============================================
/**
* Get the all-day events container element
*/
static getAllDayContainer() {
return document.querySelector('swp-calendar-header swp-allday-container');
}
/**
* Get the calendar header element
*/
static getCalendarHeader() {
return document.querySelector('swp-calendar-header');
}
/**
* Get the header spacer element
*/
static getHeaderSpacer() {
return document.querySelector('swp-header-spacer');
}
// ============================================
// EVENT ELEMENT GETTERS
// ============================================
/**
* Get all all-day event elements (excluding overflow indicators)
* Returns raw HTMLElements for DOM manipulation
*/
static getEventElements() {
const container = this.getAllDayContainer();
if (!container)
return [];
return Array.from(container.querySelectorAll('swp-allday-event:not(.max-event-indicator)'));
}
/**
* Get all-day events as ICalendarEvent objects
* Returns parsed data for business logic
*/
static getEventsAsData() {
const elements = this.getEventElements();
return elements
.map(element => {
const eventId = element.dataset.eventId;
const startStr = element.dataset.start;
const endStr = element.dataset.end;
// Validate required fields
if (!eventId || !startStr || !endStr) {
console.warn('AllDayDomReader: Invalid event data in DOM:', element);
return null;
}
const start = new Date(startStr);
const end = new Date(endStr);
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
console.warn('AllDayDomReader: Invalid event dates:', { startStr, endStr });
return null;
}
return {
id: eventId,
title: element.dataset.title || '',
start,
end,
type: element.dataset.type || 'task',
allDay: true,
syncStatus: (element.dataset.syncStatus || 'synced')
};
})
.filter((event) => event !== null);
}
// ============================================
// GRID POSITION READERS
// ============================================
/**
* Get grid row from element using computed style
* Always uses computed style for consistency
*/
static getGridRow(element) {
const computedStyle = window.getComputedStyle(element);
return parseInt(computedStyle.gridRowStart) || 0;
}
/**
* Get grid column range from element using computed style
*/
static getGridColumnRange(element) {
const computedStyle = window.getComputedStyle(element);
return {
start: parseInt(computedStyle.gridColumnStart) || 0,
end: parseInt(computedStyle.gridColumnEnd) || 0
};
}
/**
* Get grid area from element using computed style
*/
static getGridArea(element) {
const computedStyle = window.getComputedStyle(element);
return computedStyle.gridArea;
}
/**
* Calculate max row number from all events
* Uses computed styles for accurate reading
*/
static getMaxRowFromEvents() {
const events = this.getEventElements();
if (events.length === 0)
return 0;
let maxRow = 0;
events.forEach(event => {
const row = this.getGridRow(event);
maxRow = Math.max(maxRow, row);
});
return maxRow;
}
// ============================================
// STATE READERS
// ============================================
/**
* Check if all-day container is expanded
*/
static isExpanded() {
const container = this.getAllDayContainer();
return container?.classList.contains('expanded') || false;
}
/**
* Get current all-day height from CSS variable
*/
static getCurrentHeight() {
const root = document.documentElement;
const heightStr = root.style.getPropertyValue('--all-day-row-height') || '0px';
return parseInt(heightStr) || 0;
}
/**
* Count events in specific column
*/
static countEventsInColumn(columnIndex) {
const events = this.getEventElements();
let count = 0;
events.forEach((event) => {
const { start, end } = this.getGridColumnRange(event);
if (start <= columnIndex && end > columnIndex) {
count++;
}
});
return count;
}
// ============================================
// LAYOUT READERS
// ============================================
/**
* Get current layouts from DOM elements
* Returns map of eventId layout info for comparison
*/
static getCurrentLayouts() {
const layoutsMap = new Map();
const events = this.getEventElements();
events.forEach(event => {
const eventId = event.dataset.eventId;
if (eventId) {
layoutsMap.set(eventId, {
gridArea: this.getGridArea(event)
});
}
});
return layoutsMap;
}
}
//# sourceMappingURL=AllDayDomReader.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"AllDayDomReader.js","sourceRoot":"","sources":["../../../../../src/features/all-day/utils/AllDayDomReader.ts"],"names":[],"mappings":"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"}

1
wwwroot/js/index.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export {};

171
wwwroot/js/index.js Normal file
View file

@ -0,0 +1,171 @@
// Main entry point for Calendar Plantempus
import { Container } from '@novadi/core';
import { eventBus } from './core/EventBus';
import { ConfigManager } from './configurations/ConfigManager';
import { URLManager } from './utils/URLManager';
// Import all managers
import { EventManager } from './managers/EventManager';
import { EventRenderingService } from './renderers/EventRendererManager';
import { GridManager } from './managers/GridManager';
import { ScrollManager } from './managers/ScrollManager';
import { NavigationManager } from './managers/NavigationManager';
import { NavigationButtons } from './components/NavigationButtons';
import { ViewSelector } from './components/ViewSelector';
import { CalendarManager } from './managers/CalendarManager';
import { DragDropManager } from './managers/DragDropManager';
import { AllDayManager } from './managers/AllDayManager';
import { ResizeHandleManager } from './managers/ResizeHandleManager';
import { EdgeScrollManager } from './managers/EdgeScrollManager';
import { HeaderManager } from './managers/HeaderManager';
import { WorkweekPresets } from './components/WorkweekPresets';
import { IndexedDBEventRepository } from './repositories/IndexedDBEventRepository';
import { ApiEventRepository } from './repositories/ApiEventRepository';
import { IndexedDBService } from './storage/IndexedDBService';
import { OperationQueue } from './storage/OperationQueue';
// Import workers
import { SyncManager } from './workers/SyncManager';
// Import renderers
import { DateHeaderRenderer } from './renderers/DateHeaderRenderer';
import { DateColumnRenderer } from './renderers/ColumnRenderer';
import { DateEventRenderer } from './renderers/EventRenderer';
import { AllDayEventRenderer } from './renderers/AllDayEventRenderer';
import { GridRenderer } from './renderers/GridRenderer';
import { WeekInfoRenderer } from './renderers/WeekInfoRenderer';
// Import utilities and services
import { DateService } from './utils/DateService';
import { TimeFormatter } from './utils/TimeFormatter';
import { PositionUtils } from './utils/PositionUtils';
import { WorkHoursManager } from './managers/WorkHoursManager';
import { EventStackManager } from './managers/EventStackManager';
import { EventLayoutCoordinator } from './managers/EventLayoutCoordinator';
/**
* Handle deep linking functionality after managers are initialized
*/
async function handleDeepLinking(eventManager, urlManager) {
try {
const eventId = urlManager.parseEventIdFromURL();
if (eventId) {
console.log(`Deep linking to event ID: ${eventId}`);
// Wait a bit for managers to be fully ready
setTimeout(async () => {
const success = await eventManager.navigateToEvent(eventId);
if (!success) {
console.warn(`Deep linking failed: Event with ID ${eventId} not found`);
}
}, 500);
}
}
catch (error) {
console.warn('Deep linking failed:', error);
}
}
/**
* Initialize the calendar application using NovaDI
*/
async function initializeCalendar() {
try {
// Load configuration from JSON
const config = await ConfigManager.load();
// Create NovaDI container
const container = new Container();
const builder = container.builder();
// Enable debug mode for development
eventBus.setDebug(true);
// Bind core services as instances
builder.registerInstance(eventBus).as();
// Register configuration instance
builder.registerInstance(config).as();
// Register storage and repository services
builder.registerType(IndexedDBService).as();
builder.registerType(OperationQueue).as();
builder.registerType(ApiEventRepository).as();
builder.registerType(IndexedDBEventRepository).as();
// Register workers
builder.registerType(SyncManager).as();
// Register renderers
builder.registerType(DateHeaderRenderer).as();
builder.registerType(DateColumnRenderer).as();
builder.registerType(DateEventRenderer).as();
// Register core services and utilities
builder.registerType(DateService).as();
builder.registerType(EventStackManager).as();
builder.registerType(EventLayoutCoordinator).as();
builder.registerType(WorkHoursManager).as();
builder.registerType(URLManager).as();
builder.registerType(TimeFormatter).as();
builder.registerType(PositionUtils).as();
// Note: AllDayLayoutEngine is instantiated per-operation with specific dates, not a singleton
builder.registerType(WeekInfoRenderer).as();
builder.registerType(AllDayEventRenderer).as();
builder.registerType(EventRenderingService).as();
builder.registerType(GridRenderer).as();
builder.registerType(GridManager).as();
builder.registerType(ScrollManager).as();
builder.registerType(NavigationManager).as();
builder.registerType(NavigationButtons).as();
builder.registerType(ViewSelector).as();
builder.registerType(DragDropManager).as();
builder.registerType(AllDayManager).as();
builder.registerType(ResizeHandleManager).as();
builder.registerType(EdgeScrollManager).as();
builder.registerType(HeaderManager).as();
builder.registerType(CalendarManager).as();
builder.registerType(WorkweekPresets).as();
builder.registerType(ConfigManager).as();
builder.registerType(EventManager).as();
// Build the container
const app = builder.build();
// Get managers from container
const eb = app.resolveType();
const calendarManager = app.resolveType();
const eventManager = app.resolveType();
const resizeHandleManager = app.resolveType();
const headerManager = app.resolveType();
const dragDropManager = app.resolveType();
const viewSelectorManager = app.resolveType();
const navigationManager = app.resolveType();
const navigationButtonsManager = app.resolveType();
const edgeScrollManager = app.resolveType();
const allDayManager = app.resolveType();
const urlManager = app.resolveType();
const workweekPresetsManager = app.resolveType();
const configManager = app.resolveType();
// Initialize managers
await calendarManager.initialize?.();
await resizeHandleManager.initialize?.();
// Resolve SyncManager (starts automatically in constructor)
// Resolve SyncManager (starts automatically in constructor)
// Resolve SyncManager (starts automatically in constructor)
// Resolve SyncManager (starts automatically in constructor)
// Resolve SyncManager (starts automatically in constructor)
//const syncManager = app.resolveType<SyncManager>();
// Handle deep linking after managers are initialized
await handleDeepLinking(eventManager, urlManager);
// Expose to window for debugging (with proper typing)
window.calendarDebug = {
eventBus,
app,
calendarManager,
eventManager,
workweekPresetsManager,
//syncManager,
};
}
catch (error) {
throw error;
}
}
// Initialize when DOM is ready - now handles async properly
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initializeCalendar().catch(error => {
console.error('Calendar initialization failed:', error);
});
});
}
else {
initializeCalendar().catch(error => {
console.error('Calendar initialization failed:', error);
});
}
//# sourceMappingURL=index.js.map

Some files were not shown because too many files have changed in this diff Show more