Refactor column key handling and event positioning
Introduces more robust column key management across renderers and drag/resize operations Decouples column key parsing from date extraction Simplifies event positioning logic Improves multi-resource view compatibility
This commit is contained in:
parent
0eb3bacb41
commit
c2f7564f8e
7 changed files with 81 additions and 71 deletions
|
|
@ -16,6 +16,7 @@ import {
|
|||
*/
|
||||
interface IHeaderItemLayout {
|
||||
event: ICalendarEvent;
|
||||
columnKey: string; // Opaque column identifier
|
||||
row: number; // 1-indexed
|
||||
colStart: number; // 1-indexed
|
||||
colEnd: number; // exclusive
|
||||
|
|
@ -56,6 +57,10 @@ export class HeaderDrawerRenderer {
|
|||
const visibleDates = filter['date'] || [];
|
||||
if (visibleDates.length === 0) return;
|
||||
|
||||
// Get column keys from DOM for correct multi-resource positioning
|
||||
const visibleColumnKeys = this.getVisibleColumnKeysFromDOM();
|
||||
if (visibleColumnKeys.length === 0) return;
|
||||
|
||||
// Fetch events for date range
|
||||
const startDate = new Date(visibleDates[0]);
|
||||
const endDate = new Date(visibleDates[visibleDates.length - 1]);
|
||||
|
|
@ -71,8 +76,8 @@ export class HeaderDrawerRenderer {
|
|||
|
||||
if (allDayEvents.length === 0) return;
|
||||
|
||||
// Calculate layout with row stacking
|
||||
const layouts = this.calculateLayout(allDayEvents, visibleDates);
|
||||
// Calculate layout with row stacking using columnKeys
|
||||
const layouts = this.calculateLayout(allDayEvents, visibleColumnKeys);
|
||||
const rowCount = Math.max(1, ...layouts.map(l => l.row));
|
||||
|
||||
// Render each item with layout
|
||||
|
|
@ -89,13 +94,14 @@ export class HeaderDrawerRenderer {
|
|||
* Create a header item element from layout
|
||||
*/
|
||||
private createHeaderItem(layout: IHeaderItemLayout): HTMLElement {
|
||||
const { event, row, colStart, colEnd } = layout;
|
||||
const { event, columnKey, row, colStart, colEnd } = layout;
|
||||
|
||||
const item = document.createElement('swp-header-item');
|
||||
item.dataset.eventId = event.id;
|
||||
item.dataset.itemType = 'event';
|
||||
item.dataset.start = event.start.toISOString();
|
||||
item.dataset.end = event.end.toISOString();
|
||||
item.dataset.columnKey = columnKey;
|
||||
item.textContent = event.title;
|
||||
|
||||
// Color class
|
||||
|
|
@ -112,19 +118,22 @@ export class HeaderDrawerRenderer {
|
|||
* Calculate layout for all events with row stacking
|
||||
* Uses track-based algorithm to find available rows for overlapping events
|
||||
*/
|
||||
private calculateLayout(events: ICalendarEvent[], visibleDates: string[]): IHeaderItemLayout[] {
|
||||
private calculateLayout(events: ICalendarEvent[], visibleColumnKeys: string[]): IHeaderItemLayout[] {
|
||||
// tracks[row][col] = occupied
|
||||
const tracks: boolean[][] = [new Array(visibleDates.length).fill(false)];
|
||||
const tracks: boolean[][] = [new Array(visibleColumnKeys.length).fill(false)];
|
||||
const layouts: IHeaderItemLayout[] = [];
|
||||
|
||||
for (const event of events) {
|
||||
const startCol = this.getColIndex(event.start, visibleDates);
|
||||
const endCol = this.getColIndex(event.end, visibleDates);
|
||||
// Build columnKey from event fields (only place we need to construct it)
|
||||
const columnKey = this.buildColumnKeyFromEvent(event);
|
||||
const startCol = visibleColumnKeys.indexOf(columnKey);
|
||||
const endColumnKey = this.buildColumnKeyFromEvent(event, event.end);
|
||||
const endCol = visibleColumnKeys.indexOf(endColumnKey);
|
||||
if (startCol === -1 && endCol === -1) continue;
|
||||
|
||||
// Clamp til synlige kolonner
|
||||
const colStart = Math.max(0, startCol);
|
||||
const colEnd = (endCol !== -1 ? endCol : visibleDates.length - 1) + 1;
|
||||
const colEnd = (endCol !== -1 ? endCol : visibleColumnKeys.length - 1) + 1;
|
||||
|
||||
// Find ledig række
|
||||
const row = this.findAvailableRow(tracks, colStart, colEnd);
|
||||
|
|
@ -134,12 +143,23 @@ export class HeaderDrawerRenderer {
|
|||
tracks[row][c] = true;
|
||||
}
|
||||
|
||||
layouts.push({ event, row: row + 1, colStart: colStart + 1, colEnd: colEnd + 1 });
|
||||
layouts.push({ event, columnKey, row: row + 1, colStart: colStart + 1, colEnd: colEnd + 1 });
|
||||
}
|
||||
|
||||
return layouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build columnKey from event fields
|
||||
* This is the only place we construct columnKey from event data
|
||||
*/
|
||||
private buildColumnKeyFromEvent(event: ICalendarEvent, date?: Date): string {
|
||||
const dateStr = this.dateService.getDateKey(date || event.start);
|
||||
const segments: Record<string, string> = { date: dateStr };
|
||||
if (event.resourceId) segments.resource = event.resourceId;
|
||||
return this.dateService.buildColumnKey(segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find available row for event spanning columns [colStart, colEnd)
|
||||
*/
|
||||
|
|
@ -156,14 +176,6 @@ export class HeaderDrawerRenderer {
|
|||
return tracks.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column index for a date (0-indexed, -1 if not found)
|
||||
*/
|
||||
private getColIndex(date: Date, visibleDates: string[]): number {
|
||||
const dateKey = this.dateService.getDateKey(date);
|
||||
return visibleDates.indexOf(dateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color class based on event metadata or type
|
||||
*/
|
||||
|
|
@ -233,15 +245,9 @@ export class HeaderDrawerRenderer {
|
|||
item.dataset.eventId = payload.eventId;
|
||||
item.dataset.itemType = payload.itemType;
|
||||
item.dataset.duration = String(payload.duration);
|
||||
item.dataset.columnKey = payload.sourceColumnKey;
|
||||
item.textContent = payload.title;
|
||||
|
||||
// Set start/end as ISO dates (for recalculateDrawerLayout)
|
||||
const startDate = new Date(payload.sourceDate);
|
||||
const endDate = new Date(payload.sourceDate);
|
||||
endDate.setDate(endDate.getDate() + payload.duration - 1);
|
||||
item.dataset.start = startDate.toISOString();
|
||||
item.dataset.end = endDate.toISOString();
|
||||
|
||||
// Apply color class if present
|
||||
if (payload.colorClass) {
|
||||
item.classList.add(payload.colorClass);
|
||||
|
|
@ -276,12 +282,8 @@ export class HeaderDrawerRenderer {
|
|||
|
||||
this.currentItem.style.gridArea = `1 / ${col} / 2 / ${endCol}`;
|
||||
|
||||
// Update start/end dates based on new position
|
||||
const startDate = new Date(payload.dateKey);
|
||||
const endDate = new Date(payload.dateKey);
|
||||
endDate.setDate(endDate.getDate() + duration - 1);
|
||||
this.currentItem.dataset.start = startDate.toISOString();
|
||||
this.currentItem.dataset.end = endDate.toISOString();
|
||||
// Update columnKey to new position
|
||||
this.currentItem.dataset.columnKey = payload.columnKey;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -327,26 +329,27 @@ export class HeaderDrawerRenderer {
|
|||
const items = Array.from(drawer.querySelectorAll('swp-header-item')) as HTMLElement[];
|
||||
if (items.length === 0) return;
|
||||
|
||||
// Get visible dates from existing items
|
||||
const visibleDates = this.getVisibleDatesFromDOM();
|
||||
if (visibleDates.length === 0) return;
|
||||
// Get visible column keys for correct multi-resource positioning
|
||||
const visibleColumnKeys = this.getVisibleColumnKeysFromDOM();
|
||||
if (visibleColumnKeys.length === 0) return;
|
||||
|
||||
// Build layout data from DOM items
|
||||
// Build layout data from DOM items - use columnKey directly (opaque matching)
|
||||
const itemData = items.map(item => ({
|
||||
element: item,
|
||||
start: new Date(item.dataset.start || ''),
|
||||
end: new Date(item.dataset.end || '')
|
||||
columnKey: item.dataset.columnKey || '',
|
||||
duration: parseInt(item.dataset.duration || '1', 10)
|
||||
}));
|
||||
|
||||
// Calculate new layout using track algorithm
|
||||
const tracks: boolean[][] = [new Array(visibleDates.length).fill(false)];
|
||||
const tracks: boolean[][] = [new Array(visibleColumnKeys.length).fill(false)];
|
||||
|
||||
for (const item of itemData) {
|
||||
const startCol = this.getColIndex(item.start, visibleDates);
|
||||
const endCol = this.getColIndex(item.end, visibleDates);
|
||||
// Direct columnKey matching - no parsing or construction needed
|
||||
const startCol = visibleColumnKeys.indexOf(item.columnKey);
|
||||
if (startCol === -1) continue;
|
||||
|
||||
const colStart = Math.max(0, startCol);
|
||||
const colEnd = (endCol !== -1 ? endCol : visibleDates.length - 1) + 1;
|
||||
const colStart = startCol;
|
||||
const colEnd = Math.min(startCol + item.duration, visibleColumnKeys.length);
|
||||
|
||||
const row = this.findAvailableRow(tracks, colStart, colEnd);
|
||||
|
||||
|
|
@ -364,16 +367,16 @@ export class HeaderDrawerRenderer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get visible dates from header columns in DOM
|
||||
* Get visible column keys from DOM (preserves order for multi-resource views)
|
||||
*/
|
||||
private getVisibleDatesFromDOM(): string[] {
|
||||
private getVisibleColumnKeysFromDOM(): string[] {
|
||||
const columns = document.querySelectorAll('swp-day-column');
|
||||
const dates: string[] = [];
|
||||
const columnKeys: string[] = [];
|
||||
columns.forEach(col => {
|
||||
const date = (col as HTMLElement).dataset.date;
|
||||
if (date && !dates.includes(date)) dates.push(date);
|
||||
const columnKey = (col as HTMLElement).dataset.columnKey;
|
||||
if (columnKey) columnKeys.push(columnKey);
|
||||
});
|
||||
return dates;
|
||||
return columnKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue