From 9c946df709fbeca15bc6e76435cbe30269a2bd3a Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Wed, 10 Jun 2026 07:02:20 -0300 Subject: client, content, css: simple group contributions table filter --- .../generateArtistGroupContributionsInfo.js | 11 +- .../generateArtistInfoPageAdditionalFilesChunk.js | 2 +- .../generateArtistInfoPageArtworksChunk.js | 2 +- .../dependencies/generateArtistInfoPageChunk.js | 13 ++- .../generateArtistInfoPageFlashesChunk.js | 2 +- .../generateArtistInfoPageMusicVideosChunk.js | 2 +- .../generateArtistInfoPageTracksChunk.js | 2 +- src/static/css/features.css | 37 +++++++ src/static/js/client-util.js | 8 +- src/static/js/client/group-contributions-table.js | 123 +++++++++++++++++++++ src/static/js/client/index.js | 2 + 11 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 src/static/js/client/group-contributions-table.js diff --git a/src/content/dependencies/generateArtistGroupContributionsInfo.js b/src/content/dependencies/generateArtistGroupContributionsInfo.js index 3c6187f9..f9a06d4a 100644 --- a/src/content/dependencies/generateArtistGroupContributionsInfo.js +++ b/src/content/dependencies/generateArtistGroupContributionsInfo.js @@ -66,6 +66,10 @@ export default { }), data: (query) => ({ + groupDirectories: + query.groups + .map(group => group.directory), + hasCountColumn: true, @@ -117,13 +121,16 @@ export default { stitchArrays({ link: relations.groupLinks, + directory: data.groupDirectories, changesCategory: data.groupsChangeCategory, count: data.groupCounts, duration: data.groupDurations, - }).map(({link, changesCategory, count, duration}) => + }).map(({link, directory, changesCategory, count, duration}) => html.tag('tr', changesCategory && {class: 'split'}, [ html.tag('td', {class: 'group'}, - link), + link.slots({ + attributes: {'data-directory': directory}, + })), data.hasCountColumn && html.tag('td', {class: 'count'}, diff --git a/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js index 53bc103f..2159c5e9 100644 --- a/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js +++ b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js @@ -1,7 +1,7 @@ export default { relations: (relation, artist, album, contribs) => ({ template: - relation('generateArtistInfoPageChunk'), + relation('generateArtistInfoPageChunk', album), albumLink: relation('linkAlbum', album), diff --git a/src/content/dependencies/generateArtistInfoPageArtworksChunk.js b/src/content/dependencies/generateArtistInfoPageArtworksChunk.js index f98b1e85..2519a0c7 100644 --- a/src/content/dependencies/generateArtistInfoPageArtworksChunk.js +++ b/src/content/dependencies/generateArtistInfoPageArtworksChunk.js @@ -1,7 +1,7 @@ export default { relations: (relation, album, contribs) => ({ template: - relation('generateArtistInfoPageChunk'), + relation('generateArtistInfoPageChunk', album), albumLink: relation('linkAlbum', album), diff --git a/src/content/dependencies/generateArtistInfoPageChunk.js b/src/content/dependencies/generateArtistInfoPageChunk.js index e19030c9..aadafb08 100644 --- a/src/content/dependencies/generateArtistInfoPageChunk.js +++ b/src/content/dependencies/generateArtistInfoPageChunk.js @@ -1,6 +1,13 @@ import {empty} from '#sugar'; export default { + data: (thing) => ({ + groupDirectories: + (thing && thing.groups + ? thing.groups.map(group => group.directory) + : null), + }), + slots: { mode: { validate: v => v.is('flash', 'album'), @@ -26,7 +33,7 @@ export default { durationApproximate: {type: 'boolean'}, }, - generate(slots, {html, language}) { + generate(data, slots, {html, language}) { let earliestItemDate = null; let latestItemDate = null; let onlyItemDate = null; @@ -96,6 +103,10 @@ export default { return html.tags([ html.tag('dt', slots.id && {id: slots.id}, + + data.groupDirectories && + {'data-groups': data.groupDirectories.join(' ')}, + accentedLink), html.tag('dd', slots.list), diff --git a/src/content/dependencies/generateArtistInfoPageFlashesChunk.js b/src/content/dependencies/generateArtistInfoPageFlashesChunk.js index 733c8fa4..7e98ee06 100644 --- a/src/content/dependencies/generateArtistInfoPageFlashesChunk.js +++ b/src/content/dependencies/generateArtistInfoPageFlashesChunk.js @@ -1,7 +1,7 @@ export default { relations: (relation, flashAct, contribs) => ({ template: - relation('generateArtistInfoPageChunk'), + relation('generateArtistInfoPageChunk', flashAct), flashActLink: relation('linkFlashAct', flashAct), diff --git a/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js b/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js index 9ac7debf..2733dbcf 100644 --- a/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js +++ b/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js @@ -1,7 +1,7 @@ export default { relations: (relation, artist, album, contribs) => ({ template: - relation('generateArtistInfoPageChunk'), + relation('generateArtistInfoPageChunk', album), albumLink: relation('linkAlbum', album), diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunk.js b/src/content/dependencies/generateArtistInfoPageTracksChunk.js index 607f1f53..f2cc7456 100644 --- a/src/content/dependencies/generateArtistInfoPageTracksChunk.js +++ b/src/content/dependencies/generateArtistInfoPageTracksChunk.js @@ -63,7 +63,7 @@ export default { relations: (relation, query, artist, album, trackContribLists) => ({ template: - relation('generateArtistInfoPageChunk'), + relation('generateArtistInfoPageChunk', album), albumLink: relation('linkAlbum', album), 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, -- cgit 1.3.0-6-gf8a5