Skip to content

Commit b8ec1ea

Browse files
authored
search: rank results by relevance and group with headings (#202)
1 parent 9ecce09 commit b8ec1ea

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

assets/css/style.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ section h2 {
197197
border: 1px solid var(--color-border);
198198
border-radius: var(--radius);
199199
overflow: hidden;
200+
margin-top: 1rem;
200201
}
201202

202203
.app-item {
@@ -219,6 +220,17 @@ section h2 {
219220
z-index: 1;
220221
}
221222

223+
.app-group-heading {
224+
padding: 0.5rem 0.75rem;
225+
background: var(--color-surface);
226+
color: var(--color-text-muted);
227+
font-size: 0.75rem;
228+
font-weight: 600;
229+
text-transform: uppercase;
230+
letter-spacing: 0.05em;
231+
border-bottom: 1px solid var(--color-border);
232+
}
233+
222234
/* Categories */
223235
.categories-list {
224236
display: flex;

assets/js/script.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,25 @@ document.addEventListener('DOMContentLoaded', function() {
9696
}
9797
}
9898

99+
// Relevance rank: 0 exact name, 1 name starts with, 2 name contains, 3 description only.
100+
var RANK_LABELS = ['Exact match', 'Name match', 'Name match', 'Description match'];
101+
102+
function matchRank(k, terms) {
103+
var name = cache[k].name;
104+
if (terms.length === 1 && name === terms[0]) return 0;
105+
var nameHit = terms.every(function(t) { return name.indexOf(t) !== -1; });
106+
if (!nameHit) return 3;
107+
return name.indexOf(terms[0]) === 0 ? 1 : 2;
108+
}
109+
99110
function applyFilters() {
100111
var terms = input ? input.value.toLowerCase().split(/\s+/).filter(Boolean) : [];
101112
var selectedArch = arch ? arch.value : '';
113+
var list = document.getElementById('app-list').querySelector('.app-list');
114+
var matched = [];
115+
116+
var oldHeadings = list.querySelectorAll('.app-group-heading');
117+
for (var h = 0; h < oldHeadings.length; h++) oldHeadings[h].remove();
102118

103119
for (var k = 0; k < appNodes.length; k++) {
104120
var matchesSearch = !terms.length ||
@@ -108,7 +124,31 @@ document.addEventListener('DOMContentLoaded', function() {
108124
});
109125
var matchesArch = !selectedArch ||
110126
cache[k].archs.some(function(a) { return a === selectedArch; });
111-
appNodes[k].style.display = (matchesSearch && matchesArch) ? '' : 'none';
127+
var visible = matchesSearch && matchesArch;
128+
appNodes[k].style.display = visible ? '' : 'none';
129+
if (visible) matched.push({ index: k, rank: terms.length ? matchRank(k, terms) : 0 });
130+
}
131+
132+
if (terms.length) {
133+
matched.sort(function(a, b) {
134+
return a.rank !== b.rank ? a.rank - b.rank : cache[a.index].name.localeCompare(cache[b.index].name);
135+
});
136+
}
137+
138+
var lastLabel = null;
139+
for (var m = 0; m < matched.length; m++) {
140+
var entry = matched[m];
141+
if (terms.length) {
142+
var label = RANK_LABELS[entry.rank];
143+
if (label !== lastLabel) {
144+
var heading = document.createElement('div');
145+
heading.className = 'app-group-heading';
146+
heading.textContent = label;
147+
list.appendChild(heading);
148+
lastLabel = label;
149+
}
150+
}
151+
list.appendChild(appNodes[entry.index]);
112152
}
113153
}
114154

0 commit comments

Comments
 (0)