Calendar/data_attribute_solution.md

221 lines
6.6 KiB
Markdown
Raw Normal View History

# Data-Attribute Stack Tracking Solution
## Implementation Summary
Vi har nu implementeret stack tracking via data attributes i stedet for komplekse Map-baserede linked lists.
### 🎯 **How it works:**
#### **Stack Links via Data Attributes**
```html
<!-- Base event -->
<swp-event
data-event-id="event_123"
data-stack-link='{"stackLevel":0,"next":"event_456"}'
style="margin-left: 0px;">
</swp-event>
<!-- Stacked event -->
<swp-event
data-event-id="event_456"
data-stack-link='{"prev":"event_123","stackLevel":1,"next":"event_789"}'
style="margin-left: 15px;">
</swp-event>
<!-- Top stacked event -->
<swp-event
data-event-id="event_789"
data-stack-link='{"prev":"event_456","stackLevel":2}'
style="margin-left: 30px;">
</swp-event>
```
### 🔧 **Key Methods:**
#### **createStackedEvent()**
```typescript
// Links new event to end of chain
let lastElement = underlyingElement;
while (lastLink?.next) {
lastElement = this.findElementById(lastLink.next);
lastLink = this.getStackLink(lastElement);
}
// Create bidirectional link
this.setStackLink(lastElement, { ...lastLink, next: eventId });
this.setStackLink(eventElement, { prev: lastElementId, stackLevel });
```
#### **removeStackedStyling()**
```typescript
// Re-link prev and next
if (link.prev && link.next) {
this.setStackLink(prevElement, { ...prevLink, next: link.next });
this.setStackLink(nextElement, { ...nextLink, prev: link.prev });
}
// Update subsequent stack levels
this.updateSubsequentStackLevels(link.next, -1);
```
#### **restackEventsInContainer()**
```typescript
// Group by stack chains (not all stacked events together!)
for (const element of stackedEvents) {
// Find root of chain
while (rootLink?.prev) {
rootElement = this.findElementById(rootLink.prev);
}
// Collect entire chain
// Re-stack each chain separately
}
```
---
## 🏆 **Advantages vs Map Solution:**
### ✅ **Simplified State Management**
| Aspect | Map + Linked List | Data Attributes |
|--------|------------------|-----------------|
| **State Location** | Separate Map object | In DOM elements |
| **Synchronization** | Manual sync required | Automatic with DOM |
| **Memory Cleanup** | Manual Map cleanup | Automatic with element removal |
| **Debugging** | Console logs only | DevTools inspection |
| **State Consistency** | Possible sync bugs | Always consistent |
### ✅ **Code Complexity Reduction**
```typescript
// OLD: Complex Map management
private stackChains = new Map<string, { next?: string, prev?: string, stackLevel: number }>();
// Find last event in chain - complex iteration
let lastEventId = underlyingId;
while (this.stackChains.has(lastEventId) && this.stackChains.get(lastEventId)?.next) {
lastEventId = this.stackChains.get(lastEventId)!.next!;
}
// Link events - error prone
this.stackChains.get(lastEventId)!.next = eventId;
this.stackChains.set(eventId, { prev: lastEventId, stackLevel });
// NEW: Simple data attribute management
let lastElement = underlyingElement;
while (lastLink?.next) {
lastElement = this.findElementById(lastLink.next);
}
this.setStackLink(lastElement, { ...lastLink, next: eventId });
this.setStackLink(eventElement, { prev: lastElementId, stackLevel });
```
### ✅ **Better Error Handling**
```typescript
// DOM elements can't get out of sync with their own attributes
// When element is removed, its state automatically disappears
// No orphaned Map entries
```
---
## 🧪 **Test Scenarios:**
### **Scenario 1: Multiple Separate Stacks**
```
Column has:
Stack A: Event1 → Event2 → Event3 (times: 09:00-10:00, 09:15-10:15, 09:30-10:30)
Stack B: Event4 → Event5 (times: 14:00-15:00, 14:10-15:10)
Remove Event2 (middle of Stack A):
✅ Expected: Event1 → Event3 (Event3 moves to 15px margin)
✅ Expected: Stack B unchanged (Event4→Event5 still at 0px→15px)
❌ Old naive approach: Would group all events together
```
### **Scenario 2: Remove Base Event**
```
Stack: EventA(base) → EventB → EventC
Remove EventA:
✅ Expected: EventB becomes base (0px), EventC moves to 15px
✅ Data-attribute solution: EventB.stackLevel = 0, EventC.stackLevel = 1
```
### **Scenario 3: Drag and Drop**
```
Drag Event2 from Stack A to new position:
✅ removeStackedStyling() handles re-linking
✅ Other stack events maintain their relationships
✅ No Map synchronization issues
```
---
## 🔍 **Debugging Benefits:**
### **Browser DevTools Inspection:**
```html
<!-- Easy to see stack relationships directly in HTML -->
<swp-event data-stack-link='{"prev":"123","next":"789","stackLevel":1}'>
<!-- Event content -->
</swp-event>
```
### **Console Debugging:**
```javascript
// Easy to inspect stack chains
const element = document.querySelector('[data-event-id="456"]');
const link = JSON.parse(element.dataset.stackLink);
console.log('Stack chain:', link);
```
---
## 📊 **Performance Comparison:**
| Operation | Map Solution | Data-Attribute Solution |
|-----------|--------------|-------------------------|
| **Create Stack** | Map.set() + element.style | JSON.stringify() + element.style |
| **Remove Stack** | Map manipulation + DOM queries | JSON.parse/stringify + DOM queries |
| **Find Chain** | Map iteration | DOM traversal |
| **Memory Usage** | Map + DOM | DOM only |
| **Sync Overhead** | High (keep Map in sync) | None (DOM is source) |
### **Performance Notes:**
- **JSON.parse/stringify**: Very fast for small objects (~10 properties max)
- **DOM traversal**: Limited by chain length (typically 2-5 events)
- **Memory**: Significant reduction (no separate Map)
- **Garbage collection**: Better (automatic cleanup)
---
## ✅ **Solution Status:**
### **Completed:**
- [x] StackLink interface definition
- [x] Helper methods (getStackLink, setStackLink, findElementById)
- [x] createStackedEvent with data-attribute linking
- [x] removeStackedStyling with proper re-linking
- [x] restackEventsInContainer respects separate chains
- [x] isStackedEvent checks both style and data-attributes
- [x] Compilation successful
### **Ready for Testing:**
- [ ] Manual UI testing of stack behavior
- [ ] Drag and drop stacked events
- [ ] Multiple stacks in same column
- [ ] Edge cases (remove first/middle/last)
---
## 🎉 **Conclusion:**
This data-attribute solution provides:
1. **Same functionality** as the Map-based approach
2. **Simpler implementation** (DOM as single source of truth)
3. **Better debugging experience** (DevTools visibility)
4. **Automatic memory management** (no manual cleanup)
5. **No synchronization bugs** (state follows element)
The solution maintains all the precision of the original complex system while dramatically simplifying the implementation and eliminating entire classes of potential bugs.