diff options
| author | Danilo M. <danix@danix.xyz> | 2026-04-20 13:50:23 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-04-20 13:50:23 +0200 |
| commit | aefc3d8c3994ba0eb1e3dfced3564ba2b0f8b73f (patch) | |
| tree | 07ac5f584df52397b92188876612a233a25dee7b /themes/danix-xyz-hacker/assets | |
| parent | b6d4089bc8db863d361e21759469c2fcbfcd854c (diff) | |
| download | danixxyz-aefc3d8c3994ba0eb1e3dfced3564ba2b0f8b73f.tar.gz danixxyz-aefc3d8c3994ba0eb1e3dfced3564ba2b0f8b73f.zip | |
feat: create shared search module with lazy-loading and Alpine components
- Implement loadSearchIndex() for async JSON fetching and caching
- Implement filterArticles(query, articles) with case-insensitive search (max 5 results)
- Register three Alpine.js components: searchOverlay, mobileSearch, notFoundPage
- Support desktop modal, mobile menu, and 404 page search integration
- Include Escape key handling and index lazy-loading optimizations
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Diffstat (limited to 'themes/danix-xyz-hacker/assets')
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/search.js | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/themes/danix-xyz-hacker/assets/js/search.js b/themes/danix-xyz-hacker/assets/js/search.js new file mode 100644 index 0000000..94c6323 --- /dev/null +++ b/themes/danix-xyz-hacker/assets/js/search.js @@ -0,0 +1,130 @@ +// Lazy-load search index from JSON file +async function loadSearchIndex() { + if (window.searchIndex) { + return window.searchIndex; + } + try { + const response = await fetch('/search-index.json'); + if (!response.ok) throw new Error('Failed to load search index'); + window.searchIndex = await response.json(); + return window.searchIndex; + } catch (error) { + console.error('Error loading search index:', error); + return []; + } +} + +// Filter articles by query (case-insensitive, max 5 results) +function filterArticles(query, articles) { + if (!query.trim()) { + return []; + } + const lowerQuery = query.toLowerCase(); + return articles + .filter(article => + article.title.toLowerCase().includes(lowerQuery) || + article.summary.toLowerCase().includes(lowerQuery) + ) + .slice(0, 5); +} + +// Register Alpine.js components +document.addEventListener('alpine:init', () => { + // Desktop search modal component + Alpine.data('searchOverlay', () => ({ + isOpen: false, + searchQuery: '', + filteredArticles: [], + allArticles: [], + indexLoaded: false, + + async open() { + this.isOpen = true; + await this.ensureIndexLoaded(); + this.$nextTick(() => { + const input = this.$el.querySelector('#search-input-desktop'); + if (input) input.focus(); + }); + }, + + close() { + this.isOpen = false; + this.searchQuery = ''; + this.filteredArticles = []; + }, + + async ensureIndexLoaded() { + if (!this.indexLoaded) { + this.allArticles = await loadSearchIndex(); + this.indexLoaded = true; + } + }, + + filterArticles(query) { + this.searchQuery = query; + this.filteredArticles = filterArticles(query, this.allArticles); + }, + + handleEscape(event) { + if (event.key === 'Escape') { + this.close(); + } + } + })); + + // Mobile search component (integrated into hamburger menu) + Alpine.data('mobileSearch', () => ({ + searchQuery: '', + filteredArticles: [], + allArticles: [], + indexLoaded: false, + + async ensureIndexLoaded() { + if (!this.indexLoaded) { + this.allArticles = await loadSearchIndex(); + this.indexLoaded = true; + } + }, + + filterArticles(query) { + this.searchQuery = query; + this.filteredArticles = filterArticles(query, this.allArticles); + } + })); + + // Refactored 404 page component + Alpine.data('notFoundPage', () => ({ + showEasterEgg: false, + searchQuery: '', + filteredArticles: [], + allArticles: [], + indexLoaded: false, + + async init() { + await this.ensureIndexLoaded(); + }, + + async ensureIndexLoaded() { + if (!this.indexLoaded) { + this.allArticles = await loadSearchIndex(); + this.indexLoaded = true; + } + }, + + filterArticles(query) { + this.searchQuery = query; + this.filteredArticles = filterArticles(query, this.allArticles); + }, + + toggleEasterEgg() { + this.showEasterEgg = !this.showEasterEgg; + }, + + goToRandomArticle() { + if (this.allArticles.length > 0) { + const randomArticle = this.allArticles[Math.floor(Math.random() * this.allArticles.length)]; + window.location.href = randomArticle.url; + } + } + })); +}); |
