Files
thrillwiki_django_no_react/templates/rides/partials/search_script.html
pacnpal de8b6f67a3 Refactor ride filters and forms to use AlpineJS for state management and HTMX for AJAX interactions
- Enhanced filter sidebar with AlpineJS for collapsible sections and localStorage persistence.
- Removed custom JavaScript in favor of AlpineJS for managing filter states and interactions.
- Updated ride form to utilize AlpineJS for handling manufacturer, designer, and ride model selections.
- Simplified search script to leverage AlpineJS for managing search input and suggestions.
- Improved error handling for HTMX requests with minimal JavaScript.
- Refactored ride form data handling to encapsulate logic within an AlpineJS component.
2025-09-26 15:25:12 -04:00

166 lines
4.8 KiB
HTML

<!-- HTMX + AlpineJS ONLY - NO CUSTOM JAVASCRIPT -->
<div x-data="{
searchQuery: new URLSearchParams(window.location.search).get('search') || '',
showSuggestions: false,
selectedIndex: -1,
init() {
// Watch for URL changes
this.$watch('searchQuery', value => {
if (value.length >= 2) {
this.showSuggestions = true;
} else {
this.showSuggestions = false;
}
});
// Handle clicks outside to close suggestions
this.$el.addEventListener('click', (e) => {
if (!e.target.closest('#search-suggestions') && !e.target.closest('#search')) {
this.showSuggestions = false;
}
});
},
handleInput() {
// HTMX will handle the actual search request
if (this.searchQuery.length >= 2) {
this.showSuggestions = true;
} else {
this.showSuggestions = false;
}
},
selectSuggestion(text) {
this.searchQuery = text;
this.showSuggestions = false;
// Update the search input
this.$refs.searchInput.value = text;
// Trigger form change for HTMX
this.$refs.searchForm.dispatchEvent(new Event('change'));
},
handleKeydown(e) {
const suggestions = this.$el.querySelectorAll('#search-suggestions button');
if (!suggestions.length) return;
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
if (this.selectedIndex < suggestions.length - 1) {
this.selectedIndex++;
suggestions[this.selectedIndex].focus();
}
break;
case 'ArrowUp':
e.preventDefault();
if (this.selectedIndex > 0) {
this.selectedIndex--;
suggestions[this.selectedIndex].focus();
} else {
this.$refs.searchInput.focus();
this.selectedIndex = -1;
}
break;
case 'Escape':
this.showSuggestions = false;
this.selectedIndex = -1;
this.$refs.searchInput.blur();
break;
case 'Enter':
if (e.target.tagName === 'BUTTON') {
e.preventDefault();
this.selectSuggestion(e.target.dataset.text);
}
break;
case 'Tab':
this.showSuggestions = false;
break;
}
}
}"
@click.outside="showSuggestions = false">
<!-- Search Input with HTMX -->
<input
x-ref="searchInput"
x-model="searchQuery"
@input="handleInput()"
@keydown="handleKeydown($event)"
hx-get="/rides/search-suggestions/"
hx-trigger="input changed delay:200ms"
hx-target="#search-suggestions"
hx-swap="innerHTML"
hx-include="[name='park_slug']"
:aria-expanded="showSuggestions"
aria-controls="search-suggestions"
type="text"
name="search"
id="search"
placeholder="Search rides..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
<!-- Suggestions Container -->
<div
x-show="showSuggestions"
x-transition
id="search-suggestions"
role="listbox"
class="absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto"
>
<!-- HTMX will populate this -->
</div>
<!-- Form Reference for HTMX -->
<form x-ref="searchForm" style="display: none;">
<!-- Hidden form for HTMX reference -->
</form>
</div>
<!-- HTMX Loading Indicator Styles -->
<style>
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1;
}
.loading-indicator {
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 50;
display: flex;
align-items: center;
gap: 0.5rem;
color: white;
font-size: 0.875rem;
}
.loading-indicator svg {
width: 1.25rem;
height: 1.25rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</style>
<!-- HTMX Error Handling (Minimal JavaScript as allowed by Context7 docs) -->
<div x-data="{
init() {
// Only essential HTMX error handling as shown in Context7 docs
this.$el.addEventListener('htmx:responseError', (evt) => {
if (evt.detail.xhr.status === 404 || evt.detail.xhr.status === 500) {
console.error('HTMX Error:', evt.detail.xhr.status);
}
});
}
}"></div>