Stacking and Sharecolumn WIP

This commit is contained in:
Janus C. H. Knudsen 2025-10-06 17:05:18 +02:00
parent c788a1695e
commit 6b8c5d4673
7 changed files with 763 additions and 51 deletions

View file

@ -1415,6 +1415,397 @@ Result: One algorithm handles ALL scenarios!</pre>
</div>
</div>
<!-- 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:
&lt;swp-event-group class="cols-2 stack-level-0" style="top: 0px; margin-left: 0px; z-index: 100;"&gt;
&lt;swp-event data-event-id="A" style="height: 120px;"&gt;Event A&lt;/swp-event&gt;
&lt;swp-event data-event-id="B" style="height: 150px; top: 10%;"&gt;Event B&lt;/swp-event&gt;
&lt;/swp-event-group&gt;</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>
<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>