This commit is contained in:
Janus Knudsen 2025-09-09 14:35:21 +02:00
parent 727a6ec53a
commit 72019a3d9a
15 changed files with 1056 additions and 1230 deletions

View file

@ -0,0 +1,204 @@
# 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):
<swp-event data-stack-link='{"stackLevel":0,"next":"event-B"}'>
// Event B (stacked event):
<swp-event data-stack-link='{"prev":"event-A","stackLevel":1}'>
```
#### Eksempel med 3 Events:
```typescript
// Event A (base event):
<swp-event data-stack-link='{"stackLevel":0,"next":"event-B"}'>
// Event B (middle event):
<swp-event data-stack-link='{"prev":"event-A","next":"event-C","stackLevel":1}'>
// Event C (top event):
<swp-event data-stack-link='{"prev":"event-B","stackLevel":2}'>
```
### 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<br/>next: event-B]
B1 --> B2[prev: event-A<br/>next: event-C<br/>stackLevel: 1]
C1 --> C2[prev: event-B<br/>stackLevel: 2]
A2 --> A3[margin-left: 0px<br/>z-index: 100]
B2 --> B3[margin-left: 15px<br/>z-index: 101]
C2 --> C3[margin-left: 30px<br/>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