Improves event grid layout logic

Refines the event grid layout algorithm to more accurately
identify conflicting events for stacking within the grid.

Now uses an expanding search to find chains of conflicting events,
ensuring that all events that should be grouped together are
correctly identified.
This commit is contained in:
Janus C. H. Knudsen 2025-10-06 18:46:07 +02:00
parent 06356df2a3
commit b590467f60
3 changed files with 51 additions and 7 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

11
.workbench/scenarie9.html Normal file
View file

@ -0,0 +1,11 @@
<swp-event-group class="cols-2 stack-level-0" data-stack-link="{&quot;stackLevel&quot;:0}" style="top: 481px; margin-left: 0px; z-index: 100;"><div style="position: relative;"><swp-event data-event-id="S9A" data-title="Scenario 9: Event A" data-start="2025-10-09T10:00:00.000Z" data-end="2025-10-09T11:00:00.000Z" data-type="work" data-duration="60" style="position: absolute; top: 0px; height: 77px; left: 0px; right: 0px;">
<swp-event-time data-duration="60">12:00 - 13:00</swp-event-time>
<swp-event-title>Scenario 9: Event A</swp-event-title>
</swp-event></div><div style="position: relative;"><swp-event data-event-id="S9B" data-title="Scenario 9: Event B" data-start="2025-10-09T10:30:00.000Z" data-end="2025-10-09T11:30:00.000Z" data-type="work" data-duration="60" style="position: absolute; top: 40px; height: 77px; left: 0px; right: 0px;">
<swp-event-time data-duration="60">12:30 - 13:30</swp-event-time>
<swp-event-title>Scenario 9: Event B</swp-event-title>
</swp-event></div></swp-event-group>
<swp-event data-event-id="S9C" data-title="Scenario 9: Event C" data-start="2025-10-09T11:15:00.000Z" data-end="2025-10-09T13:00:00.000Z" data-type="work" data-duration="105" data-stack-link="{&quot;stackLevel&quot;:1}" style="position: absolute; top: 581px; height: 137px; left: 2px; right: 2px; margin-left: 15px; z-index: 101;">
<swp-event-time data-duration="105">13:15 - 15:00</swp-event-time>
<swp-event-title>Scenario 9: Event C</swp-event-title>
</swp-event>

View file

@ -54,18 +54,51 @@ export class EventLayoutCoordinator {
const firstEvent = remaining[0]; const firstEvent = remaining[0];
// Find events that could be in GRID with first event // Find events that could be in GRID with first event
// (start within threshold AND overlap) // Use expanding search to find chains (A→B→C where each conflicts with next)
const gridSettings = calendarConfig.getGridSettings(); const gridSettings = calendarConfig.getGridSettings();
const thresholdMinutes = gridSettings.gridStartThresholdMinutes; const thresholdMinutes = gridSettings.gridStartThresholdMinutes;
const gridCandidates = [firstEvent]; const gridCandidates = [firstEvent];
for (let i = 1; i < remaining.length; i++) { let candidatesChanged = true;
const candidate = remaining[i];
const startDiff = Math.abs(candidate.start.getTime() - firstEvent.start.getTime()) / (1000 * 60);
// Only add if starts within threshold AND overlaps with firstEvent // Keep expanding until no new candidates can be added
if (startDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(firstEvent, candidate)) { while (candidatesChanged) {
gridCandidates.push(candidate); candidatesChanged = false;
for (let i = 1; i < remaining.length; i++) {
const candidate = remaining[i];
// Skip if already in candidates
if (gridCandidates.includes(candidate)) continue;
// Check if candidate conflicts with ANY event in gridCandidates
for (const existingCandidate of gridCandidates) {
let hasConflict = false;
// Check 1: Start-to-start conflict (starts within threshold)
const startToStartDiff = Math.abs(candidate.start.getTime() - existingCandidate.start.getTime()) / (1000 * 60);
if (startToStartDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(candidate, existingCandidate)) {
hasConflict = true;
}
// Check 2: End-to-start conflict (candidate starts within threshold before existingCandidate ends)
const endToStartMinutes = (existingCandidate.end.getTime() - candidate.start.getTime()) / (1000 * 60);
if (endToStartMinutes > 0 && endToStartMinutes <= thresholdMinutes) {
hasConflict = true;
}
// Check 3: Reverse end-to-start (existingCandidate starts within threshold before candidate ends)
const reverseEndToStart = (candidate.end.getTime() - existingCandidate.start.getTime()) / (1000 * 60);
if (reverseEndToStart > 0 && reverseEndToStart <= thresholdMinutes) {
hasConflict = true;
}
if (hasConflict) {
gridCandidates.push(candidate);
candidatesChanged = true;
break; // Found conflict, move to next candidate
}
}
} }
} }