Update activeContext.md and productContext.md with new project information and context

This commit is contained in:
pacnpal
2025-09-19 13:35:53 -04:00
parent 6625fb5ba9
commit cd6403615f
23 changed files with 11224 additions and 133 deletions

View File

@@ -0,0 +1,459 @@
# Alpine.js Optimization Strategies and Best Practices
## Research Summary
Comprehensive research from Alpine.js documentation focusing on performance optimization, component patterns, and best practices for the ThrillWiki frontend redesign.
## Performance Optimization Strategies
### 1. Component Initialization and Lifecycle
#### Efficient Component Registration
```javascript
document.addEventListener('alpine:init', () => {
Alpine.data('dropdown', () => ({
open: false,
toggle() {
this.open = !this.open
},
destroy() {
// Clean up resources to prevent memory leaks
clearInterval(this.timer);
}
}))
})
```
#### Performance Measurement
```javascript
window.start = performance.now();
document.addEventListener('alpine:initialized', () => {
setTimeout(() => {
console.log(performance.now() - window.start);
});
});
```
### 2. Event Handling Optimization
#### Use .passive Modifier for Scroll Performance
```html
<div @touchstart.passive="...">...</div>
<div @wheel.passive="...">...</div>
```
**Critical**: The `.passive` modifier prevents blocking the browser's scroll optimizations by indicating the listener won't call `preventDefault()`.
#### Debounced Event Handling
```html
<input @input.debounce.300ms="search()" placeholder="Search...">
```
#### Efficient Event Delegation
```html
<div @click.outside="open = false" x-data="{ open: false }">
<!-- Content -->
</div>
```
### 3. Data Management Optimization
#### Minimize Reactive Data
```javascript
Alpine.data('app', () => ({
// Only make data reactive if it needs to trigger UI updates
items: [], // Reactive - triggers UI updates
_cache: {}, // Non-reactive - use for internal state
get filteredItems() {
// Use getters for computed properties
return this.items.filter(item => item.active)
}
}))
```
#### Efficient Array Operations
```javascript
// Good: Use array methods that don't trigger full re-renders
this.items.splice(index, 1); // Remove specific item
this.items.push(newItem); // Add item
// Avoid: Full array replacement when possible
// this.items = this.items.filter(...) // Triggers full re-render
```
### 4. DOM Manipulation Optimization
#### Use x-show vs x-if Strategically
```html
<!-- Use x-show for frequently toggled content -->
<div x-show="isVisible" x-transition>
Frequently toggled content
</div>
<!-- Use x-if for conditionally rendered content -->
<template x-if="shouldRender">
<div>Rarely shown content</div>
</template>
```
#### Optimize x-for Loops
```html
<!-- Always use :key for efficient list updates -->
<template x-for="item in items" :key="item.id">
<div x-text="item.name"></div>
</template>
```
#### Minimize DOM Queries
```javascript
Alpine.data('component', () => ({
init() {
// Cache DOM references in init()
this.element = this.$el;
this.container = this.$refs.container;
}
}))
```
### 5. Memory Management
#### Proper Cleanup in destroy()
```javascript
Alpine.data('timer', () => ({
timer: null,
counter: 0,
init() {
this.timer = setInterval(() => {
console.log('Increased counter to', ++this.counter);
}, 1000);
},
destroy() {
// Critical: Clean up to prevent memory leaks
clearInterval(this.timer);
}
}))
```
#### Avoid Memory Leaks in Event Listeners
```javascript
Alpine.data('component', () => ({
init() {
// Use arrow functions to maintain context
this.handleResize = () => this.onResize();
window.addEventListener('resize', this.handleResize);
},
destroy() {
window.removeEventListener('resize', this.handleResize);
}
}))
```
## Component Architecture Patterns
### 1. Reusable Component Registration
#### Global Component Registration
```javascript
Alpine.data('dropdown', () => ({
open: false,
toggle() {
this.open = !this.open
},
// Encapsulate directive logic
trigger: {
['@click']() {
this.toggle()
}
},
dialogue: {
['x-show']() {
return this.open
}
}
}))
```
#### Usage in Templates
```html
<div x-data="dropdown">
<button x-bind="trigger">Toggle</button>
<div x-bind="dialogue">Content</div>
</div>
```
### 2. State Management Patterns
#### Hierarchical Data Access
```html
<div x-data="{ open: false }">
<div x-data="{ label: 'Content:' }">
<span x-text="label"></span>
<span x-show="open"></span> <!-- Accesses parent's open -->
</div>
</div>
```
#### Centralized State with $store
```javascript
Alpine.store('app', {
user: null,
parks: [],
setUser(user) {
this.user = user
},
addPark(park) {
this.parks.push(park)
}
})
```
### 3. Advanced Interaction Patterns
#### Custom Event Dispatching
```html
<div @park-created="handleParkCreated">
<button @click="$dispatch('park-created', { park: newPark })">
Create Park
</button>
</div>
```
#### Intersection Observer Integration
```html
<div x-data="{ shown: false }" x-intersect="shown = true">
<div x-show="shown" x-transition>
I'm in the viewport!
</div>
</div>
```
#### Watch for Reactive Updates
```html
<div x-data="{ search: '' }"
x-init="$watch('search', value => console.log('Search:', value))">
<input x-model="search" placeholder="Search...">
</div>
```
## Integration with HTMX
### 1. Complementary Usage Patterns
#### HTMX for Server Communication, Alpine for Client State
```html
<div x-data="{ loading: false, count: 0 }">
<button hx-post="/increment"
hx-target="#result"
@click="loading = true; count++"
@htmx:after-request="loading = false">
<span x-show="!loading">Increment</span>
<span x-show="loading">Loading...</span>
</button>
<div id="result" x-text="'Local count: ' + count"></div>
</div>
```
#### Coordinated State Updates
```html
<div x-data="{ items: [] }"
@item-added.window="items.push($event.detail)">
<form hx-post="/items"
hx-target="#items-list"
@htmx:after-request="$dispatch('item-added', { item: newItem })">
<!-- Form fields -->
</form>
<div id="items-list"></div>
</div>
```
### 2. Performance Coordination
#### Efficient DOM Updates
```html
<!-- Use Alpine for immediate feedback -->
<div x-data="{ optimisticUpdate: false }">
<button @click="optimisticUpdate = true"
hx-post="/action"
hx-target="#result"
@htmx:after-request="optimisticUpdate = false">
<span x-show="!optimisticUpdate">Click me</span>
<span x-show="optimisticUpdate">Processing...</span>
</button>
</div>
```
## ThrillWiki-Specific Optimizations
### 1. Search Component Optimization
```javascript
Alpine.data('parkSearch', () => ({
query: '',
results: [],
loading: false,
async search() {
if (!this.query.trim()) {
this.results = [];
return;
}
this.loading = true;
try {
// Use HTMX for actual search, Alpine for state
const response = await fetch(`/search/parks/?q=${encodeURIComponent(this.query)}`);
this.results = await response.json();
} catch (error) {
console.error('Search failed:', error);
this.results = [];
} finally {
this.loading = false;
}
},
get filteredResults() {
return this.results.slice(0, 10); // Limit results for performance
}
}))
```
### 2. Photo Gallery Optimization
```javascript
Alpine.data('photoGallery', () => ({
photos: [],
currentIndex: 0,
loading: false,
init() {
// Lazy load images as they come into view
this.$nextTick(() => {
this.setupIntersectionObserver();
});
},
setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
this.$el.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
},
destroy() {
// Clean up observer
if (this.observer) {
this.observer.disconnect();
}
}
}))
```
### 3. Form Validation Optimization
```javascript
Alpine.data('parkForm', () => ({
form: {
name: '',
location: '',
operator: ''
},
errors: {},
validating: false,
async validateField(field) {
if (this.validating) return;
this.validating = true;
try {
// Use HTMX for server-side validation
const response = await fetch('/validate-park-field/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCsrfToken()
},
body: JSON.stringify({
field: field,
value: this.form[field]
})
});
const result = await response.json();
if (result.errors) {
this.errors[field] = result.errors;
} else {
delete this.errors[field];
}
} finally {
this.validating = false;
}
},
getCsrfToken() {
return document.querySelector('[name=csrfmiddlewaretoken]').value;
}
}))
```
## Performance Monitoring
### 1. Component Performance Tracking
```javascript
Alpine.data('performanceTracker', () => ({
init() {
const start = performance.now();
this.$nextTick(() => {
const end = performance.now();
console.log(`Component initialized in ${end - start}ms`);
});
}
}))
```
### 2. Memory Usage Monitoring
```javascript
// Monitor component count and memory usage
setInterval(() => {
if (performance.memory) {
console.log('Memory usage:', {
used: Math.round(performance.memory.usedJSHeapSize / 1048576) + 'MB',
total: Math.round(performance.memory.totalJSHeapSize / 1048576) + 'MB'
});
}
}, 10000);
```
## Implementation Priorities for ThrillWiki
### High Priority
1. **Component Registration**: Set up reusable components for common UI patterns
2. **Event Optimization**: Use .passive modifiers for scroll events
3. **Memory Management**: Implement proper cleanup in destroy() methods
4. **State Management**: Optimize reactive data usage
### Medium Priority
1. **Intersection Observer**: Lazy loading for images and content
2. **Performance Monitoring**: Track component initialization times
3. **Advanced Patterns**: Custom event dispatching and coordination
4. **Search Optimization**: Debounced search with result limiting
### Low Priority
1. **Advanced State Management**: Global stores for complex state
2. **Custom Directives**: Create project-specific Alpine directives
3. **Performance Profiling**: Detailed memory and performance analysis

View File

@@ -0,0 +1,257 @@
# HTMX Best Practices and Advanced Techniques
## Research Summary
Comprehensive research from HTMX documentation and Django-specific patterns to inform the ThrillWiki frontend redesign.
## Core HTMX Patterns for Implementation
### 1. Essential UI Patterns
#### Active Search Pattern
```html
<input type="search" name="q" placeholder="Search..."
hx-get="/search"
hx-trigger="input changed delay:250ms"
hx-target="#results">
<div id="results"></div>
```
**Application**: Enhance park and ride search functionality with real-time results.
#### Click to Edit Pattern
```html
<div hx-target="this" hx-swap="outerHTML">
<span id="name">World</span>
<button hx-get="/names/1" hx-target="closest div">Edit</button>
</div>
```
**Application**: Inline editing for park details, ride information.
#### Infinite Scroll Pattern
```html
<div id="content">
<!-- Initial content here -->
</div>
<div hx-trigger="revealed" hx-get="/more-content" hx-swap="beforeend" hx-target="#content">
Loading...
</div>
```
**Application**: Park and ride listings with progressive loading.
#### Lazy Loading Pattern
```html
<div hx-trigger="intersect" hx-get="/lazy-content" hx-swap="innerHTML">
Loading...
</div>
```
**Application**: Photo galleries, detailed ride statistics.
### 2. Form Handling Patterns
#### Inline Validation Pattern
```html
<form hx-post="/contact">
<input type="email" id="email" name="email" required
hx-trigger="input changed delay:500ms"
hx-post="/validate-email"
hx-target="#email-error">
<span id="email-error" class="error"></span>
</form>
```
**Application**: Real-time validation for park creation, ride submission forms.
#### Bulk Update Pattern
```html
<form hx-put="/names">
<input type="hidden" name="_method" value="PUT">
<table class="table">
<tbody>
<tr>
<td><input class="form-check-input" type="checkbox" name="ids[]" value="1">
<td><input type="text" name="names[]" value="Alice">
</tr>
</tbody>
</table>
<button>Update Selected</button>
</form>
```
**Application**: Batch operations for moderation, bulk park updates.
### 3. Advanced Interaction Patterns
#### Progress Bar Pattern
```html
<div class="progress">
<div id="progress-bar" class="progress-bar" style="width: 0%;">0%</div>
</div>
<button hx-post="/run-job"
hx-target="#progress-bar"
hx-swap="innerHTML">Run Job</button>
```
**Application**: Photo upload progress, data import operations.
#### Modal Dialog Pattern
```html
<button hx-get="/modal/content" hx-target="#modal-container" hx-swap="innerHTML">Open Modal</button>
<div id="modal-container"></div>
```
**Application**: Park creation forms, ride detail modals, photo viewers.
#### Value Select Pattern (Dependent Dropdowns)
```html
<select name="country" hx-get="/states" hx-target="#state-select">
<option value="US">United States</option>
<option value="CA">Canada</option>
</select>
<select id="state-select" name="state">
<option value="">Please select a state</option>
</select>
```
**Application**: Location selection (Country -> State -> City), park filtering.
## Django-Specific HTMX Patterns
### 1. Form Validation with Django
#### HTMX Form Validation Decorator
```python
def htmx_form_validate(*, form_class: type):
def decorator(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
if (
request.method == "GET"
and "Hx-Request" in request.headers
and (htmx_validation_field := request.GET.get("_validate_field", None))
):
form = form_class(request.GET)
form.is_valid() # trigger validation
return HttpResponse(render_single_field_row(form, htmx_validation_field))
return view_func(request, *args, **kwargs)
return wrapper
return decorator
```
#### Template Integration
```html
<div
class="field is-horizontal {{ classes }}"
id="form-row-{{ field.name }}"
hx-get="."
hx-vals='{"_validate_field": "{{ field.name }}" }'
hx-trigger="focusout from:#form-row-{{ field.name }}"
hx-include="#form-row-{{ field.name }}"
hx-target="this"
hx-ext="morph"
hx-swap="morph:outerHTML"
>
```
### 2. Django View Patterns
#### Block-Based Partial Rendering
```python
@for_htmx(use_block_from_params=True)
def monster_detail(request: HttpRequest, monster_id: int):
monster = get_object_or_404(Monster.objects.all(), id=monster_id)
if request.method == "POST":
if "kick" in request.POST:
monster.kick()
elif "hug" in request.POST:
monster.hug()
if not is_htmx(request):
return HttpResponseRedirect("")
return TemplateResponse(request, "monster_detail.html", {"monster": monster})
```
#### Modal Form Handling
```python
form = CreateMonsterForm(request.POST)
if form.is_valid():
monster = form.save()
return HttpResponse(
headers={
"Hx-Trigger": json.dumps({
"closeModal": True,
"monsterCreated": monster.id,
})
}
)
```
### 3. Template Organization
#### Inline Partials Pattern
```html
{% block monster-form %}
<form
method="POST"
action=""
id="monster-form"
hx-post=""
hx-target="#monster-form"
hx-swap="outerHTML"
hx-vals='{"use_block": "monster-form"}'
>
{% csrf_token %}
<!-- Form content -->
</form>
{% endblock %}
```
## Performance Optimizations
### 1. Request Optimization
- Use `hx-trigger="input changed delay:500ms"` for debounced search
- Implement `hx-push-url="true"` for browser history management
- Use `hx-swap="morph:outerHTML"` for efficient DOM updates
### 2. Loading States
- Implement loading indicators with `htmx-indicator` class
- Use skeleton screens for better perceived performance
- Add progress bars for long-running operations
### 3. Error Handling
- Implement comprehensive error responses
- Use `hx-confirm` for destructive actions
- Provide clear user feedback for all operations
## Integration with Alpine.js
### Complementary Usage
```html
<div x-data="{ count: 0 }">
<button hx-post="/increment"
hx-target="#count-display"
hx-swap="innerHTML"
@click="count++">
Increment
</button>
<span id="count-display" x-text="'Count: ' + count">Count: 0</span>
</div>
```
### Event Coordination
- Use HTMX for server communication
- Use Alpine.js for client-side state management
- Coordinate between both using custom events
## Implementation Priorities for ThrillWiki
### High Priority
1. **Active Search**: Real-time park and ride search
2. **Inline Validation**: Form validation with immediate feedback
3. **Click to Edit**: Inline editing for park/ride details
4. **Modal Dialogs**: Form submissions and detail views
### Medium Priority
1. **Infinite Scroll**: Progressive loading for large lists
2. **Bulk Operations**: Moderation and batch updates
3. **Progress Indicators**: File uploads and data operations
4. **Dependent Dropdowns**: Location and category selection
### Low Priority
1. **Advanced Animations**: Smooth transitions between states
2. **Real-time Updates**: Live notifications and updates
3. **Offline Support**: Progressive web app features