- Timestamp:
- 06/21/2026 05:26:47 PM (3 months ago)
- Branches:
- main
- Children:
- fefb80c
- Parents:
- e579a98
- Location:
- src/views
- Files:
-
- 2 edited
-
pages/search.ejs (modified) (3 diffs)
-
partials/topnav.ejs (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/views/pages/search.ejs
re579a98 r097da22 1 1 <% const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : ''; %> 2 2 <% const _tracks = (typeof tracks !== 'undefined' && tracks) ? tracks : []; %> 3 <% const _events = (typeof events !== 'undefined' && events) ? events : []; %> 4 <% const _pages = (typeof pages !== 'undefined' && pages) ? pages : []; %> 3 5 <div class="container search-page"> 4 6 <h1><%= t('nav.search') %></h1> … … 89 91 <% } %> 90 92 91 <% if (query && !queryError && !results.length && !_tracks.length) { %> 93 <% if (_events.length) { %> 94 <h2 class="search-section-title"><%= t('search.section_events') %></h2> 95 <ol class="search-results"> 96 <% _events.forEach(function(ev) { %> 97 <li class="search-result"> 98 <h3 class="search-result-title"><a href="<%= ev.url %>"><%= ev.where || '—' %></a></h3> 99 <p class="search-result-meta"><%= [ev.date, ev.time].filter(Boolean).join(' ') %></p> 100 </li> 101 <% }); %> 102 </ol> 103 <% } %> 104 105 <% if (_pages.length) { %> 106 <h2 class="search-section-title"><%= t('search.section_pages') %></h2> 107 <ul class="search-pages"> 108 <% _pages.forEach(function(pg) { %> 109 <li><a class="search-page-link" href="<%= pg.url %>"><%= pg.label %> <span aria-hidden="true">→</span></a></li> 110 <% }); %> 111 </ul> 112 <% } %> 113 114 <% if (query && !queryError && !results.length && !_tracks.length && !_events.length && !_pages.length) { %> 92 115 <p class="search-empty"><%= t('search.empty') %></p> 93 116 <% } %> … … 211 234 border-radius: 3px; 212 235 } 236 .search-pages { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; } 237 .search-page-link { 238 display: inline-flex; align-items: center; gap: 0.3rem; 239 padding: 0.45rem 0.8rem; border: 1px solid var(--rule); border-radius: 999px; 240 color: var(--ink); text-decoration: none; font-size: 0.9rem; background: var(--paper-2); 241 } 242 .search-page-link:hover { border-color: var(--accent); color: var(--accent); } 213 243 </style> -
src/views/partials/topnav.ejs
re579a98 r097da22 183 183 184 184 <!-- Search overlay (shared by mobile bottom-tab + desktop top-nav) --> 185 <div id="search-overlay" class="search-overlay" hidden >185 <div id="search-overlay" class="search-overlay" hidden data-suggest="<%= _siteUrlBase %>/search/suggest" data-action="<%= _siteUrlBase %>/search"> 186 186 <div class="container"> 187 187 <form class="search-overlay-form" method="get" action="<%= _siteUrlBase %>/search"> 188 <input type="search" name="q" placeholder=" Zoeken in posts…" autocomplete="off">189 <button type="submit" class="nav-btn" aria-label=" Zoeken">188 <input type="search" name="q" placeholder="<%= t('search.placeholder') %>" autocomplete="off"> 189 <button type="submit" class="nav-btn" aria-label="<%= t('search.button') %>"> 190 190 <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> 191 191 </button> 192 <button type="button" class="nav-btn" id="search-close" aria-label=" Sluiten">192 <button type="button" class="nav-btn" id="search-close" aria-label="<%= t('imed.cancel') %>"> 193 193 <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> 194 194 </button> 195 195 </form> 196 <div id="search-suggest" class="search-suggest" data-lenis-prevent></div> 196 197 </div> 197 198 </div> … … 215 216 function overlay() { return document.getElementById('search-overlay'); } 216 217 function openSearch() { var o = overlay(); if (o) { o.hidden = false; var i = o.querySelector('input'); if (i) i.focus(); } } 217 function closeSearch() { var o = overlay(); if (o) o.hidden = true;}218 function closeSearch() { var o = overlay(); if (o) { o.hidden = true; var b = document.getElementById('search-suggest'); if (b) b.innerHTML = ''; } } 218 219 219 220 document.body.addEventListener('click', function(e) { … … 228 229 229 230 document.addEventListener('keydown', function(e) { 230 if (e.key === 'Escape') { var o = overlay(); if (o && !o.hidden) o.hidden = true; } 231 if (e.key === 'Escape') { var o = overlay(); if (o && !o.hidden) closeSearch(); } 232 }); 233 234 // ── Live resultaten tijdens typen ─────────────────────────────── 235 var _ST = { 236 posts: '<%= t('search.section_posts') %>', tracks: '<%= t('search.section_tracks') %>', 237 events: '<%= t('search.section_events') %>', pages: '<%= t('search.section_pages') %>', 238 all: '<%= t('search.suggest_all') %>', none: '<%= t('search.suggest_empty') %>' 239 }; 240 function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function(c){ return {'&':'&','<':'<','>':'>','"':'"'}[c]; }); } 241 function renderSuggest(box, d, q, ov) { 242 var html = ''; 243 function grp(title, items, fmt) { 244 if (!items || !items.length) return; 245 html += '<div class="ss-group"><div class="ss-title">' + esc(title) + '</div>' + items.map(fmt).join('') + '</div>'; 246 } 247 grp(_ST.posts, d.posts, function(p){ return '<a class="ss-item" href="' + esc(p.url) + '">' + esc(p.title) + '</a>'; }); 248 grp(_ST.tracks, d.tracks, function(tk){ var sub = tk.artist ? ' <span class="ss-sub">' + esc(tk.artist) + '</span>' : ''; return tk.url ? '<a class="ss-item" href="' + esc(tk.url) + '">' + esc(tk.title) + sub + '</a>' : '<span class="ss-item ss-noclick">' + esc(tk.title) + sub + '</span>'; }); 249 grp(_ST.events, d.events, function(ev){ return '<a class="ss-item" href="' + esc(ev.url) + '">' + esc(ev.where || ev.when) + ' <span class="ss-sub">' + esc(ev.when) + '</span></a>'; }); 250 grp(_ST.pages, d.pages, function(pg){ return '<a class="ss-item" href="' + esc(pg.url) + '">' + esc(pg.label) + '</a>'; }); 251 var hasAny = (d.posts && d.posts.length) || (d.tracks && d.tracks.length) || (d.events && d.events.length) || (d.pages && d.pages.length); 252 if (!hasAny) { box.innerHTML = '<div class="ss-empty">' + esc(_ST.none) + '</div>'; return; } 253 var action = (ov && ov.getAttribute('data-action')) || '/search'; 254 html += '<a class="ss-all" href="' + esc(action) + '?q=' + encodeURIComponent(q) + '">' + esc(_ST.all) + '</a>'; 255 box.innerHTML = html; 256 } 257 var _sTimer; 258 document.addEventListener('input', function(e) { 259 var inp = e.target.closest && e.target.closest('#search-overlay input[name="q"]'); 260 if (!inp) return; 261 var box = document.getElementById('search-suggest'); 262 if (!box) return; 263 var q = inp.value.trim(); 264 clearTimeout(_sTimer); 265 if (q.length < 2) { box.innerHTML = ''; return; } 266 _sTimer = setTimeout(function() { 267 var ov = document.getElementById('search-overlay'); 268 var url = (ov && ov.getAttribute('data-suggest')) || '/search/suggest'; 269 fetch(url + '?q=' + encodeURIComponent(q)) 270 .then(function(r){ return r.ok ? r.json() : null; }) 271 .then(function(d){ if (d) renderSuggest(box, d, q, ov); }) 272 .catch(function(){}); 273 }, 200); 274 }); 275 // Klik op een suggestie → overlay sluiten (de link/boost doet de navigatie). 276 document.addEventListener('click', function(e) { 277 if (e.target.closest && e.target.closest('#search-suggest a')) { var o = overlay(); if (o) o.hidden = true; } 231 278 }); 232 279 })(); … … 499 546 display: inline-flex; align-items: center; justify-content: center; 500 547 } 548 /* Live-resultaten dropdown */ 549 .search-suggest { margin-top: .6rem; max-height: 60vh; overflow-y: auto; display: flex; flex-direction: column; gap: .25rem; } 550 .search-suggest:empty { display: none; } 551 .ss-group { padding: .25rem 0; } 552 .ss-title { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-soft, var(--ink-muted)); padding: .35rem .25rem .2rem; } 553 .ss-item { display: block; padding: .55rem .6rem; border-radius: 8px; color: var(--ink); text-decoration: none; font-size: .95rem; } 554 .ss-item:hover { background: var(--paper-2); color: var(--accent); } 555 .ss-item.ss-noclick { opacity: .7; cursor: default; } 556 .ss-sub { color: var(--ink-soft, var(--ink-muted)); font-size: .85em; } 557 .ss-empty { padding: .8rem .6rem; color: var(--ink-soft, var(--ink-muted)); font-size: .9rem; } 558 .ss-all { display: block; margin-top: .35rem; padding: .55rem .6rem; border-top: 1px solid var(--rule); color: var(--accent); font-weight: 600; text-decoration: none; font-size: .9rem; } 559 .ss-all:hover { text-decoration: underline; } 501 560 </style>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)