mirror of
https://github.com/hyzendust/hyzendust.github.io.git
synced 2026-09-19 02:23:17 +02:00
Add: Search bar
This commit is contained in:
@@ -1538,3 +1538,178 @@ th {
|
||||
border: 1px solid var(--foreground-color);
|
||||
color: var(--foreground-color);
|
||||
}
|
||||
|
||||
/* ── Navbar search ─────────────────────────────────────────────────────── */
|
||||
.menu.language .menu__list {
|
||||
display: flow-root;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.menu.language .menu__list > li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.menu.language .menu__list > li:not(.menu__item--search) {
|
||||
float: left;
|
||||
height: 1.8rem;
|
||||
margin: 0 0.5rem 0.5rem 0;
|
||||
}
|
||||
|
||||
.menu.language .menu__list > li.menu__item--uninotes {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hyzen-user .menu.language .menu__list > li.menu__item--uninotes {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.menu__item--search {
|
||||
float: right;
|
||||
height: 1.8rem;
|
||||
margin: 0 0 0.5rem 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-search {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.site-search__input {
|
||||
width: 9.3rem;
|
||||
max-width: 32vw;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0.55rem;
|
||||
border: 1px solid var(--background-color1);
|
||||
border-right: none;
|
||||
border-radius: 4px 0 0 4px;
|
||||
background-color: var(--background-color);
|
||||
color: var(--foreground-color);
|
||||
font-size: 0.8rem;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.site-search__input:focus {
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.site-search__input::placeholder {
|
||||
color: var(--foreground-color3, #888);
|
||||
}
|
||||
|
||||
.site-search__input::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.site-search__btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
padding: 0 0.5rem;
|
||||
border-radius: 0 4px 4px 0;
|
||||
border: 1px solid #f26522;
|
||||
background-color: #f26522;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.site-search__btn:hover,
|
||||
.site-search__btn:focus {
|
||||
background-color: transparent;
|
||||
border-color: #f26522;
|
||||
color: #f26522;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.site-search__btn {
|
||||
background-color: transparent;
|
||||
border-color: currentColor;
|
||||
color: var(--foreground-color);
|
||||
}
|
||||
|
||||
.site-search__btn:hover,
|
||||
.site-search__btn:focus {
|
||||
background-color: var(--foreground-color) !important;
|
||||
border-color: var(--foreground-color) !important;
|
||||
color: var(--background-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.site-search__input {
|
||||
width: 6.15rem;
|
||||
max-width: 40vw;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Search results page ──────────────────────────────────────────────── */
|
||||
.search-results__query {
|
||||
color: var(--foreground-color3, #888);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.search-results__status {
|
||||
margin: 1rem 0;
|
||||
color: var(--foreground-color3, #888);
|
||||
}
|
||||
|
||||
.search-results__list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 1.5rem 0 0 0;
|
||||
}
|
||||
|
||||
.search-results__item {
|
||||
margin-bottom: 1.4rem;
|
||||
border-left: 3px solid var(--accent-color);
|
||||
padding-left: 0.9rem;
|
||||
}
|
||||
|
||||
.search-results__item-title {
|
||||
margin: 0 0 0.3rem 0;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.search-results__item-title a {
|
||||
color: var(--foreground-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.search-results__item-title a:hover {
|
||||
color: var(--accent-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.search-results__item-meta {
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
color: var(--accent-color);
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.search-results__item-snippet {
|
||||
margin: 0;
|
||||
font-size: 0.92rem;
|
||||
color: var(--foreground-color);
|
||||
}
|
||||
|
||||
.search-results__item-snippet mark {
|
||||
background-color: var(--accent-color);
|
||||
color: var(--background-color);
|
||||
padding: 0 0.1rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
129
static/js/search.js
Normal file
129
static/js/search.js
Normal file
@@ -0,0 +1,129 @@
|
||||
(function () {
|
||||
var listEl = document.querySelector('[data-search-list]');
|
||||
var statusEl = document.querySelector('[data-search-status]');
|
||||
var queryEl = document.querySelector('[data-search-query]');
|
||||
if (!listEl || !statusEl) return;
|
||||
|
||||
function getQueryParam(name) {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
return params.get(name) || '';
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str.replace(/[&<>"']/g, function (c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
||||
});
|
||||
}
|
||||
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function buildSnippet(content, term) {
|
||||
var radius = 90;
|
||||
var start = 0;
|
||||
var end = Math.min(content.length, radius * 2);
|
||||
|
||||
if (term) {
|
||||
var idx = content.toLowerCase().indexOf(term.toLowerCase());
|
||||
if (idx !== -1) {
|
||||
start = Math.max(0, idx - radius);
|
||||
end = Math.min(content.length, idx + term.length + radius);
|
||||
}
|
||||
}
|
||||
|
||||
var snippet = content.slice(start, end);
|
||||
if (start > 0) snippet = '\u2026' + snippet;
|
||||
if (end < content.length) snippet = snippet + '\u2026';
|
||||
|
||||
var escaped = escapeHtml(snippet);
|
||||
if (term) {
|
||||
var re = new RegExp('(' + escapeRegExp(term) + ')', 'ig');
|
||||
escaped = escaped.replace(re, '<mark>$1</mark>');
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
function sectionLabel(section) {
|
||||
if (section === 'blog') return 'Blog';
|
||||
if (section === 'services') return 'Service';
|
||||
return section;
|
||||
}
|
||||
|
||||
var query = getQueryParam('q').trim();
|
||||
if (queryEl) queryEl.textContent = query ? 'for "' + query + '"' : '';
|
||||
|
||||
if (!query) {
|
||||
statusEl.textContent = 'Enter a search term above.';
|
||||
return;
|
||||
}
|
||||
|
||||
statusEl.textContent = 'Searching\u2026';
|
||||
|
||||
fetch('/index.json')
|
||||
.then(function (res) {
|
||||
if (!res.ok) throw new Error('Failed to load search index');
|
||||
return res.json();
|
||||
})
|
||||
.then(function (items) {
|
||||
var terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
|
||||
var matches = items
|
||||
.map(function (item) {
|
||||
var titleLower = (item.title || '').toLowerCase();
|
||||
var contentLower = (item.content || '').toLowerCase();
|
||||
var score = 0;
|
||||
terms.forEach(function (term) {
|
||||
if (titleLower.indexOf(term) !== -1) score += 10;
|
||||
if (contentLower.indexOf(term) !== -1) score += 1;
|
||||
});
|
||||
return { item: item, score: score };
|
||||
})
|
||||
.filter(function (m) {
|
||||
return m.score > 0;
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return b.score - a.score;
|
||||
});
|
||||
|
||||
listEl.innerHTML = '';
|
||||
|
||||
if (matches.length === 0) {
|
||||
statusEl.textContent = 'No results found for "' + query + '".';
|
||||
return;
|
||||
}
|
||||
|
||||
statusEl.textContent =
|
||||
matches.length + ' result' + (matches.length === 1 ? '' : 's') + ' found.';
|
||||
|
||||
matches.forEach(function (m) {
|
||||
var item = m.item;
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.className = 'search-results__item';
|
||||
|
||||
var meta = document.createElement('span');
|
||||
meta.className = 'search-results__item-meta';
|
||||
meta.textContent = sectionLabel(item.section);
|
||||
|
||||
var h2 = document.createElement('h2');
|
||||
h2.className = 'search-results__item-title';
|
||||
var a = document.createElement('a');
|
||||
a.href = item.url;
|
||||
a.textContent = item.title;
|
||||
h2.appendChild(a);
|
||||
|
||||
var p = document.createElement('p');
|
||||
p.className = 'search-results__item-snippet';
|
||||
p.innerHTML = buildSnippet(item.content || '', terms[0] || query);
|
||||
|
||||
li.appendChild(meta);
|
||||
li.appendChild(h2);
|
||||
li.appendChild(p);
|
||||
listEl.appendChild(li);
|
||||
});
|
||||
})
|
||||
.catch(function () {
|
||||
statusEl.textContent = 'Something went wrong loading search results.';
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user