« get me outta code hell

client, content, css: simple group contributions table filter - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/static
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2026-06-10 07:02:20 -0300
committer(quasar) nebula <qznebula@protonmail.com>2026-06-10 07:20:35 -0300
commit9c946df709fbeca15bc6e76435cbe30269a2bd3a (patch)
tree2e2b465fc089aa56d40dc608d0004a409f1b541e /src/static
parent7710949c13b149d40195b4203b8a8234039ef5d6 (diff)
client, content, css: simple group contributions table filter
Diffstat (limited to 'src/static')
-rw-r--r--src/static/css/features.css37
-rw-r--r--src/static/js/client-util.js8
-rw-r--r--src/static/js/client/group-contributions-table.js123
-rw-r--r--src/static/js/client/index.js2
4 files changed, 169 insertions, 1 deletions
diff --git a/src/static/css/features.css b/src/static/css/features.css
index 2d54fe7a..f0bd1425 100644
--- a/src/static/css/features.css
+++ b/src/static/css/features.css
@@ -1260,12 +1260,49 @@
       padding-right: 1.5ch;
     }
 
+    th:nth-child(1) { text-align: left; }
     td.group { padding-left: 3ch; }
     td.count { text-align: center; }
     td.duration { text-align: right; }
   }
 }
 
+@layer interaction {
+  .group-contributions-table {
+    .group a {
+      position: relative;
+      text-decoration: underline;
+      text-decoration-style: dotted;
+    }
+  }
+
+  .group-contributions-table:has(.group a.selected) {
+    tr:has(.group a:not(.selected)) {
+      td { opacity: 0.65; }
+      td.group { opacity: 0.8; }
+    }
+
+    .group a.selected {
+      text-decoration-style: solid;
+    }
+
+    .group a.selected::before {
+      content: "";
+
+      position: absolute;
+      display: inline-block;
+      top: 50%; transform: translateY(-50%);
+      left: -1.75ch;
+
+      width: 0.4em;
+      height: 0.4em;
+      border-radius: 100000cm;
+
+      background: var(--primary-color);
+    }
+  }
+}
+
 /* Image and media containers */
 
 @layer layout {
diff --git a/src/static/js/client-util.js b/src/static/js/client-util.js
index de54945c..8ad31b90 100644
--- a/src/static/js/client-util.js
+++ b/src/static/js/client-util.js
@@ -16,9 +16,15 @@ export function rebase(href, rebaseKey = 'rebaseLocalized') {
 
 export function cssProp(el, ...args) {
   if (typeof args[0] === 'string' && args.length === 1) {
-    return getComputedStyle(el).getPropertyValue(args[0]).trim();
+    if (el) {
+      return getComputedStyle(el).getPropertyValue(args[0]).trim();
+    } else {
+      return '';
+    }
   }
 
+  if (!el) return;
+
   if (typeof args[0] === 'string' && args.length === 2) {
     if (args[1] === null) {
       el.style.removeProperty(args[0]);
diff --git a/src/static/js/client/group-contributions-table.js b/src/static/js/client/group-contributions-table.js
new file mode 100644
index 00000000..3b79f84d
--- /dev/null
+++ b/src/static/js/client/group-contributions-table.js
@@ -0,0 +1,123 @@
+import {cssProp} from '../client-util.js';
+import {stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'groupContributionsInfo',
+
+  tables: null,
+  lists: null,
+
+  groupLinks: null,
+  groupLinkDirectories: null,
+
+  chunkDTs: null,
+  chunkDDs: null,
+  chunkGroupDirectories: null,
+};
+
+export function getPageReferences() {
+  if (document.documentElement.dataset.urlKey !== 'localized.artist') {
+    return;
+  }
+
+  info.tables =
+    Array.from(document.querySelectorAll('.group-contributions-table'));
+
+  info.lists =
+    info.tables
+      .map(table => table.closest('dl'));
+
+  info.groupLinks =
+    info.tables
+      .map(table => Array.from(table.querySelectorAll('td.group a')));
+
+  info.groupLinkDirectories =
+    info.groupLinks
+      .map(links => links
+        .map(link => link.dataset.directory));
+
+  info.chunkDTs =
+    info.lists
+      .map(list => Array.from(list.querySelectorAll('dt')));
+
+  info.chunkDDs =
+    info.chunkDTs
+      .map(dts => dts
+        .map(dt => dt.nextElementSibling)
+        .map(el => el.tagName === 'DD' ? el : null));
+
+  info.chunkGroupDirectories =
+    info.chunkDTs
+      .map(dts => dts
+        .map(dt => dt.dataset.groups)
+        .map(string => string ? string.split(' ') : []));
+}
+
+export function addPageListeners() {
+  if (!info.tables) return;
+
+  stitchArrays({
+    table: info.tables,
+    groupLinks: info.groupLinks,
+  }).forEach(({table, groupLinks}) => {
+      groupLinks.forEach(groupLink => {
+        groupLink.addEventListener('click', domEvent => {
+          domEvent.preventDefault();
+          handleGroupLinkClicked(table, groupLink);
+        });
+      });
+    });
+}
+
+function handleGroupLinkClicked(table, groupLink) {
+  const i = info.tables.indexOf(table);
+
+  groupLink.classList.toggle('selected');
+
+  // For now, just disable having more than one link selected at a time.
+  for (const link of info.groupLinks[i]) {
+    if (link !== groupLink) {
+      link.classList.remove('selected');
+    }
+  }
+
+  updateVisibleChunks(table);
+}
+
+function updateVisibleChunks(table) {
+  const i = info.tables.indexOf(table);
+
+  const selectedGroupDirectories =
+    stitchArrays({
+      link: info.groupLinks[i],
+      directory: info.groupLinkDirectories[i],
+    }).filter(({link}) => link.classList.contains('selected'))
+      .map(({directory}) => directory);
+
+  stitchArrays({
+    chunkDT: info.chunkDTs[i],
+    chunkDD: info.chunkDDs[i],
+    chunkGroupDirectories: info.chunkGroupDirectories[i],
+  }).forEach(({
+      chunkDT,
+      chunkDD,
+      chunkGroupDirectories,
+    }) => {
+      if (selectedGroupDirectories.length >= 1) {
+        const included =
+          chunkGroupDirectories
+            .some(d => selectedGroupDirectories.includes(d));
+
+        if (included) {
+          cssProp(chunkDT, 'display', null);
+          cssProp(chunkDD, 'display', null);
+        } else {
+          cssProp(chunkDT, 'display', 'none');
+          cssProp(chunkDD, 'display', 'none');
+        }
+      } else {
+        cssProp(chunkDT, 'display', null);
+        cssProp(chunkDD, 'display', null);
+      }
+    });
+}
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index a17f7dab..a438d6d8 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -8,6 +8,7 @@ import * as datetimestampTooltipModule from './datetimestamp-tooltip.js';
 import * as draggedLinkModule from './dragged-link.js';
 import * as expandableGridSectionModule from './expandable-grid-section.js';
 import * as galleryStyleSelectorModule from './gallery-style-selector.js';
+import * as groupContributionsTableModule from './group-contributions-table.js';
 import * as hashLinkModule from './hash-link.js';
 import * as hoverableTooltipModule from './hoverable-tooltip.js';
 import * as imageOverlayModule from './image-overlay.js';
@@ -33,6 +34,7 @@ export const modules = [
   draggedLinkModule,
   expandableGridSectionModule,
   galleryStyleSelectorModule,
+  groupContributionsTableModule,
   hashLinkModule,
   hoverableTooltipModule,
   imageOverlayModule,