« get me outta code hell

client: search results behavior stub - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-04-30 21:01:36 -0300
committer(quasar) nebula <qznebula@protonmail.com>2024-05-31 12:11:46 -0300
commitb705b488b04522a0d21da989015c40461f120c6e (patch)
tree85c99cd7a321515857a900e7441633e9f1760a6d
parent188916742f82c937d114b886ef54e71d150cea44 (diff)
client: search results behavior stub
-rw-r--r--src/content/dependencies/generateSearchSidebarBox.js2
-rw-r--r--src/search.js11
-rw-r--r--src/static/css/site.css39
-rw-r--r--src/static/js/client.js136
-rw-r--r--src/util/searchSchema.js33
5 files changed, 209 insertions, 12 deletions
diff --git a/src/content/dependencies/generateSearchSidebarBox.js b/src/content/dependencies/generateSearchSidebarBox.js
index f2fddfa5..6632a4e8 100644
--- a/src/content/dependencies/generateSearchSidebarBox.js
+++ b/src/content/dependencies/generateSearchSidebarBox.js
@@ -9,7 +9,7 @@ export default {
 
   generate: (relations, {html}) =>
     relations.sidebarBox.slots({
-      attributes: {class: 'search-sidebar-box'},
+      attributes: {class: 'wiki-search-sidebar-box'},
       collapsible: false,
 
       content: [
diff --git a/src/search.js b/src/search.js
index b82a77b8..34718b5c 100644
--- a/src/search.js
+++ b/src/search.js
@@ -70,12 +70,23 @@ async function populateSearchIndexes(indexes, wikiData) {
     indexes.tracks,
     track => ({
       name: track.name,
+      color: track.color,
       album: track.album.name,
+      albumDirectory: track.album.directory,
+
       artists: [
         track.artistContribs.map(contrib => contrib.artist.name),
         ...track.artistContribs.map(contrib => contrib.artist.aliasNames)
       ].flat(),
+
       additionalNames: track.additionalNames.map(entry => entry.name),
+
+      artworkKind:
+        (track.hasUniqueCoverArt
+          ? 'track'
+       : track.album.hasCoverArt
+          ? 'album'
+          : 'none'),
     })
   );
 
diff --git a/src/static/css/site.css b/src/static/css/site.css
index 2dc06469..d2c121e6 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -462,7 +462,7 @@ summary .group-name {
   font-weight: normal;
 }
 
-.search-sidebar-box {
+.wiki-search-sidebar-box {
   padding: 2px;
   position: sticky;
   top: 5px;
@@ -490,6 +490,43 @@ summary .group-name {
   color: inherit;
 }
 
+.wiki-search-results-container hr {
+  border-color: var(--primary-color);
+  border-style: none none dotted none;
+}
+
+.wiki-search-results-container {
+  margin-bottom: 5px;
+}
+
+.wiki-search-result {
+  display: flex;
+  padding: 0 6px;
+  margin: 3px 0 5px 0;
+}
+
+.wiki-search-result-name {
+  align-self: center;
+  flex-grow: 1;
+  padding-bottom: 2px;
+}
+
+.wiki-search-result-image,
+.wiki-search-result-image-placeholder {
+  align-self: flex-start;
+  width: 1.8em;
+  height: 1.8em;
+  margin-right: 6px;
+}
+
+.wiki-search-result-image-placeholder {
+  display: inline-block;
+}
+
+.wiki-search-results:not(:has(.wiki-search-result-image)) .wiki-search-result-image-placeholder {
+  display: none;
+}
+
 .wiki-search-input:focus {
   border: 1px solid var(--primary-color);
 }
diff --git a/src/static/js/client.js b/src/static/js/client.js
index a3d63719..0a4dc0ff 100644
--- a/src/static/js/client.js
+++ b/src/static/js/client.js
@@ -3456,6 +3456,142 @@ document.addEventListener('DOMContentLoaded', initSearch);
 
 window.searchAll = searchAll;
 
+// Sidebar search box -------------------------------------
+
+const sidebarSearchInfo = initInfo('sidebarSearchInfo', {
+  searchBox: null,
+  searchInput: null,
+
+  resultsContainer: null,
+  results: null,
+});
+
+function getSidebarSearchReferences() {
+  const info = sidebarSearchInfo;
+
+  info.searchBox =
+    document.querySelector('.wiki-search-sidebar-box');
+
+  info.searchInput =
+    document.querySelector('.wiki-search-input');
+}
+
+function mutateSidebarSearchContent() {
+  const info = sidebarSearchInfo;
+
+  if (!info.searchBox) return;
+
+  info.resultsContainer =
+    document.createElement('div');
+
+  info.resultsContainer.classList.add('wiki-search-results-container');
+
+  cssProp(info.resultsContainer, 'display', 'none');
+  info.resultsContainer.appendChild(document.createElement('hr'));
+
+  info.results =
+    document.createElement('div');
+
+  info.results.classList.add('wiki-search-results');
+
+  info.resultsContainer.appendChild(info.results);
+
+  info.searchBox.appendChild(info.resultsContainer);
+}
+
+function addSidebarSearchListeners() {
+  const info = sidebarSearchInfo;
+
+  if (!info.searchInput) return;
+
+  info.searchInput.addEventListener('change', domEvent => {
+    activateSidebarSearch(info.searchInput.value);
+  });
+}
+
+function activateSidebarSearch(query) {
+  showSidebarSearchResults(searchAll(query, {enrich: true}));
+}
+
+function showSidebarSearchResults(results) {
+  const info = sidebarSearchInfo;
+
+  const flatResults =
+    Object.entries(results)
+      .flatMap(([index, results]) => results
+        .flatMap(({field, result}) => result
+          .flatMap(({doc, id}) => ({
+            index,
+            field,
+            reference: id ?? null,
+            directory: (id ? id.split(':')[1] : null),
+            data: doc,
+          }))));
+
+  console.log(flatResults);
+
+  while (info.results.firstChild) {
+    info.results.firstChild.remove();
+  }
+
+  cssProp(info.resultsContainer, 'display', 'block');
+
+  for (const result of flatResults) {
+    if (result.index !== 'tracks') continue;
+
+    const link = document.createElement('a');
+    link.classList.add('wiki-search-result');
+
+    link.setAttribute('href', openTrack(result.directory));
+    cssProp(link, '--primary-color', result.data.color);
+
+    const span = document.createElement('span');
+    span.classList.add('wiki-search-result-name');
+
+    span.appendChild(document.createTextNode(result.data.name));
+
+    let image;
+    image = document.createElement('img');
+    image.classList.add('wiki-search-result-image');
+
+    switch (result.data.artworkKind) {
+      case 'track':
+        image.setAttribute('src', rebase(
+          (`album-art`
+         + `/${result.data.albumDirectory}`
+         + `/${result.directory}`
+         + `.small.jpg`),
+          'rebaseThumb'));
+        break;
+
+      case 'album':
+        image.setAttribute('src', rebase(
+          (`album-art`
+         + `/${result.data.albumDirectory}`
+         + `/cover.small.jpg`),
+          'rebaseThumb'));
+        break;
+
+      default:
+        image = document.createElement('span');
+        image.classList.add('wiki-search-result-image-placeholder');
+        break;
+    }
+
+    if (image) {
+      link.appendChild(image);
+    }
+
+    link.appendChild(span);
+
+    info.results.appendChild(link);
+  }
+}
+
+clientSteps.getPageReferences.push(getSidebarSearchReferences);
+clientSteps.mutatePageContent.push(mutateSidebarSearchContent);
+clientSteps.addPageListeners.push(addSidebarSearchListeners);
+
 // Sticky commentary sidebar ------------------------------
 
 const albumCommentarySidebarInfo = initInfo('albumCommentarySidebarInfo', {
diff --git a/src/util/searchSchema.js b/src/util/searchSchema.js
index ce0e7a84..1628dbd4 100644
--- a/src/util/searchSchema.js
+++ b/src/util/searchSchema.js
@@ -3,28 +3,41 @@
 export function makeSearchIndexes(FlexSearch) {
   const indexes = {
     albums: new FlexSearch.Document({
-      id: "reference",
-      index: ["name", "groups"],
+      id: 'reference',
+      index: ['name', 'groups'],
     }),
 
     tracks: new FlexSearch.Document({
-      id: "reference",
-      index: ["name", "album", "artists", "additionalNames"],
+      id: 'reference',
+
+      index: [
+        'name',
+        'album',
+        'artists',
+        'additionalNames',
+      ],
+
+      store: [
+        'color',
+        'name',
+        'albumDirectory',
+        'artworkKind',
+      ],
     }),
 
     artists: new FlexSearch.Document({
-      id: "reference",
-      index: ["names"],
+      id: 'reference',
+      index: ['names'],
     }),
 
     groups: new FlexSearch.Document({
-      id: "reference",
-      index: ["name", "description", "category"],
+      id: 'reference',
+      index: ['name', 'description', 'category'],
     }),
 
     flashes: new FlexSearch.Document({
-      id: "reference",
-      index: ["name", "tracks", "contributors"],
+      id: 'reference',
+      index: ['name', 'tracks', 'contributors'],
     }),
   };