# Stack Binding System - Calendar Plantempus ## Oversigt Dette dokument beskriver hvordan overlappende events er bundet sammen i Calendar Plantempus systemet, specifikt hvordan 2 eller flere events der ligger oven i hinanden (stacked) er forbundet. ## Stack Binding Mekanisme ### SimpleEventOverlapManager Systemet bruger `SimpleEventOverlapManager` til at håndtere event overlap og stacking. Denne implementation bruger **data-attributes** på DOM elementerne til at holde styr på stack chains. ### Hvordan Stacked Events er Bundet Sammen Når 2 eller flere events ligger oven i hinanden, oprettes en **linked list struktur** via `data-stack-link` attributter: #### Eksempel med 2 Events: ```typescript // Event A (base event): // Event B (stacked event): ``` #### Eksempel med 3 Events: ```typescript // Event A (base event): // Event B (middle event): // Event C (top event): ``` ### StackLink Interface ```typescript interface StackLink { prev?: string; // Event ID af forrige event i stacken next?: string; // Event ID af næste event i stacken stackLevel: number; // 0 = base event, 1 = første stacked, etc } ``` ### Visual Styling Hvert stacked event får automatisk styling baseret på deres `stackLevel`: - **Event A (base)**: `margin-left: 0px`, `z-index: 100` - **Event B (middle)**: `margin-left: 15px`, `z-index: 101` - **Event C (top)**: `margin-left: 30px`, `z-index: 102` **Formel:** - `margin-left = stackLevel * 15px` - `z-index = 100 + stackLevel` ### Stack Chain Navigation Systemet kan traversere stack chains i begge retninger: ```typescript // Find næste event i stacken const link = getStackLink(currentElement); if (link?.next) { const nextElement = document.querySelector(`swp-event[data-event-id="${link.next}"]`); } // Find forrige event i stacken if (link?.prev) { const prevElement = document.querySelector(`swp-event[data-event-id="${link.prev}"]`); } ``` ### Automatisk Chain Opdatering Når events fjernes fra en stack, opdateres chain automatisk: 1. **Middle element fjernet**: Prev og next events linkes direkte sammen 2. **Chain breaking**: Hvis events ikke længere overlapper, brydes chain 3. **Stack level opdatering**: Alle efterfølgende events får opdateret stackLevel ### Overlap Detection Events klassificeres som **stacking** hvis: - De overlapper i tid OG - Start tid forskel > 30 minutter ```typescript const timeDiffMinutes = Math.abs( new Date(event1.start).getTime() - new Date(event2.start).getTime() ) / (1000 * 60); return timeDiffMinutes > 30 ? OverlapType.STACKING : OverlapType.COLUMN_SHARING; ``` ## Eksempler ### 2 Events Stack ``` Event A: 09:00-11:00 (base) → margin-left: 0px, z-index: 100 Event B: 09:45-10:30 (stacked) → margin-left: 15px, z-index: 101 ``` **Stack chain:** ``` A ←→ B ``` **Data attributes:** - A: `{"stackLevel":0,"next":"B"}` - B: `{"prev":"A","stackLevel":1}` ### 3 Events Stack ``` Event A: 09:00-11:00 (base) → margin-left: 0px, z-index: 100 Event B: 09:45-10:30 (middle) → margin-left: 15px, z-index: 101 Event C: 10:15-11:15 (top) → margin-left: 30px, z-index: 102 ``` **Stack chain:** ``` A ←→ B ←→ C ``` **Data attributes:** - A: `{"stackLevel":0,"next":"B"}` - B: `{"prev":"A","next":"C","stackLevel":1}` - C: `{"prev":"B","stackLevel":2}` ## Visual Stack Chain Diagram ```mermaid graph TD A[Event A - Base Event] --> A1[data-stack-link] B[Event B - Middle Event] --> B1[data-stack-link] C[Event C - Top Event] --> C1[data-stack-link] A1 --> A2[stackLevel: 0
next: event-B] B1 --> B2[prev: event-A
next: event-C
stackLevel: 1] C1 --> C2[prev: event-B
stackLevel: 2] A2 --> A3[margin-left: 0px
z-index: 100] B2 --> B3[margin-left: 15px
z-index: 101] C2 --> C3[margin-left: 30px
z-index: 102] subgraph Stack Chain Navigation A4[Event A] -.->|next| B4[Event B] B4 -.->|next| C4[Event C] C4 -.->|prev| B4 B4 -.->|prev| A4 end subgraph Visual Result V1[Event A - Full Width] V2[Event B - 15px Offset] V3[Event C - 30px Offset] V1 -.-> V2 V2 -.-> V3 end ``` ## Stack Chain Operations ```mermaid sequenceDiagram participant DOM as DOM Element participant SM as SimpleEventOverlapManager participant Chain as Stack Chain Note over DOM,Chain: Creating Stack Chain DOM->>SM: createStackedEvent(eventB, eventA, 1) SM->>Chain: Set eventA: {stackLevel:0, next:"eventB"} SM->>Chain: Set eventB: {prev:"eventA", stackLevel:1} SM->>DOM: Apply margin-left: 15px, z-index: 101 Note over DOM,Chain: Removing from Chain DOM->>SM: removeStackedStyling(eventB) SM->>Chain: Get eventB links SM->>Chain: Link eventA -> eventC directly SM->>Chain: Update eventC stackLevel: 1 SM->>DOM: Update eventC margin-left: 15px SM->>Chain: Delete eventB entry ``` ## Fordele ved Data-Attribute Approach 1. **Ingen global state** - alt information er på DOM elementerne 2. **Persistent** - overlever DOM manipulationer 3. **Debuggable** - kan inspiceres i browser dev tools 4. **Performant** - ingen in-memory maps at vedligeholde 5. **Robust** - automatisk cleanup når elements fjernes ## Se Også - [`SimpleEventOverlapManager.ts`](../src/managers/SimpleEventOverlapManager.ts) - Implementation - [`EventRenderer.ts`](../src/renderers/EventRenderer.ts) - Usage - [Event Overlap CSS](../wwwroot/css/calendar-events-css.css) - Styling - [Complexity Comparison](../complexity_comparison.md) - Before/after analysis