Some ignored filles was missing
This commit is contained in:
parent
7db22245e2
commit
fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,7 +1,6 @@
|
||||||
# Build outputs
|
# Build outputs
|
||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
wwwroot/js/
|
|
||||||
|
|
||||||
# Node modules
|
# Node modules
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
@ -30,6 +29,5 @@ Thumbs.db
|
||||||
*.suo
|
*.suo
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
js/
|
|
||||||
|
packages/calendar/dist/
|
||||||
packages/calendar/dist/
|
|
||||||
|
|
|
||||||
26
wwwroot/js/calendar-min.js
vendored
Normal file
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
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
1664
wwwroot/js/calendar.js
Normal file
File diff suppressed because one or more lines are too long
63
wwwroot/js/components/NavigationButtons.d.ts
vendored
Normal file
63
wwwroot/js/components/NavigationButtons.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
131
wwwroot/js/components/NavigationButtons.js
Normal file
131
wwwroot/js/components/NavigationButtons.js
Normal 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
|
||||||
1
wwwroot/js/components/NavigationButtons.js.map
Normal file
1
wwwroot/js/components/NavigationButtons.js.map
Normal 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
70
wwwroot/js/components/ViewSelector.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
130
wwwroot/js/components/ViewSelector.js
Normal file
130
wwwroot/js/components/ViewSelector.js
Normal 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
|
||||||
1
wwwroot/js/components/ViewSelector.js.map
Normal file
1
wwwroot/js/components/ViewSelector.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"ViewSelector.js","sourceRoot":"","sources":["../../../src/components/ViewSelector.ts"],"names":[],"mappings":"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"}
|
||||||
47
wwwroot/js/components/WorkweekPresets.d.ts
vendored
Normal file
47
wwwroot/js/components/WorkweekPresets.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
95
wwwroot/js/components/WorkweekPresets.js
Normal file
95
wwwroot/js/components/WorkweekPresets.js
Normal 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
|
||||||
1
wwwroot/js/components/WorkweekPresets.js.map
Normal file
1
wwwroot/js/components/WorkweekPresets.js.map
Normal 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"}
|
||||||
44
wwwroot/js/configuration/CalendarConfig.d.ts
vendored
Normal file
44
wwwroot/js/configuration/CalendarConfig.d.ts
vendored
Normal 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 };
|
||||||
90
wwwroot/js/configuration/CalendarConfig.js
Normal file
90
wwwroot/js/configuration/CalendarConfig.js
Normal 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
|
||||||
1
wwwroot/js/configuration/CalendarConfig.js.map
Normal file
1
wwwroot/js/configuration/CalendarConfig.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/CalendarConfig.ts"],"names":[],"mappings":"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"}
|
||||||
11
wwwroot/js/configuration/ConfigManager.d.ts
vendored
Normal file
11
wwwroot/js/configuration/ConfigManager.d.ts
vendored
Normal 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>;
|
||||||
|
}
|
||||||
43
wwwroot/js/configuration/ConfigManager.js
Normal file
43
wwwroot/js/configuration/ConfigManager.js
Normal 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
|
||||||
1
wwwroot/js/configuration/ConfigManager.js.map
Normal file
1
wwwroot/js/configuration/ConfigManager.js.map
Normal 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"}
|
||||||
10
wwwroot/js/configuration/DateViewSettings.d.ts
vendored
Normal file
10
wwwroot/js/configuration/DateViewSettings.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
2
wwwroot/js/configuration/DateViewSettings.js
Normal file
2
wwwroot/js/configuration/DateViewSettings.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=DateViewSettings.js.map
|
||||||
1
wwwroot/js/configuration/DateViewSettings.js.map
Normal file
1
wwwroot/js/configuration/DateViewSettings.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configuration/DateViewSettings.ts"],"names":[],"mappings":""}
|
||||||
22
wwwroot/js/configuration/GridSettings.d.ts
vendored
Normal file
22
wwwroot/js/configuration/GridSettings.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
11
wwwroot/js/configuration/GridSettings.js
Normal file
11
wwwroot/js/configuration/GridSettings.js
Normal 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
|
||||||
1
wwwroot/js/configuration/GridSettings.js.map
Normal file
1
wwwroot/js/configuration/GridSettings.js.map
Normal 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"}
|
||||||
21
wwwroot/js/configuration/ICalendarConfig.d.ts
vendored
Normal file
21
wwwroot/js/configuration/ICalendarConfig.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
2
wwwroot/js/configuration/ICalendarConfig.js
Normal file
2
wwwroot/js/configuration/ICalendarConfig.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=ICalendarConfig.js.map
|
||||||
1
wwwroot/js/configuration/ICalendarConfig.js.map
Normal file
1
wwwroot/js/configuration/ICalendarConfig.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/ICalendarConfig.ts"],"names":[],"mappings":""}
|
||||||
10
wwwroot/js/configuration/TimeFormatConfig.d.ts
vendored
Normal file
10
wwwroot/js/configuration/TimeFormatConfig.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Time format configuration settings
|
||||||
|
*/
|
||||||
|
export interface ITimeFormatConfig {
|
||||||
|
timezone: string;
|
||||||
|
use24HourFormat: boolean;
|
||||||
|
locale: string;
|
||||||
|
dateFormat: 'locale' | 'technical';
|
||||||
|
showSeconds: boolean;
|
||||||
|
}
|
||||||
2
wwwroot/js/configuration/TimeFormatConfig.js
Normal file
2
wwwroot/js/configuration/TimeFormatConfig.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=TimeFormatConfig.js.map
|
||||||
1
wwwroot/js/configuration/TimeFormatConfig.js.map
Normal file
1
wwwroot/js/configuration/TimeFormatConfig.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configuration/TimeFormatConfig.ts"],"names":[],"mappings":""}
|
||||||
9
wwwroot/js/configuration/WorkWeekSettings.d.ts
vendored
Normal file
9
wwwroot/js/configuration/WorkWeekSettings.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Work week configuration settings
|
||||||
|
*/
|
||||||
|
export interface IWorkWeekSettings {
|
||||||
|
id: string;
|
||||||
|
workDays: number[];
|
||||||
|
totalDays: number;
|
||||||
|
firstWorkDay: number;
|
||||||
|
}
|
||||||
2
wwwroot/js/configuration/WorkWeekSettings.js
Normal file
2
wwwroot/js/configuration/WorkWeekSettings.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=WorkWeekSettings.js.map
|
||||||
1
wwwroot/js/configuration/WorkWeekSettings.js.map
Normal file
1
wwwroot/js/configuration/WorkWeekSettings.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"WorkWeekSettings.js","sourceRoot":"","sources":["../../../src/configuration/WorkWeekSettings.ts"],"names":[],"mappings":""}
|
||||||
46
wwwroot/js/configurations/CalendarConfig.d.ts
vendored
Normal file
46
wwwroot/js/configurations/CalendarConfig.d.ts
vendored
Normal 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 };
|
||||||
85
wwwroot/js/configurations/CalendarConfig.js
Normal file
85
wwwroot/js/configurations/CalendarConfig.js
Normal 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
|
||||||
1
wwwroot/js/configurations/CalendarConfig.js.map
Normal file
1
wwwroot/js/configurations/CalendarConfig.js.map
Normal 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"}
|
||||||
28
wwwroot/js/configurations/ConfigManager.d.ts
vendored
Normal file
28
wwwroot/js/configurations/ConfigManager.d.ts
vendored
Normal 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>;
|
||||||
|
}
|
||||||
80
wwwroot/js/configurations/ConfigManager.js
Normal file
80
wwwroot/js/configurations/ConfigManager.js
Normal 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
|
||||||
1
wwwroot/js/configurations/ConfigManager.js.map
Normal file
1
wwwroot/js/configurations/ConfigManager.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../../src/configurations/ConfigManager.ts"],"names":[],"mappings":"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"}
|
||||||
10
wwwroot/js/configurations/DateViewSettings.d.ts
vendored
Normal file
10
wwwroot/js/configurations/DateViewSettings.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
2
wwwroot/js/configurations/DateViewSettings.js
Normal file
2
wwwroot/js/configurations/DateViewSettings.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=DateViewSettings.js.map
|
||||||
1
wwwroot/js/configurations/DateViewSettings.js.map
Normal file
1
wwwroot/js/configurations/DateViewSettings.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configurations/DateViewSettings.ts"],"names":[],"mappings":""}
|
||||||
22
wwwroot/js/configurations/GridSettings.d.ts
vendored
Normal file
22
wwwroot/js/configurations/GridSettings.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
11
wwwroot/js/configurations/GridSettings.js
Normal file
11
wwwroot/js/configurations/GridSettings.js
Normal 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
|
||||||
1
wwwroot/js/configurations/GridSettings.js.map
Normal file
1
wwwroot/js/configurations/GridSettings.js.map
Normal 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"}
|
||||||
21
wwwroot/js/configurations/ICalendarConfig.d.ts
vendored
Normal file
21
wwwroot/js/configurations/ICalendarConfig.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
2
wwwroot/js/configurations/ICalendarConfig.js
Normal file
2
wwwroot/js/configurations/ICalendarConfig.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=ICalendarConfig.js.map
|
||||||
1
wwwroot/js/configurations/ICalendarConfig.js.map
Normal file
1
wwwroot/js/configurations/ICalendarConfig.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configurations/ICalendarConfig.ts"],"names":[],"mappings":""}
|
||||||
10
wwwroot/js/configurations/TimeFormatConfig.d.ts
vendored
Normal file
10
wwwroot/js/configurations/TimeFormatConfig.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Time format configuration settings
|
||||||
|
*/
|
||||||
|
export interface ITimeFormatConfig {
|
||||||
|
timezone: string;
|
||||||
|
use24HourFormat: boolean;
|
||||||
|
locale: string;
|
||||||
|
dateFormat: 'locale' | 'technical';
|
||||||
|
showSeconds: boolean;
|
||||||
|
}
|
||||||
2
wwwroot/js/configurations/TimeFormatConfig.js
Normal file
2
wwwroot/js/configurations/TimeFormatConfig.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=TimeFormatConfig.js.map
|
||||||
1
wwwroot/js/configurations/TimeFormatConfig.js.map
Normal file
1
wwwroot/js/configurations/TimeFormatConfig.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configurations/TimeFormatConfig.ts"],"names":[],"mappings":""}
|
||||||
9
wwwroot/js/configurations/WorkWeekSettings.d.ts
vendored
Normal file
9
wwwroot/js/configurations/WorkWeekSettings.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Work week configuration settings
|
||||||
|
*/
|
||||||
|
export interface IWorkWeekSettings {
|
||||||
|
id: string;
|
||||||
|
workDays: number[];
|
||||||
|
totalDays: number;
|
||||||
|
firstWorkDay: number;
|
||||||
|
}
|
||||||
2
wwwroot/js/configurations/WorkWeekSettings.js
Normal file
2
wwwroot/js/configurations/WorkWeekSettings.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=WorkWeekSettings.js.map
|
||||||
1
wwwroot/js/configurations/WorkWeekSettings.js.map
Normal file
1
wwwroot/js/configurations/WorkWeekSettings.js.map
Normal 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
37
wwwroot/js/constants/CoreEvents.d.ts
vendored
Normal 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";
|
||||||
|
};
|
||||||
48
wwwroot/js/constants/CoreEvents.js
Normal file
48
wwwroot/js/constants/CoreEvents.js
Normal 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
|
||||||
1
wwwroot/js/constants/CoreEvents.js.map
Normal file
1
wwwroot/js/constants/CoreEvents.js.map
Normal 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
225
wwwroot/js/core/CalendarConfig.d.ts
vendored
Normal 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 {};
|
||||||
421
wwwroot/js/core/CalendarConfig.js
Normal file
421
wwwroot/js/core/CalendarConfig.js
Normal 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
|
||||||
1
wwwroot/js/core/CalendarConfig.js.map
Normal file
1
wwwroot/js/core/CalendarConfig.js.map
Normal file
File diff suppressed because one or more lines are too long
60
wwwroot/js/core/EventBus.d.ts
vendored
Normal file
60
wwwroot/js/core/EventBus.d.ts
vendored
Normal 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
158
wwwroot/js/core/EventBus.js
Normal 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
|
||||||
1
wwwroot/js/core/EventBus.js.map
Normal file
1
wwwroot/js/core/EventBus.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"EventBus.js","sourceRoot":"","sources":["../../../src/core/EventBus.ts"],"names":[],"mappings":"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"}
|
||||||
55
wwwroot/js/datasources/DateColumnDataSource.d.ts
vendored
Normal file
55
wwwroot/js/datasources/DateColumnDataSource.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
94
wwwroot/js/datasources/DateColumnDataSource.js
Normal file
94
wwwroot/js/datasources/DateColumnDataSource.js
Normal 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
|
||||||
1
wwwroot/js/datasources/DateColumnDataSource.js.map
Normal file
1
wwwroot/js/datasources/DateColumnDataSource.js.map
Normal 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
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
104
wwwroot/js/edge-scroll.js
Normal 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();
|
||||||
|
})();
|
||||||
98
wwwroot/js/elements/SwpEventElement.d.ts
vendored
Normal file
98
wwwroot/js/elements/SwpEventElement.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
303
wwwroot/js/elements/SwpEventElement.js
Normal file
303
wwwroot/js/elements/SwpEventElement.js
Normal 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
|
||||||
1
wwwroot/js/elements/SwpEventElement.js.map
Normal file
1
wwwroot/js/elements/SwpEventElement.js.map
Normal file
File diff suppressed because one or more lines are too long
55
wwwroot/js/factories/CalendarTypeFactory.d.ts
vendored
Normal file
55
wwwroot/js/factories/CalendarTypeFactory.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
84
wwwroot/js/factories/CalendarTypeFactory.js
Normal file
84
wwwroot/js/factories/CalendarTypeFactory.js
Normal 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
|
||||||
1
wwwroot/js/factories/CalendarTypeFactory.js.map
Normal file
1
wwwroot/js/factories/CalendarTypeFactory.js.map
Normal 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"}
|
||||||
18
wwwroot/js/factories/ManagerFactory.d.ts
vendored
Normal file
18
wwwroot/js/factories/ManagerFactory.d.ts
vendored
Normal 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>;
|
||||||
|
}
|
||||||
60
wwwroot/js/factories/ManagerFactory.js
Normal file
60
wwwroot/js/factories/ManagerFactory.js
Normal 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
|
||||||
1
wwwroot/js/factories/ManagerFactory.js.map
Normal file
1
wwwroot/js/factories/ManagerFactory.js.map
Normal 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"}
|
||||||
45
wwwroot/js/features/all-day/AllDayCollapseService.d.ts
vendored
Normal file
45
wwwroot/js/features/all-day/AllDayCollapseService.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
168
wwwroot/js/features/all-day/AllDayCollapseService.js
Normal file
168
wwwroot/js/features/all-day/AllDayCollapseService.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/AllDayCollapseService.js.map
Normal file
1
wwwroot/js/features/all-day/AllDayCollapseService.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"AllDayCollapseService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayCollapseService.ts"],"names":[],"mappings":"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"}
|
||||||
45
wwwroot/js/features/all-day/AllDayCoordinator.d.ts
vendored
Normal file
45
wwwroot/js/features/all-day/AllDayCoordinator.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
168
wwwroot/js/features/all-day/AllDayCoordinator.js
Normal file
168
wwwroot/js/features/all-day/AllDayCoordinator.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/AllDayCoordinator.js.map
Normal file
1
wwwroot/js/features/all-day/AllDayCoordinator.js.map
Normal file
File diff suppressed because one or more lines are too long
74
wwwroot/js/features/all-day/AllDayDomReader.d.ts
vendored
Normal file
74
wwwroot/js/features/all-day/AllDayDomReader.d.ts
vendored
Normal 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;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
175
wwwroot/js/features/all-day/AllDayDomReader.js
Normal file
175
wwwroot/js/features/all-day/AllDayDomReader.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/AllDayDomReader.js.map
Normal file
1
wwwroot/js/features/all-day/AllDayDomReader.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"AllDayDomReader.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayDomReader.ts"],"names":[],"mappings":"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"}
|
||||||
50
wwwroot/js/features/all-day/AllDayDragService.d.ts
vendored
Normal file
50
wwwroot/js/features/all-day/AllDayDragService.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
183
wwwroot/js/features/all-day/AllDayDragService.js
Normal file
183
wwwroot/js/features/all-day/AllDayDragService.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/AllDayDragService.js.map
Normal file
1
wwwroot/js/features/all-day/AllDayDragService.js.map
Normal file
File diff suppressed because one or more lines are too long
26
wwwroot/js/features/all-day/AllDayHeightService.d.ts
vendored
Normal file
26
wwwroot/js/features/all-day/AllDayHeightService.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
85
wwwroot/js/features/all-day/AllDayHeightService.js
Normal file
85
wwwroot/js/features/all-day/AllDayHeightService.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/AllDayHeightService.js.map
Normal file
1
wwwroot/js/features/all-day/AllDayHeightService.js.map
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"AllDayHeightService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayHeightService.ts"],"names":[],"mappings":"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"}
|
||||||
9
wwwroot/js/features/all-day/index.d.ts
vendored
Normal file
9
wwwroot/js/features/all-day/index.d.ts
vendored
Normal 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';
|
||||||
10
wwwroot/js/features/all-day/index.js
Normal file
10
wwwroot/js/features/all-day/index.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/index.js.map
Normal file
1
wwwroot/js/features/all-day/index.js.map
Normal 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"}
|
||||||
74
wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts
vendored
Normal file
74
wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts
vendored
Normal 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;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
175
wwwroot/js/features/all-day/utils/AllDayDomReader.js
Normal file
175
wwwroot/js/features/all-day/utils/AllDayDomReader.js
Normal 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
|
||||||
1
wwwroot/js/features/all-day/utils/AllDayDomReader.js.map
Normal file
1
wwwroot/js/features/all-day/utils/AllDayDomReader.js.map
Normal 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
1
wwwroot/js/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export {};
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue