Some ignored filles was missing
This commit is contained in:
parent
7db22245e2
commit
fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions
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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"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue