« get me outta code hell

content, test: linkContribution: tooltip icons - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/static/client3.js
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-07-21 20:06:32 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-11-24 13:45:08 -0400
commitc11edada828dc734cce6988e5819630a73326085 (patch)
tree2754223a452ea3d6bc71cf12e6787f7c1ad40c37 /src/static/client3.js
parenta6dedb0b17f514e408919240c060072df2b179dd (diff)
content, test: linkContribution: tooltip icons
Diffstat (limited to 'src/static/client3.js')
-rw-r--r--src/static/client3.js150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/static/client3.js b/src/static/client3.js
index 8372a268..091d1fcf 100644
--- a/src/static/client3.js
+++ b/src/static/client3.js
@@ -958,6 +958,8 @@ clientSteps.addPageListeners.push(addScrollListenerForStickyHeadings);
 
 // Image overlay ------------------------------------------
 
+// TODO: Update to clientSteps style.
+
 function addImageOverlayClickHandlers() {
   const container = document.getElementById('image-overlay-container');
 
@@ -1245,6 +1247,8 @@ function loadImage(imageUrl, onprogress) {
 
 // Group contributions table ------------------------------
 
+// TODO: Update to clientSteps style.
+
 const groupContributionsTableInfo =
   Array.from(document.querySelectorAll('#content dl'))
     .filter(dl => dl.querySelector('a.group-contributions-sort-button'))
@@ -1277,6 +1281,152 @@ for (const info of groupContributionsTableInfo) {
   });
 }
 
+// Artist link icon tooltips ------------------------------
+
+// TODO: Update to clientSteps style.
+
+const linkIconTooltipInfo =
+  Array.from(document.querySelectorAll('span.contribution.has-tooltip'))
+    .map(span => ({
+      mainLink: span.querySelector('a'),
+      iconsContainer: span.querySelector('span.icons-tooltip'),
+      iconLinks: span.querySelectorAll('span.icons-tooltip a'),
+    }));
+
+for (const info of linkIconTooltipInfo) {
+  const focusElements =
+    [info.mainLink, ...info.iconLinks];
+
+  const hoverElements =
+    [info.mainLink, info.iconsContainer];
+
+  let hidden = true;
+
+  const show = () => {
+    info.iconsContainer.classList.add('visible');
+    info.iconsContainer.inert = false;
+    hidden = false;
+  };
+
+  const hide = () => {
+    info.iconsContainer.classList.remove('visible');
+    info.iconsContainer.inert = true;
+    hidden = true;
+  };
+
+  const considerHiding = () => {
+    if (hoverElements.some(el => el.matches(':hover'))) {
+      return;
+    }
+
+    if (focusElements.includes(document.activeElement)) {
+      return;
+    }
+
+    if (justTouched) {
+      return;
+    }
+
+    hide();
+  };
+
+  // Hover (pointer)
+
+  let hoverTimeout;
+
+  info.mainLink.addEventListener('mouseenter', () => {
+    if (hidden) {
+      hoverTimeout = setTimeout(show, 250);
+    }
+  });
+
+  info.mainLink.addEventListener('mouseout', () => {
+    if (hidden) {
+      clearTimeout(hoverTimeout);
+    } else {
+      considerHiding();
+    }
+  });
+
+  info.iconsContainer.addEventListener('mouseout', () => {
+    if (!hidden) {
+      considerHiding();
+    }
+  });
+
+  // Focus (keyboard)
+
+  let focusTimeout;
+
+  info.mainLink.addEventListener('focus', () => {
+    focusTimeout = setTimeout(show, 750);
+  });
+
+  info.mainLink.addEventListener('blur', () => {
+    clearTimeout(focusTimeout);
+  });
+
+  info.iconsContainer.addEventListener('focusout', () => {
+    requestAnimationFrame(considerHiding);
+  });
+
+  info.mainLink.addEventListener('blur', () => {
+    requestAnimationFrame(considerHiding);
+  });
+
+  // Touch (finger)
+
+  let justTouched = false;
+  let touchTimeout;
+
+  info.mainLink.addEventListener('touchend', event => {
+    let wasTarget = false;
+
+    for (const touch of event.changedTouches) {
+      if (touch.target === info.mainLink) {
+        wasTarget = true;
+        break;
+      }
+    }
+
+    if (!wasTarget) {
+      return;
+    }
+
+    justTouched = true;
+
+    clearTimeout(touchTimeout);
+    touchTimeout = setTimeout(() => {
+      justTouched = false;
+    }, 250);
+
+    show();
+  });
+
+  info.mainLink.addEventListener('click', event => {
+    if (hidden && justTouched) {
+      event.preventDefault();
+      event.target.focus();
+      show();
+    }
+  });
+
+  document.body.addEventListener('touchend', event => {
+    const touches = [...event.changedTouches, ...event.touches];
+    for (const {clientX, clientY} of touches) {
+      const touchEl = document.elementFromPoint(clientX, clientY);
+      if (!touchEl) continue;
+
+      for (const hoverEl of hoverElements) {
+        if (touchEl === hoverEl) return;
+        if (hoverEl.contains(touchEl)) return;
+      }
+    }
+
+    hide();
+  });
+}
+
 // Sticky commentary sidebar ------------------------------
 
 const albumCommentarySidebarInfo = clientInfo.albumCommentarySidebarInfo = {