« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/content/dependencies/generateAdditionalNamesBox.js48
-rw-r--r--src/content/dependencies/generatePageLayout.js38
-rw-r--r--src/content/dependencies/generateTrackInfoPage.js8
-rw-r--r--src/static/client3.js90
-rw-r--r--src/static/site5.css66
-rw-r--r--src/strings-default.yaml15
6 files changed, 249 insertions, 16 deletions
diff --git a/src/content/dependencies/generateAdditionalNamesBox.js b/src/content/dependencies/generateAdditionalNamesBox.js
new file mode 100644
index 00000000..f7fa3b00
--- /dev/null
+++ b/src/content/dependencies/generateAdditionalNamesBox.js
@@ -0,0 +1,48 @@
+import {stitchArrays} from '#sugar';
+
+export default {
+  contentDependencies: ['transformContent'],
+  extraDependencies: ['html', 'language'],
+
+  relations: (relation, additionalNames) => ({
+    names:
+      additionalNames.map(({name}) =>
+        relation('transformContent', name)),
+
+    annotations:
+      additionalNames.map(({annotation}) =>
+        (annotation
+          ? relation('transformContent', annotation)
+          : null)),
+  }),
+
+  generate: (relations, {html, language}) => {
+    const names =
+      relations.names.map(name =>
+        html.tag('span', {class: 'additional-name'},
+          name.slot('mode', 'inline')));
+
+    const annotations =
+      relations.annotations.map(annotation =>
+        (annotation
+          ? html.tag('span', {class: 'annotation'},
+              language.$('misc.additionalNames.item.annotation', {
+                annotation:
+                  annotation.slot('mode', 'inline'),
+              }))
+          : null));
+
+    return html.tag('div', {id: 'additional-names-box'}, [
+      html.tag('p',
+        language.$('misc.additionalNames.title')),
+
+      html.tag('ul',
+        stitchArrays({name: names, annotation: annotations})
+          .map(({name, annotation}) =>
+            html.tag('li',
+              (annotation
+                ? language.$('misc.additionalNames.item.withAnnotation', {name, annotation})
+                : language.$('misc.additionalNames.item', {name}))))),
+    ]);
+  },
+};
diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js
index 95551f3e..7dee8cc3 100644
--- a/src/content/dependencies/generatePageLayout.js
+++ b/src/content/dependencies/generatePageLayout.js
@@ -108,6 +108,8 @@ export default {
     title: {type: 'html'},
     showWikiNameInTitle: {type: 'boolean', default: true},
 
+    additionalNames: {type: 'html'},
+
     cover: {type: 'html'},
 
     socialEmbed: {type: 'html'},
@@ -222,22 +224,25 @@ export default {
     const colors = getColors(slots.color ?? data.wikiColor);
     const hasSocialEmbed = !html.isBlank(slots.socialEmbed);
 
-    let titleHTML = null;
-
-    if (!html.isBlank(slots.title)) {
-      switch (slots.headingMode) {
-        case 'sticky':
-          titleHTML =
-            relations.stickyHeadingContainer.slots({
-              title: slots.title,
-              cover: slots.cover,
-            });
-          break;
-        case 'static':
-          titleHTML = html.tag('h1', slots.title);
-          break;
-      }
-    }
+    const titleContentsHTML =
+      (html.isBlank(slots.title)
+        ? null
+     : html.isBlank(slots.additionalNames)
+        ? language.sanitize(slots.title)
+        : html.tag('a', {
+            href: '#additional-names-box',
+            title: language.$('misc.additionalNames.tooltip').toString(),
+          }, language.sanitize(slots.title)));
+
+    const titleHTML =
+      (html.isBlank(slots.title)
+        ? null
+     : slots.headingMode === 'sticky'
+        ? relations.stickyHeadingContainer.slots({
+            title: titleContentsHTML,
+            cover: slots.cover,
+          })
+        : html.tag('h1', titleContentsHTML));
 
     let footerContent = slots.footerContent;
 
@@ -254,6 +259,7 @@ export default {
         titleHTML,
 
         slots.cover,
+        slots.additionalNames,
 
         html.tag('div',
           {
diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js
index 93334948..180e5c29 100644
--- a/src/content/dependencies/generateTrackInfoPage.js
+++ b/src/content/dependencies/generateTrackInfoPage.js
@@ -6,6 +6,7 @@ import getChronologyRelations from '../util/getChronologyRelations.js';
 export default {
   contentDependencies: [
     'generateAdditionalFilesShortcut',
+    'generateAdditionalNamesBox',
     'generateAlbumAdditionalFilesList',
     'generateAlbumNavAccent',
     'generateAlbumSidebar',
@@ -106,6 +107,11 @@ export default {
       list: relation('generateAlbumAdditionalFilesList', album, additionalFiles),
     });
 
+    if (!empty(track.additionalNames)) {
+      relations.additionalNamesBox =
+        relation('generateAdditionalNamesBox', track.additionalNames);
+    }
+
     if (track.hasUniqueCoverArt || album.hasCoverArt) {
       relations.cover =
         relation('generateTrackCoverArtwork', track);
@@ -300,6 +306,8 @@ export default {
         title: language.$('trackPage.title', {track: data.name}),
         headingMode: 'sticky',
 
+        additionalNames: relations.additionalNamesBox ?? null,
+
         color: data.color,
         styleRules: [relations.albumStyleRules],
 
diff --git a/src/static/client3.js b/src/static/client3.js
index 4a5dffc2..94ba4a23 100644
--- a/src/static/client3.js
+++ b/src/static/client3.js
@@ -1260,6 +1260,96 @@ function loadImage(imageUrl, onprogress) {
   });
 }
 
+// "Additional names" box ---------------------------------
+
+const additionalNamesBoxInfo = clientInfo.additionalNamesBox = {
+  box: null,
+  links: null,
+  mainContentContainer: null,
+
+  state: {
+    visible: false,
+  },
+};
+
+function getAdditionalNamesBoxReferences() {
+  const info = additionalNamesBoxInfo;
+
+  info.box =
+    document.getElementById('additional-names-box');
+
+  info.links =
+    document.querySelectorAll('a[href="#additional-names-box"]');
+
+  info.mainContentContainer =
+    document.querySelector('#content .main-content-container');
+}
+
+function addAdditionalNamesBoxInternalListeners() {
+  const info = additionalNamesBoxInfo;
+
+  hashLinkInfo.event.beforeHashLinkScrolls.push(({target}) => {
+    if (target === info.box) {
+      return false;
+    }
+  });
+}
+
+function addAdditionalNamesBoxListeners() {
+  const info = additionalNamesBoxInfo;
+
+  for (const link of info.links) {
+    link.addEventListener('click', domEvent => {
+      handleAdditionalNamesBoxLinkClicked(domEvent);
+    });
+  }
+}
+
+function handleAdditionalNamesBoxLinkClicked(domEvent) {
+  const info = additionalNamesBoxInfo;
+  const {state} = info;
+
+  domEvent.preventDefault();
+
+  if (!info.box || !info.mainContentContainer) return;
+
+  const margin =
+    +(cssProp(info.box, 'scroll-margin-top').replace('px', ''));
+
+  const {top} =
+    (state.visible
+      ? info.box.getBoundingClientRect()
+      : info.mainContentContainer.getBoundingClientRect());
+
+  if (top + 20 < margin || top > 0.4 * window.innerHeight) {
+    if (!state.visible) {
+      toggleAdditionalNamesBox();
+    }
+
+    window.scrollTo({
+      top: window.scrollY + top - margin,
+      behavior: 'smooth',
+    });
+  } else {
+    toggleAdditionalNamesBox();
+  }
+}
+
+function toggleAdditionalNamesBox() {
+  const info = additionalNamesBoxInfo;
+  const {state} = info;
+
+  state.visible = !state.visible;
+  info.box.style.display =
+    (state.visible
+      ? 'block'
+      : 'none');
+}
+
+clientSteps.getPageReferences.push(getAdditionalNamesBoxReferences);
+clientSteps.addInternalListeners.push(addAdditionalNamesBoxInternalListeners);
+clientSteps.addPageListeners.push(addAdditionalNamesBoxListeners);
+
 // Group contributions table ------------------------------
 
 const groupContributionsTableInfo =
diff --git a/src/static/site5.css b/src/static/site5.css
index ea27e35e..31b2995b 100644
--- a/src/static/site5.css
+++ b/src/static/site5.css
@@ -802,6 +802,68 @@ html[data-url-key="localized.listing"][data-url-value0="random"] #content a:not(
   opacity: 0.7;
 }
 
+/* Additional names (heading and box) */
+
+h1 a[href="#additional-names-box"] {
+  color: inherit;
+  text-decoration: underline;
+  text-decoration-style: dotted;
+}
+
+h1 a[href="#additional-names-box"]:hover {
+  text-decoration-style: solid;
+}
+
+#additional-names-box {
+  --custom-scroll-offset: calc(0.5em - 2px);
+
+  margin: 1em 0 1em -10px;
+  padding: 15px 20px 10px 20px;
+  width: max-content;
+  max-width: min(60vw, 600px);
+
+  border: 1px dotted var(--primary-color);
+  border-radius: 6px;
+
+  background:
+    linear-gradient(var(--bg-color), var(--bg-color)),
+    linear-gradient(#000000bb, #000000bb),
+    var(--primary-color);
+
+  box-shadow: 0 -2px 6px -1px var(--dim-color) inset;
+
+  display: none;
+}
+
+#additional-names-box > :first-child { margin-top: 0; }
+#additional-names-box > :last-child { margin-bottom: 0; }
+
+#additional-names-box p {
+  padding-left: 10px;
+  padding-right: 10px;
+  margin-bottom: 0;
+  font-style: oblique;
+}
+
+#additional-names-box ul {
+  padding-left: 10px;
+  margin-top: 0.5em;
+}
+
+#additional-names-box li .additional-name {
+  margin-right: 0.25em;
+}
+
+#additional-names-box li .additional-name .content-image {
+  margin-bottom: 0.25em;
+  margin-top: 0.5em;
+}
+
+#additional-names-box li .annotation {
+  opacity: 0.8;
+  display: inline-block;
+}
+
 /* Images */
 
 .image-container {
@@ -1760,6 +1822,10 @@ html[data-language-code="preview-en"][data-url-key="localized.home"] #content
     max-width: unset;
   }
 
+  #additional-names-box {
+    max-width: unset;
+  }
+
   /* Show sticky heading above cover art */
 
   .content-sticky-heading-container {
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index e6b8d6db..7562af84 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -338,6 +338,21 @@ trackList:
 #
 misc:
 
+  # additionalNames:
+  #   "Drop"-styled box that catalogues a variety of additional or
+  #   alternate names for the current thing; toggled by clicking on the
+  #   thing's title, which is styled interactively and gets a tooltip
+  #   (hover text), since it isn't usually an interactive element.
+
+  additionalNames:
+    title: "Additional or alternate names:"
+    tooltip: "Click to view additional or alternate names"
+
+    item:
+      _: "{NAME}"
+      withAnnotation: "{NAME} {ANNOTATION}"
+      annotation: "({ANNOTATION})"
+
   # alt:
   #   Fallback text for the alt text of images and artworks - these
   #   are read aloud by screen readers.