2025-10-05 23:54:50 +02:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Event Stacking Visualization< / title >
< style >
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 40px;
background: #f5f5f5;
line-height: 1.6;
}
h1 {
color: #333;
margin-bottom: 10px;
}
h2 {
color: #555;
margin-top: 40px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #ddd;
}
h3 {
color: #666;
margin-top: 30px;
margin-bottom: 15px;
}
.section {
background: white;
padding: 30px;
margin-bottom: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
margin-top: 20px;
}
.calendar-column {
background: #fafafa;
border: 2px solid #e0e0e0;
border-radius: 4px;
padding: 20px;
position: relative;
min-height: 400px;
}
.column-title {
font-weight: bold;
margin-bottom: 15px;
color: #333;
text-align: center;
}
.timeline {
position: absolute;
left: 0;
top: 60px;
width: 50px;
height: calc(100% - 80px);
border-right: 2px solid #ddd;
background: #fff;
}
.time-marker {
position: absolute;
left: 5px;
font-size: 11px;
color: #999;
transform: translateY(-50%);
}
.events-container {
position: relative;
margin-left: 60px;
height: 350px;
background: white;
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.event {
position: absolute;
border-radius: 4px;
padding: 8px;
font-size: 13px;
font-weight: 500;
border: 2px solid;
cursor: default;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.event-a {
background: #e3f2fd;
border-color: #2196f3;
color: #1565c0;
}
.event-b {
background: #f3e5f5;
border-color: #9c27b0;
color: #6a1b9a;
}
.event-c {
background: #fff3e0;
border-color: #ff9800;
color: #e65100;
}
.event-d {
background: #e8f5e9;
border-color: #4caf50;
color: #2e7d32;
}
.legend {
margin-top: 20px;
padding: 15px;
background: #f9f9f9;
border-radius: 4px;
font-size: 14px;
}
.legend-item {
margin: 5px 0;
}
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
margin-left: 10px;
}
.badge-level-0 { background: #e3f2fd; color: #1565c0; }
.badge-level-1 { background: #f3e5f5; color: #6a1b9a; }
.badge-level-2 { background: #fff3e0; color: #e65100; }
.savings {
background: #c8e6c9;
padding: 15px;
border-left: 4px solid #4caf50;
margin-top: 20px;
border-radius: 4px;
}
.problem {
background: #ffcdd2;
padding: 15px;
border-left: 4px solid #f44336;
margin-top: 20px;
border-radius: 4px;
}
.note {
background: #fff9c4;
padding: 10px;
border-left: 4px solid #fbc02d;
margin: 15px 0;
font-size: 14px;
}
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.grid-lines {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
}
.grid-line {
position: absolute;
left: 0;
right: 0;
height: 1px;
background: #f0f0f0;
}
.single-column {
max-width: 600px;
}
2025-10-06 21:16:29 +02:00
.test-badge {
display: inline-block;
margin-left: 15px;
padding: 6px 14px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
vertical-align: middle;
}
.test-passed {
background: #4caf50;
color: white;
}
.test-failed {
background: #f44336;
color: white;
}
.test-pending {
background: #ff9800;
color: white;
}
2025-10-05 23:54:50 +02:00
< / style >
< / head >
< body >
< h1 > Event Stacking Visualization< / h1 >
< p style = "color: #666; margin-bottom: 30px;" > Visual demonstration of naive vs optimized event stacking< / p >
<!-- Scenario 1: Your Example -->
< div class = "section" >
< h2 > Scenario 1: Your Example - The Problem with Naive Stacking< / h2 >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 09:00 - 14:00 (5 hours, contains both B and C)< / li >
< li > Event B: 10:00 - 12:00 (2 hours)< / li >
< li > Event C: 12:30 - 13:00 (30 minutes)< / li >
< / ul >
< div class = "note" >
< strong > Key Observation:< / strong > Event B and Event C do < strong > NOT< / strong > overlap with each other!
They are separated by 30 minutes (12:00 to 12:30).
< / div >
< div class = "comparison" >
<!-- Naive Stacking -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ Naive Stacking (Inefficient)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 20%" > 10:00< / div >
< div class = "time-marker" style = "top: 40%" > 11:00< / div >
< div class = "time-marker" style = "top: 60%" > 12:00< / div >
< div class = "time-marker" style = "top: 80%" > 13:00< / div >
< div class = "time-marker" style = "top: 100%" > 14:00< / div >
< / div >
< div class = "events-container" >
< div class = "grid-lines" >
< div class = "grid-line" style = "top: 0%" > < / div >
< div class = "grid-line" style = "top: 20%" > < / div >
< div class = "grid-line" style = "top: 40%" > < / div >
< div class = "grid-line" style = "top: 60%" > < / div >
< div class = "grid-line" style = "top: 80%" > < / div >
< div class = "grid-line" style = "top: 100%" > < / div >
< / div >
<!-- Event A: 09:00 - 14:00 (full height) -->
< div class = "event event-a" style = "
top: 0%;
height: 100%;
left: 2px;
right: 2px;
z-index: 100;
">Event A (09:00-14:00)< / div >
<!-- Event B: 10:00 - 12:00 (stackLevel 1 = 15px offset) -->
< div class = "event event-b" style = "
top: 20%;
height: 40%;
left: 17px;
right: 2px;
z-index: 101;
">Event B (10:00-12:00)< / div >
<!-- Event C: 12:30 - 13:00 (stackLevel 2 = 30px offset) -->
< div class = "event event-c" style = "
top: 70%;
height: 10%;
left: 32px;
right: 2px;
z-index: 102;
">Event C (12:30-13:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: < code > marginLeft: 0px< / code > < span class = "badge badge-level-0" > Level 0< / span > < / div >
< div class = "legend-item" > Event B: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< div class = "legend-item" > Event C: < code > marginLeft: 30px< / code > < span class = "badge badge-level-2" > Level 2< / span > < / div >
< / div >
< div class = "problem" >
< strong > Problem:< / strong > Event C is pushed 30px to the right even though it doesn't conflict with Event B! Wastes 15px of space.
< / div >
< / div >
<!-- Optimized Stacking -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Optimized Stacking (Efficient)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 20%" > 10:00< / div >
< div class = "time-marker" style = "top: 40%" > 11:00< / div >
< div class = "time-marker" style = "top: 60%" > 12:00< / div >
< div class = "time-marker" style = "top: 80%" > 13:00< / div >
< div class = "time-marker" style = "top: 100%" > 14:00< / div >
< / div >
< div class = "events-container" >
< div class = "grid-lines" >
< div class = "grid-line" style = "top: 0%" > < / div >
< div class = "grid-line" style = "top: 20%" > < / div >
< div class = "grid-line" style = "top: 40%" > < / div >
< div class = "grid-line" style = "top: 60%" > < / div >
< div class = "grid-line" style = "top: 80%" > < / div >
< div class = "grid-line" style = "top: 100%" > < / div >
< / div >
<!-- Event A: 09:00 - 14:00 -->
< div class = "event event-a" style = "
top: 0%;
height: 100%;
left: 2px;
right: 2px;
z-index: 100;
">Event A (09:00-14:00)< / div >
<!-- Event B: 10:00 - 12:00 (stackLevel 1 = 15px offset) -->
< div class = "event event-b" style = "
top: 20%;
height: 40%;
left: 17px;
right: 2px;
z-index: 101;
">Event B (10:00-12:00)< / div >
<!-- Event C: 12:30 - 13:00 (ALSO stackLevel 1 = 15px offset) -->
< div class = "event event-c" style = "
top: 70%;
height: 10%;
left: 17px;
right: 2px;
z-index: 101;
">Event C (12:30-13:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: < code > marginLeft: 0px< / code > < span class = "badge badge-level-0" > Level 0< / span > < / div >
< div class = "legend-item" > Event B: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< div class = "legend-item" > Event C: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< / div >
< div class = "savings" >
< strong > Benefit:< / strong > Event C reuses stackLevel 1 because it doesn't conflict with Event B. Saves 15px (33% space savings)!
< / div >
< / div >
< / div >
< / div >
<!-- Scenario 2: Multiple Parallel Tracks -->
< div class = "section" >
< h2 > Scenario 2: Multiple Parallel Tracks< / h2 >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 09:00 - 15:00 (6 hours, very long event)< / li >
< li > Event B: 10:00 - 11:00 (1 hour)< / li >
< li > Event C: 11:30 - 12:30 (1 hour)< / li >
< li > Event D: 13:00 - 14:00 (1 hour)< / li >
< / ul >
< div class = "note" >
< strong > Key Insight:< / strong > Events B, C, and D all overlap with A, but < strong > NOT< / strong > with each other.
They can all share stackLevel 1!
< / div >
< div class = "comparison" >
<!-- Naive -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ Naive (4 levels)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A< / div >
<!-- Event B: stackLevel 1 -->
< div class = "event event-b" style = "top: 16.67%; height: 16.67%; left: 17px; right: 2px; z-index: 101;" > Event B< / div >
<!-- Event C: stackLevel 2 (naive) -->
< div class = "event event-c" style = "top: 41.67%; height: 16.67%; left: 32px; right: 2px; z-index: 102;" > Event C< / div >
<!-- Event D: stackLevel 3 (naive) -->
< div class = "event event-d" style = "top: 66.67%; height: 16.67%; left: 47px; right: 2px; z-index: 103;" > Event D< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: Level 0 (0px)< / div >
< div class = "legend-item" > Event B: Level 1 (15px)< / div >
< div class = "legend-item" > Event C: Level 2 (30px)< / div >
< div class = "legend-item" > Event D: Level 3 (45px)< / div >
< / div >
< div class = "problem" >
Total width: < strong > 60px< / strong > (0+15+30+45)
< / div >
< / div >
<!-- Optimized -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Optimized (2 levels)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A< / div >
<!-- Event B: stackLevel 1 -->
< div class = "event event-b" style = "top: 16.67%; height: 16.67%; left: 17px; right: 2px; z-index: 101;" > Event B< / div >
<!-- Event C: ALSO stackLevel 1 -->
< div class = "event event-c" style = "top: 41.67%; height: 16.67%; left: 17px; right: 2px; z-index: 101;" > Event C< / div >
<!-- Event D: ALSO stackLevel 1 -->
< div class = "event event-d" style = "top: 66.67%; height: 16.67%; left: 17px; right: 2px; z-index: 101;" > Event D< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: Level 0 (0px)< / div >
< div class = "legend-item" > Event B: Level 1 (15px)< / div >
< div class = "legend-item" > Event C: Level 1 (15px)< / div >
< div class = "legend-item" > Event D: Level 1 (15px)< / div >
< / div >
< div class = "savings" >
Total width: < strong > 30px< / strong > (0+15+15+15)< br >
< strong > 50% space savings!< / strong >
< / div >
< / div >
< / div >
< / div >
<!-- Scenario 3: Nested Overlaps -->
< div class = "section" >
< h2 > Scenario 3: Nested Overlaps with Optimization< / h2 >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 09:00 - 15:00 (6 hours, contains all)< / li >
< li > Event B: 10:00 - 13:00 (3 hours)< / li >
< li > Event C: 11:00 - 12:00 (1 hour)< / li >
< li > Event D: 12:30 - 13:30 (1 hour)< / li >
< / ul >
< div class = "note" >
< strong > Complex Case:< / strong > C and D both overlap with A and B, but C and D don't overlap with < strong > each other< / strong > . They can share a level!
< / div >
< div class = "comparison" >
<!-- Naive -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ Naive (4 levels)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: 09:00 - 15:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A< / div >
<!-- Event B: 10:00 - 13:00 (stackLevel 1) -->
< div class = "event event-b" style = "top: 16.67%; height: 50%; left: 17px; right: 2px; z-index: 101;" > Event B< / div >
<!-- Event C: 11:00 - 12:00 (stackLevel 2) -->
< div class = "event event-c" style = "top: 33.33%; height: 16.67%; left: 32px; right: 2px; z-index: 102;" > Event C< / div >
<!-- Event D: 12:30 - 13:30 (stackLevel 3) -->
< div class = "event event-d" style = "top: 58.33%; height: 16.67%; left: 47px; right: 2px; z-index: 103;" > Event D< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > A: Level 0, B: Level 1, C: Level 2, D: Level 3< / div >
< / div >
< / div >
<!-- Optimized -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Optimized (3 levels)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: 09:00 - 15:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A< / div >
<!-- Event B: 10:00 - 13:00 (stackLevel 1) -->
< div class = "event event-b" style = "top: 16.67%; height: 50%; left: 17px; right: 2px; z-index: 101;" > Event B< / div >
<!-- Event C: 11:00 - 12:00 (stackLevel 2) -->
< div class = "event event-c" style = "top: 33.33%; height: 16.67%; left: 32px; right: 2px; z-index: 102;" > Event C< / div >
<!-- Event D: 12:30 - 13:30 (ALSO stackLevel 2!) -->
< div class = "event event-d" style = "top: 58.33%; height: 16.67%; left: 32px; right: 2px; z-index: 102;" > Event D< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > A: Level 0, B: Level 1, C & D: Level 2< / div >
< / div >
< div class = "savings" >
< strong > 25% space savings!< / strong > D shares level with C because they don't overlap.
< / div >
< / div >
< / div >
< / div >
<!-- Scenario 4: Fully Nested Events (Complete Containment) -->
< div class = "section" >
< h2 > Scenario 4: Fully Nested Events - All Must Stack< / h2 >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 09:00 - 15:00 (6 hours, contains B)< / li >
< li > Event B: 10:00 - 14:00 (4 hours, contains C)< / li >
< li > Event C: 11:00 - 13:00 (2 hours, innermost)< / li >
< / ul >
< div class = "note" >
< strong > Important Case:< / strong > When Event C is completely inside Event B, and Event B is completely inside Event A,
< strong > all three events overlap with each other< / strong > . No optimization is possible - they must all stack sequentially.
< / div >
< div class = "comparison" >
<!-- Naive -->
< div class = "calendar-column" >
< div class = "column-title" > Naive Stacking< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: 09:00 - 15:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A (09:00-15:00)< / div >
<!-- Event B: 10:00 - 14:00 (stackLevel 1) -->
< div class = "event event-b" style = "top: 16.67%; height: 66.67%; left: 17px; right: 2px; z-index: 101;" > Event B (10:00-14:00)< / div >
<!-- Event C: 11:00 - 13:00 (stackLevel 2) -->
< div class = "event event-c" style = "top: 33.33%; height: 33.33%; left: 32px; right: 2px; z-index: 102;" > Event C (11:00-13:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: < code > marginLeft: 0px< / code > < span class = "badge badge-level-0" > Level 0< / span > < / div >
< div class = "legend-item" > Event B: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< div class = "legend-item" > Event C: < code > marginLeft: 30px< / code > < span class = "badge badge-level-2" > Level 2< / span > < / div >
< / div >
< div style = "background: #e3f2fd; padding: 15px; border-left: 4px solid #2196f3; margin-top: 20px; border-radius: 4px;" >
< strong > Analysis:< / strong > All events overlap with each other:< br >
• A overlaps B: ✓ (B is inside A)< br >
• A overlaps C: ✓ (C is inside A)< br >
• B overlaps C: ✓ (C is inside B)< br >
< br >
Result: Sequential stacking required.
< / div >
< / div >
<!-- Optimized (Same Result) -->
< div class = "calendar-column" >
< div class = "column-title" > Optimized Stacking (Same Result)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 16.67%" > 10:00< / div >
< div class = "time-marker" style = "top: 33.33%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 66.67%" > 13:00< / div >
< div class = "time-marker" style = "top: 83.33%" > 14:00< / div >
< div class = "time-marker" style = "top: 100%" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: 09:00 - 15:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A (09:00-15:00)< / div >
<!-- Event B: 10:00 - 14:00 (stackLevel 1) -->
< div class = "event event-b" style = "top: 16.67%; height: 66.67%; left: 17px; right: 2px; z-index: 101;" > Event B (10:00-14:00)< / div >
<!-- Event C: 11:00 - 13:00 (stackLevel 2 - CANNOT be optimized) -->
< div class = "event event-c" style = "top: 33.33%; height: 33.33%; left: 32px; right: 2px; z-index: 102;" > Event C (11:00-13:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: < code > marginLeft: 0px< / code > < span class = "badge badge-level-0" > Level 0< / span > < / div >
< div class = "legend-item" > Event B: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< div class = "legend-item" > Event C: < code > marginLeft: 30px< / code > < span class = "badge badge-level-2" > Level 2< / span > < / div >
< / div >
< div style = "background: #fff9c4; padding: 15px; border-left: 4px solid #fbc02d; margin-top: 20px; border-radius: 4px;" >
< strong > No Optimization Possible:< / strong > < br >
The optimized algorithm tries to assign C to level 1, but level 1 is occupied by B which overlaps with C.
It then tries level 2 - which is free. Result is identical to naive approach.
< / div >
< / div >
< / div >
< div style = "margin-top: 20px; padding: 20px; background: #f5f5f5; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Algorithm Behavior:< / h3 >
< pre style = "background: white; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 13px;" >
For Event C (11:00-13:00):
overlapping = [Event A, Event B] // Both A and B overlap with C
Try stackLevel 0:
✗ Occupied by Event A (which overlaps C)
Try stackLevel 1:
✗ Occupied by Event B (which overlaps C)
Try stackLevel 2:
✓ Free! Assign C to stackLevel 2
Result: C must be at level 2 (no optimization)< / pre >
< / div >
< div style = "margin-top: 20px; padding: 15px; background: #e8f5e9; border-left: 4px solid #4caf50; border-radius: 4px;" >
< strong > Key Takeaway:< / strong > Optimization only helps when events at higher levels don't overlap with each other.
When events are fully nested (matryoshka doll pattern), both approaches yield the same result.
< / div >
< / div >
<!-- Scenario 5: Column Sharing vs Stacking -->
< div class = "section" >
< h2 > Scenario 5: Column Sharing - When Events Start Close Together< / h2 >
< p > < strong > New Concept:< / strong > When events start within a threshold (±15 minutes, configurable), they should be displayed side-by-side (column sharing) instead of stacked.< / p >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 10:00 - 13:00 (3 hours)< / li >
< li > Event B: 11:00 - 12:30 (1.5 hours, starts 60 min after A)< / li >
< li > Event C: 11:00 - 12:00 (1 hour, starts same time as B)< / li >
< / ul >
< div class = "note" >
< strong > Threshold Logic (±15 minutes):< / strong > < br >
• Event A starts at 10:00< br >
• Events B and C both start at 11:00< br >
• A vs B/C: 60 minutes apart (exceeds ±15 min threshold) → < strong > A is stacked separately< / strong > < br >
• B vs C: 0 minutes apart (within ±15 min threshold) → < strong > B and C share flexbox< / strong > < br >
• < strong > Result:< / strong > A gets full width (stackLevel 0), B and C share flexbox at stackLevel 1 (50%/50%)
< / div >
< div class = "comparison" >
<!-- Stacking Only (Wrong Approach) -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ Pure Stacking (Poor UX)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 10:00< / div >
< div class = "time-marker" style = "top: 25%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 75%" > 12:30< / div >
< div class = "time-marker" style = "top: 100%" > 13:00< / div >
< / div >
< div class = "events-container" style = "height: 300px;" >
<!-- Event A: 10:00 - 13:00 (full 3 hours) -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A (10:00-13:00)< / div >
<!-- Event B: 11:00 - 12:30 (stacked at 15px) -->
< div class = "event event-b" style = "top: 33.33%; height: 50%; left: 17px; right: 2px; z-index: 101;" > Event B (11:00-12:30)< / div >
<!-- Event C: 11:00 - 12:00 (stacked at 30px) -->
< div class = "event event-c" style = "top: 33.33%; height: 33.33%; left: 32px; right: 2px; z-index: 102;" > Event C (11:00-12:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: Level 0 (0px)< / div >
< div class = "legend-item" > Event B: Level 1 (15px)< / div >
< div class = "legend-item" > Event C: Level 2 (30px)< / div >
< / div >
< div class = "problem" >
< strong > Problem:< / strong > B and C start at the same time but are stacked sequentially.
Wastes horizontal space and makes it hard to see that they start together.
< / div >
< / div >
<!-- Column Sharing (Correct Approach) -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Column Sharing (Better UX)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 10:00< / div >
< div class = "time-marker" style = "top: 25%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 75%" > 12:30< / div >
< div class = "time-marker" style = "top: 100%" > 13:00< / div >
< / div >
< div class = "events-container" style = "height: 300px;" >
<!-- Event A: 10:00 - 13:00 (full width) -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A (10:00-13:00)< / div >
<!-- Flexbox container for B and C (they share space 50/50) -->
< div style = "
position: absolute;
top: 33.33%;
height: 66.67%;
left: 17px;
right: 2px;
display: flex;
gap: 2px;
z-index: 101;
">
<!-- Event B: 11:00 - 12:30 (50% width, 75% of container height) -->
< div class = "event event-b" style = "
position: relative;
flex: 1;
height: 75%;
left: 0;
right: 0;
">Event B (11:00-12:30)< / div >
<!-- Event C: 11:00 - 12:00 (50% width, 50% of container height) -->
< div class = "event event-c" style = "
position: relative;
flex: 1;
height: 50%;
left: 0;
right: 0;
">Event C (11:00-12:00)< / div >
< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: stackLevel 0 (full width)< / div >
< div class = "legend-item" > Events B & C: stackLevel 1 (flex: 1 each = 50% / 50%)< / div >
< / div >
< div class = "savings" >
< strong > Benefits:< / strong > < br >
• Clear visual indication that B and C start at same time< br >
• Better space utilization (no 30px offset for C)< br >
• Scales well: if Event D is added at 11:00, all three share 33% / 33% / 33%
< / div >
< / div >
< / div >
< div style = "margin-top: 20px; padding: 20px; background: #f5f5f5; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Column Sharing Algorithm:< / h3 >
< pre style = "background: white; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 13px;" >
const FLEXBOX_START_THRESHOLD_MINUTES = 15; // Configurable
function shouldShareFlexbox(event1, event2) {
const startDiff = Math.abs(event1.start - event2.start) / (1000 * 60);
return startDiff < = FLEXBOX_START_THRESHOLD_MINUTES;
}
For events A, B, C:
• A starts at 10:00
• B starts at 11:00 (diff = 60 min > 15 min) → A and B do NOT share
• C starts at 11:00 (diff = 0 min ≤ 15 min) → B and C DO share
Result:
• Event A: stackLevel 0, full width
• Events B & C: stackLevel 1, flexbox container (50% each)< / pre >
< / div >
< div style = "margin-top: 20px; padding: 15px; background: #e3f2fd; border-left: 4px solid #2196f3; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Hybrid Approach: Column Sharing + Stacking + Nesting< / h3 >
< p > The best approach combines < strong > three techniques< / strong > :< / p >
< ul style = "margin-left: 20px;" >
< li > < strong > Column Sharing (Flexbox):< / strong > When events start within ±15 min threshold< / li >
< li > < strong > Regular Stacking:< / strong > When events start far apart (> 15 min)< / li >
< li > < strong > Nested Stacking:< / strong > When an event starts outside threshold but overlaps a flexbox column< / li >
< / ul >
< p style = "margin-top: 10px;" >
< strong > Example:< / strong > If a 4th event (Event D) starts at 11:30, it would NOT join the B/C flexbox
(30 min > 15 min threshold). Instead, D would be stacked INSIDE whichever column it overlaps (e.g., B's column)
with a 15px left margin to show the nested relationship.
< / p >
< / div >
< / div >
<!-- Scenario 6.5: Real Data - Events 144, 145, 146 -->
< div class = "section" >
< h2 > Scenario 6.5: Real Data - Events 144, 145, 146 (Chain Overlap)< / h2 >
< p > < strong > Events (from actual JSON data):< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event 145 (Månedlig Planlægning): 07:00 - 08:00 (1 hour)< / li >
< li > Event 144 (Team Standup): 07:30 - 08:30 (1 hour)< / li >
< li > Event 146 (Performance Test): 08:15 - 10:00 (1h 45min)< / li >
< / ul >
< div class = "note" >
< strong > Key Observation:< / strong > < br >
• 145 ↔ 144: < strong > OVERLAP< / strong > (07:30-08:00 = 30 min)< br >
• 145 ↔ 146: < strong > NO OVERLAP< / strong > (145 ends 08:00, 146 starts 08:15)< br >
• 144 ↔ 146: < strong > OVERLAP< / strong > (08:15-08:30 = 15 min)< br >
< br >
< strong > Expected Stack Levels:< / strong > < br >
• Event 145: stackLevel 0 (margin-left: 0px)< br >
• Event 144: stackLevel 1 (margin-left: 15px) - overlaps 145< br >
• Event 146: stackLevel 2 (margin-left: 30px) - overlaps 144< br >
< br >
< strong > Why 146 cannot share level with 145:< / strong > < br >
Even though 145 and 146 don't overlap, 146 overlaps with 144 (which has stackLevel 1).
Therefore 146 must be ABOVE 144 → stackLevel 2.
< / div >
< div class = "comparison" >
< div class = "calendar-column" >
< div class = "column-title" > ✅ Correct: Chain Overlap Stacking< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 07:00< / div >
< div class = "time-marker" style = "top: 33%" > 08:00< / div >
< div class = "time-marker" style = "top: 66%" > 09:00< / div >
< div class = "time-marker" style = "top: 100%" > 10:00< / div >
< / div >
< div class = "events-container" style = "height: 300px;" >
<!-- Event 145: 07:00 - 08:00 (stackLevel 0) -->
< div class = "event event-a" style = "
top: 0%;
height: 33.33%;
left: 2px;
right: 2px;
z-index: 100;
">145: Månedlig (07:00-08:00)< / div >
<!-- Event 144: 07:30 - 08:30 (stackLevel 1 = 15px offset) -->
< div class = "event event-b" style = "
top: 16.66%;
height: 33.33%;
left: 17px;
right: 2px;
z-index: 101;
">144: Standup (07:30-08:30)< / div >
<!-- Event 146: 08:15 - 10:00 (stackLevel 2 = 30px offset) -->
< div class = "event event-c" style = "
top: 41.66%;
height: 58.34%;
left: 32px;
right: 2px;
z-index: 102;
">146: Performance (08:15-10:00)< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event 145: < code > marginLeft: 0px< / code > < span class = "badge badge-level-0" > Level 0< / span > < / div >
< div class = "legend-item" > Event 144: < code > marginLeft: 15px< / code > < span class = "badge badge-level-1" > Level 1< / span > < / div >
< div class = "legend-item" > Event 146: < code > marginLeft: 30px< / code > < span class = "badge badge-level-2" > Level 2< / span > < / div >
< / div >
< div class = "savings" >
< strong > Why stackLevel 2 for 146?< / strong > < br >
146 overlaps with 144 (stackLevel 1), so 146 must be positioned ABOVE 144.
Even though 146 doesn't overlap 145, it forms a "chain" through 144.
< / div >
< / div >
< / div >
< / div >
<!-- Scenario 7: Column Sharing for Overlapping Events (Real Case: 153, 154) -->
< div class = "section" >
< h2 > Scenario 7: Column Sharing for Overlapping Events Starting Simultaneously< / h2 >
< p > < strong > Events (start at same time but overlap):< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event 153: 09:00 - 10:00 (1 hour)< / li >
< li > Event 154: 09:00 - 09:30 (30 minutes)< / li >
< / ul >
< div class = "note" >
< strong > Key Observation:< / strong > < br >
• Events start at SAME time (09:00)< br >
• Event 154 OVERLAPS with Event 153 (09:00-09:30)< br >
• Even though they overlap, they should share columns 50/50 because they start simultaneously< br >
< br >
< strong > Expected Rendering:< / strong > < br >
• Use GRID container (not stacking)< br >
• Both events get 50% width (side-by-side)< br >
• Event 153: Full height (1 hour) in left column< br >
• Event 154: Shorter height (30 min) in right column< br >
< br >
< strong > Rule:< / strong > < br >
Events starting simultaneously (±15 min) should ALWAYS use column sharing (GRID),
even if they overlap each other.
< / div >
< div class = "comparison" >
< div class = "calendar-column" >
< div class = "column-title" > ❌ Wrong: Stacking< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 50%" > 09:30< / div >
< div class = "time-marker" style = "top: 100%" > 10:00< / div >
< / div >
< div class = "events-container" style = "height: 200px;" >
<!-- Event 153: 09:00 - 10:00 (stackLevel 0) -->
< div class = "event event-a" style = "
top: 0%;
height: 100%;
left: 2px;
right: 2px;
z-index: 100;
">153 (09:00-10:00)< / div >
<!-- Event 154: 09:00 - 09:30 (stackLevel 1 = 15px offset) - WRONG! -->
< div class = "event event-b" style = "
top: 0%;
height: 50%;
left: 17px;
right: 2px;
z-index: 101;
">154 (09:00-09:30)< / div >
< / div >
< div class = "problem" >
< strong > Problem:< / strong > Event 154 is stacked on top of 153 even though they start at the same time.
This makes it hard to see that they're simultaneous events.
< / div >
< / div >
< div class = "calendar-column" >
< div class = "column-title" > ✅ Correct: Column Sharing (GRID)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 09:00< / div >
< div class = "time-marker" style = "top: 50%" > 09:30< / div >
< div class = "time-marker" style = "top: 100%" > 10:00< / div >
< / div >
< div class = "events-container" style = "height: 200px;" >
<!-- Grid container for both events (50/50 split) -->
< div style = "
position: absolute;
top: 0%;
height: 100%;
left: 2px;
right: 2px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2px;
z-index: 100;
">
<!-- Event 153: 09:00 - 10:00 (full height, left column) -->
< div class = "event event-a" style = "
position: relative;
height: 100%;
">153 (09:00-10:00)< / div >
<!-- Event 154: 09:00 - 09:30 (50% height, right column) -->
< div class = "event event-b" style = "
position: relative;
height: 50%;
">154 (09:00-09:30)< / div >
< / div >
< / div >
< div class = "savings" >
< strong > Benefits:< / strong > < br >
• Clear visual that events start simultaneously< br >
• Better use of horizontal space< br >
• Each event gets 50% width instead of being stacked
< / div >
< / div >
< / div >
< / div >
<!-- Scenario 6: Column Sharing + Nested Stacking -->
< div class = "section" >
< h2 > Scenario 6: Column Sharing with Nested Stacking< / h2 >
< p > < strong > Complex Case:< / strong > What happens when a 4th event needs to be added to an existing column-sharing group?< / p >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 10:00 - 13:00 (3 hours)< / li >
< li > Event B: 11:00 - 12:30 (1.5 hours)< / li >
< li > Event C: 11:00 - 12:00 (1 hour)< / li >
< li > < strong > Event D: 11:30 - 11:45 (15 minutes) ← NEW!< / strong > < / li >
< / ul >
< div class = "note" >
< strong > New Rule:< / strong > Flexbox threshold = < strong > ±15 minutes< / strong > (configurable)< br >
• B starts at 11:00< br >
• C starts at 11:00 (diff = 0 min ≤ 15 min) → < strong > B and C share flexbox< / strong > ✓< br >
• D starts at 11:30 (diff = 30 min > 15 min) → < strong > D does NOT join flexbox< / strong > ✗< br >
• D overlaps only with B → < strong > D is stacked inside B's column< / strong > ✓
< / div >
< div class = "comparison" >
<!-- Wrong: All in flexbox -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ All Events in Flexbox (Wrong)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 10:00< / div >
< div class = "time-marker" style = "top: 25%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 75%" > 12:30< / div >
< div class = "time-marker" style = "top: 100%" > 13:00< / div >
< / div >
< div class = "events-container" style = "height: 300px;" >
<!-- Event A: 10:00 - 13:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A< / div >
<!-- Flexbox with B, C, D (33% each) -->
< div style = "
position: absolute;
top: 33.33%;
height: 66.67%;
left: 17px;
right: 2px;
display: flex;
gap: 2px;
z-index: 101;
">
< div class = "event event-b" style = "position: relative; flex: 1; height: 75%;" > Event B< / div >
< div class = "event event-c" style = "position: relative; flex: 1; height: 50%;" > Event C< / div >
< div class = "event event-d" style = "position: relative; flex: 1; height: 25%;" > Event D< / div >
< / div >
< / div >
< div class = "problem" >
< strong > Problem:< / strong > All events get 33% width, making them too narrow.
Event D is squeezed even though it's contained within Event B's timeframe.
< / div >
< / div >
<!-- Correct: Nested stacking inside flexbox column -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Flexbox + Nested Stack in Column< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 10:00< / div >
< div class = "time-marker" style = "top: 25%" > 11:00< / div >
< div class = "time-marker" style = "top: 50%" > 12:00< / div >
< div class = "time-marker" style = "top: 75%" > 12:30< / div >
< div class = "time-marker" style = "top: 100%" > 13:00< / div >
< / div >
< div class = "events-container" style = "height: 300px;" >
<!-- Event A: 10:00 - 13:00 -->
< div class = "event event-a" style = "top: 0%; height: 100%; left: 2px; right: 2px; z-index: 100;" > Event A (10:00-13:00)< / div >
<!-- Flexbox container for B and C (they start at same time: 11:00) -->
< div style = "
position: absolute;
top: 33.33%;
height: 66.67%;
left: 17px;
right: 2px;
display: flex;
gap: 2px;
z-index: 101;
">
<!-- Event B column (50% width) with nested Event D inside -->
< div style = "position: relative; flex: 1;" >
<!-- Event B -->
< div class = "event event-b" style = "
position: absolute;
top: 0;
height: 75%;
left: 0;
right: 0;
z-index: 101;
">Event B (11:00-12:30)< / div >
<!-- Event D nested INSIDE B's column, stacked with offset -->
< div class = "event event-d" style = "
position: absolute;
top: 25%;
height: 12.5%;
left: 15px;
right: 0;
z-index: 102;
font-size: 11px;
">Event D (11:30-11:45)< / div >
< / div >
<!-- Event C column (50% width) -->
< div class = "event event-c" style = "
position: relative;
flex: 1;
height: 50%;
">Event C (11:00-12:00)< / div >
< / div >
< / div >
< div class = "legend" >
< div class = "legend-item" > Event A: stackLevel 0 (full width)< / div >
< div class = "legend-item" > Events B & C: stackLevel 1 (flexbox 50%/50%)< / div >
< div class = "legend-item" > Event D: Nested in B's column with 15px marginLeft< / div >
< / div >
< div class = "savings" >
< strong > Strategy:< / strong > < br >
• B and C start at 11:00 (diff = 0 min ≤ 15 min threshold) → < strong > Use flexbox< / strong > ✓< br >
• D starts at 11:30 (diff = 30 min > 15 min threshold) → < strong > NOT in flexbox< / strong > ✗< br >
• D overlaps with B (11:00-12:30) but NOT C (11:00-12:00) ✓< br >
• D is stacked INSIDE B's flexbox column with 15px left margin
< / div >
< / div >
< / div >
< div style = "margin-top: 20px; padding: 20px; background: #f5f5f5; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Nested Stacking in Flexbox Columns:< / h3 >
< pre style = "background: white; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 13px; line-height: 1.6;" >
const FLEXBOX_START_THRESHOLD_MINUTES = 15; // Configurable
Step 1: Identify flexbox groups (events starting within ±15 min)
• B starts at 11:00
• C starts at 11:00 (diff = 0 min ≤ 15 min) → B and C share flexbox ✓
• D starts at 11:30 (diff = 30 min > 15 min) → D does NOT join flexbox ✗
Step 2: Create flexbox for B and C
• Flexbox container at stackLevel 1 (15px from A)
• B gets 50% width (left column)
• C gets 50% width (right column)
Step 3: Process Event D (11:30-11:45)
• D overlaps with B (11:00-12:30)? YES ✓
• D overlaps with C (11:00-12:00)? NO ✗ (D starts at 11:30, C ends at 12:00)
Wait... 11:30 < 12:00 , so they DO overlap !
• D overlaps with ONLY B? Let's check:
- B: 11:00-12:30, D: 11:30-11:45 → overlap ✓
- C: 11:00-12:00, D: 11:30-11:45 → overlap ✓
• Actually D overlaps BOTH! But start time difference (30 min) > threshold
• Decision: Stack D inside the column it overlaps most with (B is longer)
Step 4: Nested stacking inside B's column
• D is placed INSIDE B's flexbox column (position: relative)
• D gets 15px left margin (stacked within the column)
• D appears only in B's half, not spanning both
Result: Flexbox preserved, D clearly nested in B!< / pre >
< / div >
< div style = "margin-top: 20px; padding: 15px; background: #e3f2fd; border-left: 4px solid #2196f3; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Decision Tree: When to Use Nested Stacking< / h3 >
< pre style = "background: white; padding: 15px; border-radius: 4px; font-size: 13px;" >
Analyzing events B (11:00-12:30), C (11:00-12:00), D (11:30-11:45):
Step 1: Check flexbox threshold (±15 min)
├─ B starts 11:00
├─ C starts 11:00 (diff = 0 min ≤ 15 min) → Join flexbox ✓
└─ D starts 11:30 (diff = 30 min > 15 min) → Do NOT join flexbox ✗
Step 2: Create flexbox for B and C
└─ Flexbox container: [B (50%), C (50%)]
Step 3: Process Event D
├─ D starts OUTSIDE threshold (30 min > 15 min)
├─ Check which flexbox columns D overlaps:
│ ├─ D overlaps B? → YES ✓ (11:30-11:45 within 11:00-12:30)
│ └─ D overlaps C? → YES ✓ (11:30-11:45 within 11:00-12:00)
│
└─ D overlaps BOTH B and C
Step 4: Placement strategy
• D cannot join flexbox (start time > threshold)
• D overlaps multiple columns
• Choose primary column: B (longer duration: 1.5h vs 1h)
• Nest D INSIDE B's column with 15px left margin
Result:
• Flexbox maintained for B & C (50%/50%)
• D stacked inside B's column with clear indentation< / pre >
< / div >
< div style = "margin-top: 20px; padding: 15px; background: #fff9c4; border-left: 4px solid #fbc02d; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > 💡 Key Insight: Flexbox Threshold + Nested Stacking< / h3 >
< p >
< strong > The Two-Rule System:< / strong >
< / p >
< ol style = "margin-left: 20px; line-height: 1.8;" >
< li > < strong > Flexbox Rule:< / strong > Events with start times within < code > ±15 minutes< / code > (configurable) share flexbox columns< / li >
< li > < strong > Nested Stacking Rule:< / strong > Events starting OUTSIDE threshold are stacked inside the overlapping flexbox column with 15px left margin< / li >
< / ol >
< p style = "margin-top: 15px;" >
< strong > Why ±15 minutes (not ±30)?< / strong > < br >
A tighter threshold ensures that only events with < em > truly simultaneous< / em > start times share columns.
Events starting 30 minutes later are clearly sequential and should be visually nested/indented.
< / p >
< p style = "margin-top: 10px;" >
< strong > When event overlaps multiple columns:< / strong > < br >
Choose the column with longest duration (or earliest start) as the "primary" parent,
and nest the event there with proper indentation. This maintains the flexbox structure
while showing clear parent-child relationships.
< / p >
< p style = "margin-top: 10px; font-size: 13px; color: #666;" >
< strong > Configuration:< / strong > < code > FLEXBOX_START_THRESHOLD_MINUTES = 15< / code >
< / p >
< / div >
< / div >
<!-- Summary -->
< div class = "section" >
< h2 > Summary: Unified Layout Logic< / h2 >
< div style = "background: #e3f2fd; padding: 25px; border-radius: 4px; margin-bottom: 30px; border-left: 5px solid #2196f3;" >
< h3 style = "margin-top: 0;" > 🎯 The Core Algorithm - One Rule to Rule Them All< / h3 >
< p style = "font-size: 15px; line-height: 1.8;" >
All scenarios follow the < strong > same underlying logic< / strong > :
< / p >
< ol style = "margin-left: 20px; line-height: 2; font-size: 14px;" >
< li > < strong > Group by start time proximity:< / strong > Events starting within ±15 min share a container< / li >
< li > < strong > Container type decision:< / strong >
< ul style = "margin-top: 8px;" >
< li > If group has 1 event → Regular positioning (no special container)< / li >
< li > If group has 2+ events with no mutual overlaps → < strong > Flexbox container< / strong > < / li >
< li > If group has overlapping events → < strong > Regular stacking container< / strong > < / li >
< / ul >
< / li >
< li > < strong > Handle late arrivals:< / strong > Events starting OUTSIDE threshold (> 15 min later) are nested inside the container they overlap with< / li >
< / ol >
< / div >
< div style = "display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; margin-top: 20px;" >
< div style = "background: #ffebee; padding: 20px; border-radius: 4px;" >
< h3 style = "margin-top: 0; color: #c62828;" > Scenario 1-2< / h3 >
< p style = "font-size: 13px; margin-bottom: 8px;" > < strong > Optimized Stacking< / strong > < / p >
< ul style = "margin-left: 20px; font-size: 13px; line-height: 1.6;" >
< li > No flexbox groups< / li >
< li > Events share levels when they don't overlap< / li >
< li > Pure optimization play< / li >
< / ul >
< / div >
< div style = "background: #fff3e0; padding: 20px; border-radius: 4px;" >
< h3 style = "margin-top: 0; color: #e65100;" > Scenario 5< / h3 >
< p style = "font-size: 13px; margin-bottom: 8px;" > < strong > Flexbox Columns< / strong > < / p >
< ul style = "margin-left: 20px; font-size: 13px; line-height: 1.6;" >
< li > B & C start together (±15 min)< / li >
< li > They don't overlap each other< / li >
< li > Perfect for flexbox (50%/50%)< / li >
< / ul >
< / div >
< div style = "background: #e8f5e9; padding: 20px; border-radius: 4px;" >
< h3 style = "margin-top: 0; color: #2e7d32;" > Scenario 6< / h3 >
< p style = "font-size: 13px; margin-bottom: 8px;" > < strong > Nested in Flexbox< / strong > < / p >
< ul style = "margin-left: 20px; font-size: 13px; line-height: 1.6;" >
< li > B & C flexbox maintained< / li >
< li > D starts later (> 15 min)< / li >
< li > D nested in B's column< / li >
< / ul >
< / div >
< / div >
< div style = "margin-top: 30px; padding: 20px; background: #e3f2fd; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Unified Algorithm - All Scenarios Use This< / h3 >
< pre style = "background: white; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 13px; line-height: 1.6;" >
const FLEXBOX_START_THRESHOLD_MINUTES = 15;
const STACK_OFFSET_PX = 15;
// PHASE 1: Group events by start time proximity
function groupEventsByStartTime(events) {
const groups = [];
const sorted = events.sort((a, b) => a.start - b.start);
for (const event of sorted) {
const existingGroup = groups.find(g => {
const groupStart = g[0].start;
const diffMinutes = Math.abs(event.start - groupStart) / (1000 * 60);
return diffMinutes < = FLEXBOX_START_THRESHOLD_MINUTES;
});
if (existingGroup) {
existingGroup.push(event);
} else {
groups.push([event]);
}
}
return groups; // Each group = events starting within ±15 min
}
// PHASE 2: Decide container type for each group
function decideContainerType(group) {
if (group.length === 1) return 'NONE';
// Check if any events in group overlap each other
const hasOverlaps = group.some((e1, i) =>
group.slice(i + 1).some(e2 => doEventsOverlap(e1, e2))
);
return hasOverlaps ? 'STACKING' : 'FLEXBOX';
}
// PHASE 3: Handle events that start OUTSIDE threshold
function nestLateArrivals(groups, allEvents) {
for (const event of allEvents) {
const belongsToGroup = groups.some(g => g.includes(event));
if (belongsToGroup) continue; // Already placed
// Find which group/container this event overlaps with
const overlappingGroup = groups.find(g =>
g.some(e => doEventsOverlap(event, e))
);
if (overlappingGroup) {
// Nest inside the overlapping container
// If flexbox: choose column with longest duration
// If stacking: add to stack with proper offset
nestEventInContainer(event, overlappingGroup);
}
}
}
Result: One algorithm handles ALL scenarios!< / pre >
< / div >
< div style = "margin-top: 30px; padding: 20px; background: #f5f5f5; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > Algorithm Complexity< / h3 >
< ul style = "margin-left: 20px;" >
< li > < strong > Overlap Detection:< / strong > O(n²) where n = number of events< / li >
< li > < strong > Grouping by Start Time:< / strong > O(n log n) for sorting< / li >
< li > < strong > Stack Assignment:< / strong > O(n²) for checking all overlaps< / li >
< li > < strong > Visual Update:< / strong > O(n) to apply styling< / li >
< / ul >
< p style = "margin-top: 10px;" >
< strong > Total:< / strong > O(n²) - Same as naive approach, but with much better UX!
< / p >
< / div >
< div style = "margin-top: 30px; padding: 20px; background: #e8f5e9; border-left: 4px solid #4caf50; border-radius: 4px;" >
< h3 style = "margin-top: 0;" > 🎯 Key Insight: The Pattern That Connects Everything< / h3 >
< p style = "line-height: 1.8; margin-bottom: 15px;" >
The < strong > same 3-phase algorithm< / strong > handles all scenarios:
< / p >
< table style = "width: 100%; border-collapse: collapse; font-size: 13px;" >
< thead >
< tr style = "background: #f5f5f5;" >
< th style = "padding: 10px; text-align: left; border: 1px solid #ddd;" > Phase< / th >
< th style = "padding: 10px; text-align: left; border: 1px solid #ddd;" > Logic< / th >
< th style = "padding: 10px; text-align: left; border: 1px solid #ddd;" > Scenario 1-4< / th >
< th style = "padding: 10px; text-align: left; border: 1px solid #ddd;" > Scenario 5< / th >
< th style = "padding: 10px; text-align: left; border: 1px solid #ddd;" > Scenario 6< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td style = "padding: 10px; border: 1px solid #ddd;" > < strong > 1. Group< / strong > < / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > Start time ±15 min< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > No groups (all separate)< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > [B, C] group< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > [B, C] group, D separate< / td >
< / tr >
< tr >
< td style = "padding: 10px; border: 1px solid #ddd;" > < strong > 2. Container< / strong > < / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > Overlaps? Stack : Flex< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > N/A (single events)< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > Flexbox (no overlaps)< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > Flexbox (no overlaps)< / td >
< / tr >
< tr >
< td style = "padding: 10px; border: 1px solid #ddd;" > < strong > 3. Late arrivals< / strong > < / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > Nest in overlapping container< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > N/A< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > N/A< / td >
< td style = "padding: 10px; border: 1px solid #ddd;" > D nested in B's column< / td >
< / tr >
< / tbody >
< / table >
< p style = "margin-top: 20px; padding: 15px; background: white; border-radius: 4px; font-size: 14px;" >
< strong > Conclusion:< / strong > The difference between scenarios is NOT different algorithms,
but rather < strong > different inputs< / strong > to the same algorithm. The 3 phases always run in order,
and each phase makes decisions based on the data (start times, overlaps, thresholds).
< / p >
< / div >
< / div >
2025-10-06 17:05:18 +02:00
<!-- Scenario 8: Edge Case - Exactly 15 min apart with overlap -->
< div class = "section" >
< h2 > Scenario 8: Edge Case - Events Starting Exactly 15 Minutes Apart (WITH Overlap)< / h2 >
< p > < strong > Edge Case:< / strong > What happens when events start exactly at the ±15 min threshold AND overlap?< / p >
< p > < strong > Events:< / strong > < / p >
< ul style = "margin: 10px 0 20px 20px;" >
< li > Event A: 11:00 - 12:00 (1 hour)< / li >
< li > Event B: 11:15 - 12:30 (1.25 hours)< / li >
< / ul >
< div class = "note" >
< strong > Analysis:< / strong > < br >
• A starts at 11:00< br >
• B starts at 11:15 (diff = 15 min ≤ 15 min) → < strong > Within threshold< / strong > ✓< br >
• A and B overlap (11:15 - 12:00) → < strong > They DO overlap< / strong > ✓< br >
• < strong > Visual priority:< / strong > Show that they start simultaneously (±15 min)< br >
• < strong > Result:< / strong > Use GRID (column sharing) even though they overlap
< / div >
< div class = "comparison" >
<!-- Wrong: Stacking -->
< div class = "calendar-column" >
< div class = "column-title" > ❌ Wrong: Stacking (Hides Simultaneity)< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 11:00< / div >
< div class = "time-marker" style = "top: 33%" > 11:30< / div >
< div class = "time-marker" style = "top: 66%" > 12:00< / div >
< div class = "time-marker" style = "top: 100%" > 12:30< / div >
< / div >
< div class = "events-container" style = "height: 180px;" >
<!-- Event A: 11:00 - 12:00 (60 min = 66.7% of 90 min total) -->
< div class = "event event-a" style = "top: 0%; height: 66.7%; left: 2px; right: 2px; z-index: 100;" >
Event A< br >
< span style = "font-size: 11px; opacity: 0.8;" > 11:00-12:00< / span >
< / div >
<!-- Event B: 11:15 - 12:30 (75 min = 83.3% of 90 min total, starts at 16.7%) -->
< div class = "event event-b" style = "top: 16.7%; height: 83.3%; left: 17px; right: 2px; z-index: 101; margin-left: 15px;" >
Event B< br >
< span style = "font-size: 11px; opacity: 0.8;" > 11:15-12:30< / span >
< / div >
< / div >
< div class = "savings wrong" >
< strong > Problems:< / strong > < br >
• B is offset to the right → looks like it happens AFTER A< br >
• Doesn't convey that they start almost simultaneously (15 min apart)< br >
• Wastes horizontal space
< / div >
< / div >
<!-- Right: GRID -->
< div class = "calendar-column" >
< div class = "column-title" > ✅ Correct: GRID Column Sharing< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%" > 11:00< / div >
< div class = "time-marker" style = "top: 33%" > 11:30< / div >
< div class = "time-marker" style = "top: 66%" > 12:00< / div >
< div class = "time-marker" style = "top: 100%" > 12:30< / div >
< / div >
< div class = "events-container" style = "height: 180px;" >
<!-- Grid container for A and B -->
< div style = "position: absolute; top: 0%; left: 2px; right: 2px; display: grid; grid-template-columns: 1fr 1fr; gap: 2px; z-index: 100;" >
<!-- Event A: 11:00 - 12:00 -->
< div class = "event event-a" style = "position: relative; height: 120px;" >
Event A< br >
< span style = "font-size: 11px; opacity: 0.8;" > 11:00-12:00< / span >
< / div >
<!-- Event B: 11:15 - 12:30 (starts 15 min later = 10% offset) -->
< div class = "event event-b" style = "position: relative; height: 150px; top: 10%;" >
Event B< br >
< span style = "font-size: 11px; opacity: 0.8;" > 11:15-12:30< / span >
< / div >
< / div >
< / div >
< div class = "savings" >
< strong > Benefits:< / strong > < br >
• Side-by-side layout shows they're concurrent< br >
• Each event gets 50% width< br >
• Clear visual: these events start nearly simultaneously (±15 min)< br >
• Despite overlapping, simultaneity is visual priority
< / div >
< / div >
< / div >
< div class = "note" >
< strong > Key Rule:< / strong > Events starting within ±15 minutes should ALWAYS use GRID (column sharing),
even if they overlap. The visual priority is to show that events start < em > simultaneously< / em > ,
not to avoid overlap. Overlap is handled by the grid container having appropriate height.
< / div >
< div class = "code-example" >
< strong > Expected Behavior:< / strong >
< pre style = "background: #f8f8f8; padding: 15px; border-radius: 4px; overflow-x: auto; font-size: 13px;" >
// Phase 1: Group by start time
groupEventsByStartTime([A, B])
→ Group 1: [A, B] // 15 min apart ≤ threshold
// Phase 2: Decide container type
decideContainerType(Group 1)
→ GRID // Always GRID for grouped events, even if overlapping
// Phase 3: Calculate stack level
calculateGridGroupStackLevel(Group 1)
→ stackLevel: 0 // No other events to stack above
// Result:
< swp-event-group class="cols-2 stack-level-0" style="top: 0px; margin-left: 0px; z-index: 100;">
< swp-event data-event-id="A" style="height: 120px;"> Event A< /swp-event>
< swp-event data-event-id="B" style="height: 150px; top: 10%;"> Event B< /swp-event>
< /swp-event-group> < / pre >
< / div >
< / div >
<!-- ============================================ -->
<!-- SCENARIO 9: Grid with Staggered Start Times -->
<!-- ============================================ -->
< div class = "section" >
< h2 > Scenario 9: Grid with Staggered Start Times< / h2 >
< div class = "legend" >
< div class = "legend-item" > < strong > Event A:< / strong > 09:00 - 10:00 (1 hour)< / div >
< div class = "legend-item" > < strong > Event B:< / strong > 09:30 - 10:30 (1 hour, starts 30 min after A)< / div >
< div class = "legend-item" > < strong > Event C:< / strong > 10:15 - 12:00 (1h 45min, starts 45 min after B)< / div >
< / div >
< div class = "note" >
< strong > Special Case: End-to-Start Conflicts Create Shared Columns< / strong > < br > < br >
• Event A: 09:00 - 10:00< br >
• Event B: 09:30 - 10:30 (starts 30 min before A ends → conflicts with A)< br >
• Event C: 10:15 - 12:00 (starts 15 min before B ends → conflicts with B)< br > < br >
< strong > Key Rule:< / strong > Events share columns (GRID) when they conflict within threshold< br >
• Conflict = Event starts within ±threshold minutes of another event's end time< br >
• A and B: B starts 30 min before A ends → conflict (≤ 30 min threshold)< br >
• B and C: C starts 15 min before B ends → conflict (≤ 30 min threshold)< br >
• Therefore: A, B, and C all share columns in a 3-column GRID< br > < br >
< strong > With threshold = 15 min:< / strong > Only A-B conflict (30 min > 15), C is separate → Stack< br >
< strong > With threshold = 30 min:< / strong > Both A-B and B-C conflict → All 3 share columns in GRID
< / div >
< div class = "comparison" >
<!-- Threshold = 15 min -->
< div class = "calendar-column" >
< div class = "column-title" > With Threshold = 15 min< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%;" > 09:00< / div >
< div class = "time-marker" style = "top: 25%;" > 10:00< / div >
< div class = "time-marker" style = "top: 50%;" > 11:00< / div >
< div class = "time-marker" style = "top: 75%;" > 12:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: 09:00 - 10:00 (stackLevel 0) -->
< div class = "event event-a" style = "top: 0px; left: 2px; right: 2px; height: 60px; margin-left: 0px; z-index: 100;" >
Event A< br > 09:00-10:00
< / div >
<!-- Event B: 09:30 - 10:30 (stackLevel 1, overlaps A) -->
< div class = "event event-b" style = "top: 30px; left: 2px; right: 2px; height: 60px; margin-left: 15px; z-index: 101;" >
Event B< br > 09:30-10:30
< / div >
<!-- Event C: 10:15 - 12:00 (stackLevel 2, overlaps B) -->
< div class = "event event-c" style = "top: 75px; left: 2px; right: 2px; height: 105px; margin-left: 30px; z-index: 102;" >
Event C< br > 10:15-12:00
< / div >
< / div >
< / div >
<!-- Threshold = 30 min -->
< div class = "calendar-column" >
< div class = "column-title" > With Threshold = 30 min< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%;" > 09:00< / div >
< div class = "time-marker" style = "top: 25%;" > 10:00< / div >
< div class = "time-marker" style = "top: 50%;" > 11:00< / div >
< div class = "time-marker" style = "top: 75%;" > 12:00< / div >
< / div >
< div class = "events-container" >
<!-- Grid Group for A, B & C: stackLevel 0 (2 columns, A and C share column 1) -->
< div style = "position: absolute; top: 0px; left: 2px; right: 2px; margin-left: 0px; z-index: 100; display: grid; grid-template-columns: 1fr 1fr; gap: 2px;" >
<!-- Column 1: Event A and C (no overlap) -->
< div style = "position: relative;" >
< div class = "event event-a" style = "position: absolute; top: 0px; height: 60px; left: 0; right: 0;" >
Event A< br > 09:00-10:00
< / div >
< div class = "event event-c" style = "position: absolute; top: 75px; height: 105px; left: 0; right: 0;" >
Event C< br > 10:15-12:00
< / div >
< / div >
<!-- Column 2: Event B -->
< div style = "position: relative;" >
< div class = "event event-b" style = "position: absolute; top: 30px; height: 60px; left: 0; right: 0;" >
Event B< br > 09:30-10:30
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< h3 > Stack Analysis< / h3 >
< div class = "legend" >
< p > < strong > Threshold = 15 min (Stack):< / strong > < / p >
< ul >
< li > Event A: stackLevel 0< / li >
< li > Event B: stackLevel 1 (starts 30 min before A ends, but 30 > 15 threshold) → Stack with margin-left: 15px< / li >
< li > Event C: stackLevel 2 (starts 15 min before B ends, but separate from A-B stack) → Stack with margin-left: 30px< / li >
< / ul >
< p > < strong > Threshold = 30 min (Shared GRID with 2 columns):< / strong > < / p >
< ul >
< li > Grid Group (A, B & C): 2-column grid layout< / li >
< li > < strong > Column 1:< / strong > Event A (09:00-10:00) + Event C (10:15-12:00) - they don't overlap!< / li >
< li > < strong > Column 2:< / strong > Event B (09:30-10:30) - overlaps both A and C< / li >
< li > Event A: grid column 1, top: 0px< / li >
< li > Event B: grid column 2, top: 30px< / li >
< li > Event C: grid column 1, top: 75px (shares column with A, no overlap)< / li >
< li > All events: stackLevel 0, margin-left: 0px (no stacking, all in same grid container)< / li >
< / ul >
< / div >
< / div >
<!-- ============================================ -->
<!-- SCENARIO 10: Complex Column Sharing with Multiple Events -->
<!-- ============================================ -->
< div class = "section" >
< h2 > Scenario 10: Complex Column Sharing< / h2 >
< div class = "legend" >
< div class = "legend-item" > < strong > Event A:< / strong > 12:00 - 15:00 (3 hours)< / div >
< div class = "legend-item" > < strong > Event B:< / strong > 12:30 - 13:00 (30 min, starts 30 min after A)< / div >
< div class = "legend-item" > < strong > Event C:< / strong > 13:30 - 14:30 (1 hour, starts 30 min after B ends)< / div >
< div class = "legend-item" > < strong > Event D:< / strong > 14:00 - 15:00 (1 hour, starts 30 min before C ends)< / div >
< div class = "legend-item" > < strong > Event E:< / strong > 14:00 - 15:00 (1 hour, starts same time as D)< / div >
< / div >
< div class = "note" >
< strong > Analysis with threshold = 30 min:< / strong > < br >
• A-B conflict: B starts 30 min after A (≤ 30) → grouped< br >
• B-C conflict: C starts 30 min after B ends (≤ 30) → grouped with A-B< br >
• C-D conflict: D starts 30 min before C ends (≤ 30) → grouped with A-B-C< br >
• D-E conflict: D and E start at same time (0 min) → grouped with all< br >
• Therefore: All 5 events in ONE grid group< br > < br >
< strong > Column allocation:< / strong > < br >
• A overlaps: B, C, D, E → needs own column< br >
• B overlaps: A → needs own column< br >
• C overlaps: A, D, E → needs own column< br >
• D overlaps: A, C, E → needs own column< br >
• E overlaps: A, C, D → can share column with B (they don't overlap)< br >
• Result: 4 columns needed
< / div >
< div class = "comparison" >
<!-- Threshold = 15 min -->
< div class = "calendar-column" >
< div class = "column-title" > With Threshold = 15 min< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%;" > 12:00< / div >
< div class = "time-marker" style = "top: 33%;" > 13:00< / div >
< div class = "time-marker" style = "top: 67%;" > 14:00< / div >
< div class = "time-marker" style = "top: 100%;" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Event A: stackLevel 0 -->
< div class = "event event-a" style = "position: absolute; top: 0px; left: 2px; right: 2px; height: 180px; margin-left: 0px; z-index: 100;" >
Event A< br > 12:00-15:00
< / div >
<!-- Event B: stackLevel 1 (overlaps A) -->
< div class = "event event-b" style = "position: absolute; top: 30px; left: 2px; right: 2px; height: 30px; margin-left: 15px; z-index: 101;" >
Event B< br > 12:30-13:00
< / div >
<!-- Event C: stackLevel 2 (overlaps A, not B) -->
< div class = "event event-c" style = "position: absolute; top: 90px; left: 2px; right: 2px; height: 60px; margin-left: 30px; z-index: 102;" >
Event C< br > 13:30-14:30
< / div >
<!-- Grid Group for D & E: stackLevel 3 (start simultaneously, overlap A and C) -->
< div style = "position: absolute; top: 120px; left: 2px; right: 2px; margin-left: 45px; z-index: 103; display: grid; grid-template-columns: 1fr 1fr; gap: 2px;" >
< div style = "position: relative;" >
< div class = "event event-d" style = "position: absolute; top: 0px; height: 60px; left: 0; right: 0;" >
Event D< br > 14:00-15:00
< / div >
< / div >
< div style = "position: relative;" >
< div class = "event event-d" style = "position: absolute; top: 0px; height: 60px; left: 0; right: 0;" >
Event E< br > 14:00-15:00
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Threshold = 30 min -->
< div class = "calendar-column" >
< div class = "column-title" > With Threshold = 30 min< / div >
< div class = "timeline" >
< div class = "time-marker" style = "top: 0%;" > 12:00< / div >
< div class = "time-marker" style = "top: 33%;" > 13:00< / div >
< div class = "time-marker" style = "top: 67%;" > 14:00< / div >
< div class = "time-marker" style = "top: 100%;" > 15:00< / div >
< / div >
< div class = "events-container" >
<!-- Grid Group: All 5 events (4 columns) -->
< div style = "position: absolute; top: 0px; left: 2px; right: 2px; margin-left: 0px; z-index: 100; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 2px;" >
<!-- Column 1: Event A -->
< div style = "position: relative;" >
< div class = "event event-a" style = "position: absolute; top: 0px; height: 180px; left: 0; right: 0;" >
Event A< br > 12:00-15:00
< / div >
< / div >
<!-- Column 2: Event B + E (don't overlap) -->
< div style = "position: relative;" >
< div class = "event event-b" style = "position: absolute; top: 30px; height: 30px; left: 0; right: 0;" >
Event B< br > 12:30-13:00
< / div >
< div class = "event event-d" style = "position: absolute; top: 120px; height: 60px; left: 0; right: 0;" >
Event E< br > 14:00-15:00
< / div >
< / div >
<!-- Column 3: Event C -->
< div style = "position: relative;" >
< div class = "event event-c" style = "position: absolute; top: 90px; height: 60px; left: 0; right: 0;" >
Event C< br > 13:30-14:30
< / div >
< / div >
<!-- Column 4: Event D -->
< div style = "position: relative;" >
< div class = "event event-d" style = "position: absolute; top: 120px; height: 60px; left: 0; right: 0;" >
Event D< br > 14:00-15:00
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< h3 > Expected Layout< / h3 >
< div class = "legend" >
< p > < strong > Threshold = 15 min (Stack + Small Grid):< / strong > < / p >
< ul >
< li > Event A: stackLevel 0< / li >
< li > Event B: stackLevel 1 (overlaps A, 30 min > 15 threshold) → margin-left: 15px< / li >
< li > Event C: stackLevel 2 (overlaps A, 30 min > 15 threshold) → margin-left: 30px< / li >
< li > Grid Group (D & E): stackLevel 3 (start simultaneously) → margin-left: 45px
< ul >
< li > 2-column grid: D in column 1, E in column 2< / li >
< / ul >
< / li >
< / ul >
< p > < strong > Threshold = 30 min (Large Grid):< / strong > < / p >
< ul >
< li > Grid Group (A, B, C, D, E): All in ONE grid group
< ul >
< li > < strong > Column 1:< / strong > Event A (top: 0px, height: 180px)< / li >
< li > < strong > Column 2:< / strong > Event B (top: 30px) + Event E (top: 120px)< / li >
< li > < strong > Column 3:< / strong > Event C (top: 90px)< / li >
< li > < strong > Column 4:< / strong > Event D (top: 120px)< / li >
< / ul >
< / li >
< / ul >
< p > < strong > Key Points:< / strong > < / p >
< ul >
< li > With threshold = 30: All events grouped due to chained end-to-start conflicts< / li >
< li > With threshold = 15: Only D & E grouped (start simultaneously), A/B/C stacked separately< / li >
< li > B and E can share column 2 (they don't overlap: B ends 13:00, E starts 14:00)< / li >
< li > D and E start at same time but need separate columns (they overlap perfectly)< / li >
< li > Result with 30 min: 4 columns instead of 5 (optimization saves 1 column)< / li >
< / ul >
< / div >
< / div >
2025-10-05 23:54:50 +02:00
< footer style = "text-align: center; color: #999; margin-top: 40px; padding: 20px;" >
< p > Event Stacking Visualization - Calendar Plantempus< / p >
< p style = "font-size: 12px;" > Static documentation for event stacking concepts< / p >
< / footer >
< / body >
< / html >