« get me outta code hell

content, client: listArtTagNetwork: art tag stat switcher - 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-03-19 19:46:47 -0300
committer(quasar) nebula <qznebula@protonmail.com>2025-02-25 20:03:28 -0400
commita94150e3fc6d5276310c97a7693a378462a7b393 (patch)
tree0ff63fa74cd4528d2cf1ccf0ad471a26ea108a6c
parentd91f879aee6cd624256be3cf5ddbcd638cc6c264 (diff)
content, client: listArtTagNetwork: art tag stat switcher
-rw-r--r--src/content/dependencies/listArtTagNetwork.js105
-rw-r--r--src/static/css/site.css15
-rw-r--r--src/static/js/client/art-tag-network.js136
-rw-r--r--src/static/js/client/index.js2
-rw-r--r--src/strings-default.yaml14
5 files changed, 265 insertions, 7 deletions
diff --git a/src/content/dependencies/listArtTagNetwork.js b/src/content/dependencies/listArtTagNetwork.js
index 830c5929..56d72e17 100644
--- a/src/content/dependencies/listArtTagNetwork.js
+++ b/src/content/dependencies/listArtTagNetwork.js
@@ -27,6 +27,20 @@ export default {
        {directAncestorArtTags: ancestorsB}) =>
         ancestorsA.length - ancestorsB.length);
 
+    const getStats = (artTag) => ({
+      directUses:
+        artTag.directlyTaggedInThings.length,
+
+      totalUses:
+        unique([
+          ...artTag.indirectlyTaggedInThings,
+          ...artTag.directlyTaggedInThings,
+        ]).length,
+
+      descendants:
+        artTag.allDescendantArtTags.length,
+    });
+
     const recursive = (artTag, depth) => {
       const descendantNodes =
         (empty(artTag.directDescendantArtTags)
@@ -52,8 +66,11 @@ export default {
           ? unique(artTag.directAncestorArtTags.map(recursiveGetRootAncestor))
           : null);
 
+      const stats = getStats(artTag);
+
       return {
         artTag,
+        stats,
         descendantNodes,
         ancestorRootArtTags,
       };
@@ -121,6 +138,15 @@ export default {
       directory:
         queryNode.artTag.directory,
 
+      directUses:
+        queryNode.stats.directUses,
+
+      totalUses:
+        queryNode.stats.totalUses,
+
+      descendants:
+        queryNode.stats.descendants,
+
       representsRoot:
         rootArtTags.includes(queryNode.artTag),
 
@@ -148,6 +174,45 @@ export default {
   generate(data, relations, {html, language}) {
     const prefix = `listingPage.listArtTags.network`;
 
+    const wrapTag = (dataNode, relationsNode) => [
+      html.tag('span', {class: 'network-tag'},
+        language.$(prefix, 'tag', {
+          tag:
+            relationsNode.artTagLink,
+        })),
+
+      html.tag('span', {class: 'network-tag'},
+        {class: 'with-stat'},
+        {style: 'display: none'},
+
+        language.$(prefix, 'tag.withStat', {
+          tag:
+            (dataNode.representsRoot
+              ? relationsNode.artTagLink.slots({
+                  anchor: true,
+                  hash: dataNode.directory,
+                })
+              : relationsNode.artTagLink),
+
+          stat:
+            language.$(prefix, 'tag.withStat.stat', {
+              stat: [
+                html.tag('span', {class: 'network-tag-stat'},
+                  {class: 'network-tag-direct-uses-stat'},
+                  dataNode.directUses.toString()),
+
+                html.tag('span', {class: 'network-tag-stat'},
+                  {class: 'network-tag-total-uses-stat'},
+                  dataNode.totalUses.toString()),
+
+                html.tag('span', {class: 'network-tag-stat'},
+                  {class: 'network-tag-descendants-stat'},
+                  dataNode.descendants.toString()),
+              ],
+            })
+        }))
+    ];
+
     const recursive = (dataNode, relationsNode, depth) => [
       html.tag('dt',
         {
@@ -158,7 +223,9 @@ export default {
         (depth === 0
           ? (relationsNode.ancestorTagLinks
               ? language.$(prefix, 'root.withAncestors', {
-                  tag: relationsNode.artTagLink,
+                  tag:
+                    wrapTag(dataNode, relationsNode),
+
                   ancestors:
                     language.formatUnitList(
                       stitchArrays({
@@ -171,7 +238,9 @@ export default {
                           }))),
                 })
               : language.$(prefix, 'root.jumpToTop', {
-                  tag: relationsNode.artTagLink,
+                  tag:
+                    wrapTag(dataNode, relationsNode),
+
                   link:
                     html.tag('a', {href: '#top'},
                       language.$(prefix, 'root.jumpToTop.link')),
@@ -179,13 +248,11 @@ export default {
           : (dataNode.representsRoot
               ? language.$(prefix, 'descendant.jumpToRoot', {
                   tag:
-                    relationsNode.artTagLink.slots({
-                      anchor: true,
-                      hash: dataNode.directory,
-                    }),
+                    wrapTag(dataNode, relationsNode),
                 })
               : language.$(prefix, 'descendant', {
-                  tag: relationsNode.artTagLink,
+                  tag:
+                    wrapTag(dataNode, relationsNode),
                 })))),
 
       dataNode.descendantNodes &&
@@ -204,6 +271,30 @@ export default {
       type: 'custom',
 
       content: [
+        html.tag('p', {id: 'network-stat-line'},
+          language.$(prefix, 'statLine', {
+            stat: [
+              html.tag('a', {id: 'network-stat-none'},
+                {href: '#'},
+                language.$(prefix, 'statLine.none')),
+
+              html.tag('a', {id: 'network-stat-total-uses'},
+                {href: '#'},
+                {style: 'display: none'},
+                language.$(prefix, 'statLine.totalUses')),
+
+              html.tag('a', {id: 'network-stat-direct-uses'},
+                {href: '#'},
+                {style: 'display: none'},
+                language.$(prefix, 'statLine.directUses')),
+
+              html.tag('a', {id: 'network-stat-descendants'},
+                {href: '#'},
+                {style: 'display: none'},
+                language.$(prefix, 'statLine.descendants')),
+            ],
+          })),
+
         html.tag('dl', {id: 'network-top-dl'}, [
           html.tag('dt', {id: 'top'},
             language.$(prefix, 'jumpToRoot.title')),
diff --git a/src/static/css/site.css b/src/static/css/site.css
index e8c2b17f..125756cd 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -2022,6 +2022,21 @@ html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:not
   padding-bottom: 20px;
 }
 
+html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-stat-line {
+  padding-left: 10px;
+  margin-left: 20px;
+}
+
+html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-stat-line a {
+  text-decoration: underline;
+  text-decoration-style: dotted;
+}
+
+/* CSS rule order pls */
+html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-stat-line a:hover {
+  text-decoration-style: solid;
+}
+
 html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt {
   padding-left: 10px;
   margin-left: 20px;
diff --git a/src/static/js/client/art-tag-network.js b/src/static/js/client/art-tag-network.js
new file mode 100644
index 00000000..fc5f6526
--- /dev/null
+++ b/src/static/js/client/art-tag-network.js
@@ -0,0 +1,136 @@
+/* eslint-env browser */
+
+import {cssProp} from '../client-util.js';
+
+import {atOffset, stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'artTagNetworkInfo',
+
+  noneStatLink: null,
+  totalUsesStatLink: null,
+  directUsesStatLink: null,
+  descendantsStatLink: null,
+
+  tagsWithoutStats: null,
+  tagsWithStats: null,
+
+  totalUsesStats: null,
+  directUsesStats: null,
+  descendantsStats: null,
+};
+
+export function getPageReferences() {
+  if (
+    document.documentElement.dataset.urlKey !== 'localized.listing' ||
+    document.documentElement.dataset.urlValue0 !== 'tags/network'
+  ) {
+    return;
+  }
+
+  info.noneStatLink =
+    document.getElementById('network-stat-none');
+
+  info.totalUsesStatLink =
+    document.getElementById('network-stat-total-uses');
+
+  info.directUsesStatLink =
+    document.getElementById('network-stat-direct-uses');
+
+  info.descendantsStatLink =
+    document.getElementById('network-stat-descendants');
+
+  info.tagsWithoutStats =
+    document.querySelectorAll('.network-tag:not(.with-stat)');
+
+  info.tagsWithStats =
+    document.querySelectorAll('.network-tag.with-stat');
+
+  info.totalUsesStats =
+    Array.from(document.getElementsByClassName('network-tag-total-uses-stat'));
+
+  info.directUsesStats =
+    Array.from(document.getElementsByClassName('network-tag-direct-uses-stat'));
+
+  info.descendantsStats =
+    Array.from(document.getElementsByClassName('network-tag-descendants-stat'));
+}
+
+export function addPageListeners() {
+  if (!info.noneStatLink) return;
+
+  const linkOrder = [
+    info.noneStatLink,
+    info.totalUsesStatLink,
+    info.directUsesStatLink,
+    info.descendantsStatLink,
+  ];
+
+  const statsOrder = [
+    null,
+    info.totalUsesStats,
+    info.directUsesStats,
+    info.descendantsStats,
+  ];
+
+  const stitched =
+    stitchArrays({
+      link: linkOrder,
+      stats: statsOrder,
+    });
+
+  for (const [index, {link}] of stitched.entries()) {
+    const next = atOffset(stitched, index, +1, {wrap: true});
+
+    link.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+
+      cssProp(link, 'display', 'none');
+      cssProp(next.link, 'display', null);
+
+      if (next.stats === null) {
+        hideArtTagNetworkStats();
+      } else {
+        showArtTagNetworkStats(next.stats);
+      }
+    });
+  }
+}
+
+function showArtTagNetworkStats(stats) {
+  for (const tagElement of info.tagsWithoutStats) {
+    cssProp(tagElement, 'display', 'none');
+  }
+
+  for (const tagElement of info.tagsWithStats) {
+    cssProp(tagElement, 'display', null);
+  }
+
+  const allStats = [
+    ...info.totalUsesStats,
+    ...info.directUsesStats,
+    ...info.descendantsStats,
+  ];
+
+  const otherStats =
+    allStats
+      .filter(stat => !stats.includes(stat));
+
+  for (const statElement of otherStats) {
+    cssProp(statElement, 'display', 'none');
+  }
+
+  for (const statElement of stats) {
+    cssProp(statElement, 'display', null);
+  }
+}
+
+function hideArtTagNetworkStats() {
+  for (const tagElement of info.tagsWithoutStats) {
+    cssProp(tagElement, 'display', null);
+  }
+
+  for (const tagElement of info.tagsWithStats) {
+    cssProp(tagElement, 'display', 'none');
+  }
+}
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index 63fbc5d6..81ea3415 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -5,6 +5,7 @@ import '../group-contributions-table.js';
 import * as additionalNamesBoxModule from './additional-names-box.js';
 import * as albumCommentarySidebarModule from './album-commentary-sidebar.js';
 import * as artTagGalleryFilterModule from './art-tag-gallery-filter.js';
+import * as artTagNetworkModule from './art-tag-network.js';
 import * as artistExternalLinkTooltipModule from './artist-external-link-tooltip.js';
 import * as cssCompatibilityAssistantModule from './css-compatibility-assistant.js';
 import * as datetimestampTooltipModule from './datetimestamp-tooltip.js';
@@ -26,6 +27,7 @@ export const modules = [
   additionalNamesBoxModule,
   albumCommentarySidebarModule,
   artTagGalleryFilterModule,
+  artTagNetworkModule,
   artistExternalLinkTooltipModule,
   cssCompatibilityAssistantModule,
   datetimestampTooltipModule,
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index b94f8b77..a56bacf2 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -1710,6 +1710,13 @@ listingPage:
         title: "Jump to one of the upper-most tags:"
         item: "{TAG}"
 
+      statLine:
+        _: "Displaying tag info: {STAT}"
+        none: "name only"
+        directUses: "uses (direct only)"
+        totalUses: "uses (total)"
+        descendants: "descendants"
+
       root:
         _: "{TAG}"
 
@@ -1724,6 +1731,13 @@ listingPage:
         _: "{TAG}"
         jumpToRoot: "Jump to: {TAG}"
 
+      tag:
+        _: "{TAG}"
+
+        withStat:
+          _: "{STAT} {TAG}"
+          stat: "({STAT})"
+
       orphanArtTags:
         title: "These tags don't have any descendants or ancestors:"
         item: "{TAG}"