wip
This commit is contained in:
parent
727a6ec53a
commit
72019a3d9a
15 changed files with 1056 additions and 1230 deletions
|
|
@ -25,67 +25,59 @@ export interface StackLink {
|
|||
}
|
||||
|
||||
export class SimpleEventOverlapManager {
|
||||
private static readonly STACKING_TIME_THRESHOLD_MINUTES = 30;
|
||||
private static readonly STACKING_WIDTH_REDUCTION_PX = 15;
|
||||
|
||||
/**
|
||||
* Detect overlap type between two events - simplified logic
|
||||
* Detect overlap type between two DOM elements - pixel-based logic
|
||||
*/
|
||||
public detectOverlap(event1: CalendarEvent, event2: CalendarEvent): OverlapType {
|
||||
if (!this.eventsOverlapInTime(event1, event2)) {
|
||||
public resolveOverlapType(element1: HTMLElement, element2: HTMLElement): OverlapType {
|
||||
const top1 = parseInt(element1.style.top) || 0;
|
||||
const height1 = parseInt(element1.style.height) || 0;
|
||||
const bottom1 = top1 + height1;
|
||||
|
||||
const top2 = parseInt(element2.style.top) || 0;
|
||||
const height2 = parseInt(element2.style.height) || 0;
|
||||
const bottom2 = top2 + height2;
|
||||
|
||||
// Check if events overlap in pixel space
|
||||
const tolerance = 2;
|
||||
if (bottom1 <= (top2 + tolerance) || bottom2 <= (top1 + tolerance)) {
|
||||
return OverlapType.NONE;
|
||||
}
|
||||
|
||||
const timeDiffMinutes = Math.abs(
|
||||
new Date(event1.start).getTime() - new Date(event2.start).getTime()
|
||||
) / (1000 * 60);
|
||||
// Events overlap - check start position difference for overlap type
|
||||
const startDifference = Math.abs(top1 - top2);
|
||||
|
||||
return timeDiffMinutes > SimpleEventOverlapManager.STACKING_TIME_THRESHOLD_MINUTES
|
||||
? OverlapType.STACKING
|
||||
: OverlapType.COLUMN_SHARING;
|
||||
// Over 40px start difference = stacking
|
||||
if (startDifference > 40) {
|
||||
return OverlapType.STACKING;
|
||||
}
|
||||
|
||||
// Within 40px start difference = column sharing
|
||||
return OverlapType.COLUMN_SHARING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple time overlap check
|
||||
*/
|
||||
private eventsOverlapInTime(event1: CalendarEvent, event2: CalendarEvent): boolean {
|
||||
const start1 = new Date(event1.start).getTime();
|
||||
const end1 = new Date(event1.end).getTime();
|
||||
const start2 = new Date(event2.start).getTime();
|
||||
const end2 = new Date(event2.end).getTime();
|
||||
|
||||
return !(end1 <= start2 || end2 <= start1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group overlapping events - much cleaner algorithm
|
||||
* Group overlapping elements - pixel-based algorithm
|
||||
*/
|
||||
public groupOverlappingEvents(events: CalendarEvent[]): OverlapGroup[] {
|
||||
const groups: OverlapGroup[] = [];
|
||||
const processed = new Set<string>();
|
||||
public groupOverlappingElements(elements: HTMLElement[]): HTMLElement[][] {
|
||||
const groups: HTMLElement[][] = [];
|
||||
const processed = new Set<HTMLElement>();
|
||||
|
||||
for (const event of events) {
|
||||
if (processed.has(event.id)) continue;
|
||||
for (const element of elements) {
|
||||
if (processed.has(element)) continue;
|
||||
|
||||
// Find all events that overlap with this one
|
||||
const overlapping = events.filter(other => {
|
||||
if (processed.has(other.id)) return false;
|
||||
return other.id === event.id || this.detectOverlap(event, other) !== OverlapType.NONE;
|
||||
// Find all elements that overlap with this one
|
||||
const overlapping = elements.filter(other => {
|
||||
if (processed.has(other)) return false;
|
||||
return other === element || this.resolveOverlapType(element, other) !== OverlapType.NONE;
|
||||
});
|
||||
|
||||
// Mark all as processed
|
||||
overlapping.forEach(e => processed.add(e.id));
|
||||
overlapping.forEach(e => processed.add(e));
|
||||
|
||||
// Determine group type
|
||||
const overlapType = overlapping.length > 1
|
||||
? this.detectOverlap(overlapping[0], overlapping[1])
|
||||
: OverlapType.NONE;
|
||||
|
||||
groups.push({
|
||||
type: overlapType,
|
||||
events: overlapping,
|
||||
position: this.calculateGroupPosition(overlapping)
|
||||
});
|
||||
groups.push(overlapping);
|
||||
}
|
||||
|
||||
return groups;
|
||||
|
|
@ -96,14 +88,6 @@ export class SimpleEventOverlapManager {
|
|||
*/
|
||||
public createEventGroup(events: CalendarEvent[], position: { top: number; height: number }): HTMLElement {
|
||||
const container = document.createElement('swp-event-group');
|
||||
container.style.cssText = `
|
||||
position: absolute;
|
||||
top: ${position.top}px;
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
`;
|
||||
return container;
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +188,7 @@ export class SimpleEventOverlapManager {
|
|||
const nextLink = this.getStackLink(nextElement);
|
||||
|
||||
// CRITICAL: Check if prev and next actually overlap without the middle element
|
||||
const actuallyOverlap = this.checkPixelOverlap(prevElement, nextElement);
|
||||
const actuallyOverlap = this.resolveOverlapType(prevElement, nextElement);
|
||||
|
||||
if (!actuallyOverlap) {
|
||||
// CHAIN BREAKING: prev and next don't overlap - break the chain
|
||||
|
|
@ -346,24 +330,7 @@ export class SimpleEventOverlapManager {
|
|||
const eventElement = container.querySelector(`swp-event[data-event-id="${eventId}"]`) as HTMLElement;
|
||||
if (!eventElement) return false;
|
||||
|
||||
// Calculate correct absolute position for standalone event
|
||||
const startTime = eventElement.dataset.start;
|
||||
if (startTime) {
|
||||
const startDate = new Date(startTime);
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const startMinutes = startDate.getHours() * 60 + startDate.getMinutes();
|
||||
const dayStartMinutes = gridSettings.dayStartHour * 60;
|
||||
const top = ((startMinutes - dayStartMinutes) / 60) * gridSettings.hourHeight;
|
||||
|
||||
// Convert back to absolute positioning
|
||||
eventElement.style.position = 'absolute';
|
||||
eventElement.style.top = `${top + 1}px`;
|
||||
eventElement.style.left = '2px';
|
||||
eventElement.style.right = '2px';
|
||||
eventElement.style.flex = '';
|
||||
eventElement.style.minWidth = '';
|
||||
}
|
||||
|
||||
// Simply remove the element - no position calculation needed since it's being removed
|
||||
eventElement.remove();
|
||||
|
||||
// Handle remaining events
|
||||
|
|
@ -378,22 +345,15 @@ export class SimpleEventOverlapManager {
|
|||
if (remainingCount === 1) {
|
||||
const remainingEvent = remainingEvents[0] as HTMLElement;
|
||||
|
||||
// Convert last event back to absolute positioning
|
||||
const remainingStartTime = remainingEvent.dataset.start;
|
||||
if (remainingStartTime) {
|
||||
const remainingStartDate = new Date(remainingStartTime);
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const remainingStartMinutes = remainingStartDate.getHours() * 60 + remainingStartDate.getMinutes();
|
||||
const dayStartMinutes = gridSettings.dayStartHour * 60;
|
||||
const remainingTop = ((remainingStartMinutes - dayStartMinutes) / 60) * gridSettings.hourHeight;
|
||||
|
||||
remainingEvent.style.position = 'absolute';
|
||||
remainingEvent.style.top = `${remainingTop + 1}px`;
|
||||
remainingEvent.style.left = '2px';
|
||||
remainingEvent.style.right = '2px';
|
||||
remainingEvent.style.flex = '';
|
||||
remainingEvent.style.minWidth = '';
|
||||
}
|
||||
// Convert last event back to absolute positioning - use current pixel position
|
||||
const currentTop = parseInt(remainingEvent.style.top) || 0;
|
||||
|
||||
remainingEvent.style.position = 'absolute';
|
||||
remainingEvent.style.top = `${currentTop}px`;
|
||||
remainingEvent.style.left = '2px';
|
||||
remainingEvent.style.right = '2px';
|
||||
remainingEvent.style.flex = '';
|
||||
remainingEvent.style.minWidth = '';
|
||||
|
||||
container.parentElement?.insertBefore(remainingEvent, container);
|
||||
container.remove();
|
||||
|
|
@ -471,33 +431,6 @@ export class SimpleEventOverlapManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate position for group - simplified calculation
|
||||
*/
|
||||
private calculateGroupPosition(events: CalendarEvent[]): { top: number; height: number } {
|
||||
if (events.length === 0) return { top: 0, height: 0 };
|
||||
|
||||
const times = events.flatMap(e => [
|
||||
new Date(e.start).getTime(),
|
||||
new Date(e.end).getTime()
|
||||
]);
|
||||
|
||||
const earliestStart = Math.min(...times);
|
||||
const latestEnd = Math.max(...times);
|
||||
|
||||
const startDate = new Date(earliestStart);
|
||||
const endDate = new Date(latestEnd);
|
||||
|
||||
const gridSettings = calendarConfig.getGridSettings();
|
||||
const startMinutes = startDate.getHours() * 60 + startDate.getMinutes();
|
||||
const endMinutes = endDate.getHours() * 60 + endDate.getMinutes();
|
||||
const dayStartMinutes = gridSettings.dayStartHour * 60;
|
||||
|
||||
const top = ((startMinutes - dayStartMinutes) / 60) * gridSettings.hourHeight;
|
||||
const height = ((endMinutes - startMinutes) / 60) * gridSettings.hourHeight;
|
||||
|
||||
return { top, height };
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility methods - simple DOM traversal
|
||||
|
|
@ -537,22 +470,4 @@ export class SimpleEventOverlapManager {
|
|||
return document.querySelector(`swp-event[data-event-id="${eventId}"]`) as HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two elements overlap in pixel space
|
||||
*/
|
||||
private checkPixelOverlap(element1: HTMLElement, element2: HTMLElement): boolean {
|
||||
if (!element1 || !element2) return false;
|
||||
|
||||
const top1 = parseFloat(element1.style.top) || 0;
|
||||
const height1 = parseFloat(element1.style.height) || 0;
|
||||
const bottom1 = top1 + height1;
|
||||
|
||||
const top2 = parseFloat(element2.style.top) || 0;
|
||||
const height2 = parseFloat(element2.style.height) || 0;
|
||||
const bottom2 = top2 + height2;
|
||||
|
||||
// Add tolerance for small gaps (borders, etc)
|
||||
const tolerance = 2;
|
||||
return !(bottom1 <= (top2 + tolerance) || bottom2 <= (top1 + tolerance));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue