diff options
| author | (quasar) nebula <qznebula@protonmail.com> | 2026-06-10 06:06:39 -0300 |
|---|---|---|
| committer | (quasar) nebula <qznebula@protonmail.com> | 2026-06-10 06:45:19 -0300 |
| commit | 7710949c13b149d40195b4203b8a8234039ef5d6 (patch) | |
| tree | b5cf3e40f379a7e08a12dc54a08cdede03c6902e | |
| parent | f10f5a187f26d08019e452e7fbd417b7f462faa4 (diff) | |
content, css: group contributions table 2
| -rw-r--r-- | src/content/dependencies/generateArtistGroupContributionsInfo.js | 242 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPage.js | 44 | ||||
| -rw-r--r-- | src/static/css/features.css | 36 | ||||
| -rw-r--r-- | src/static/js/client/index.js | 2 | ||||
| -rw-r--r-- | src/static/js/group-contributions-table.js | 33 | ||||
| -rw-r--r-- | src/strings-default.yaml | 23 |
6 files changed, 100 insertions, 280 deletions
diff --git a/src/content/dependencies/generateArtistGroupContributionsInfo.js b/src/content/dependencies/generateArtistGroupContributionsInfo.js index 72ce0944..3c6187f9 100644 --- a/src/content/dependencies/generateArtistGroupContributionsInfo.js +++ b/src/content/dependencies/generateArtistGroupContributionsInfo.js @@ -1,4 +1,4 @@ -import {accumulateSum, empty, stitchArrays, withEntries} from '#sugar'; +import {accumulateSum, stitchArrays, withEntries} from '#sugar'; export default { sprawl: ({groupCategoryData}) => ({ @@ -45,196 +45,94 @@ export default { ([group, things]) => ([group, accumulateSum(things, thing => thing.duration)]))) - const groupsSortedByCount = - allGroupsOrdered - .filter(group => groupToTotalContributions.get(group) > 0) - .sort((a, b) => - (groupToTotalContributions.get(b) - - groupToTotalContributions.get(a))); - - const groupsSortedByDuration = - allGroupsOrdered - .filter(group => groupToTotalDuration.get(group) > 0) - .sort((a, b) => - (groupToTotalDuration.get(b) - - groupToTotalDuration.get(a))); - - const groupCountsSortedByCount = - groupsSortedByCount - .map(group => groupToTotalContributions.get(group)); - - const groupDurationsSortedByCount = - groupsSortedByCount - .map(group => groupToTotalDuration.get(group)); - - const groupDurationsApproximateSortedByCount = - groupsSortedByCount - .map(group => groupToThingsCountedForDuration.get(group).size > 1); - - const groupCountsSortedByDuration = - groupsSortedByDuration - .map(group => groupToTotalContributions.get(group)); - - const groupDurationsSortedByDuration = - groupsSortedByDuration - .map(group => groupToTotalDuration.get(group)); - - const groupDurationsApproximateSortedByDuration = - groupsSortedByDuration - .map(group => groupToThingsCountedForDuration.get(group).size > 1); - return { - groupsSortedByCount, - groupsSortedByDuration, + groups: + allGroupsOrdered, - groupCountsSortedByCount, - groupDurationsSortedByCount, - groupDurationsApproximateSortedByCount, + groupCounts: + allGroupsOrdered + .map(group => groupToTotalContributions.get(group)), - groupCountsSortedByDuration, - groupDurationsSortedByDuration, - groupDurationsApproximateSortedByDuration, + groupDurations: + allGroupsOrdered + .map(group => groupToTotalDuration.get(group)), }; }, relations: (relation, query) => ({ - groupLinksSortedByCount: - query.groupsSortedByCount - .map(group => relation('linkGroup', group)), - - groupLinksSortedByDuration: - query.groupsSortedByDuration + groupLinks: + query.groups .map(group => relation('linkGroup', group)), }), data: (query) => ({ - groupCountsSortedByCount: - query.groupCountsSortedByCount, + hasCountColumn: + true, - groupDurationsSortedByCount: - query.groupDurationsSortedByCount, + hasDurationColumn: + query.groupDurations.some(Boolean), - groupDurationsApproximateSortedByCount: - query.groupDurationsApproximateSortedByCount, + groupsChangeCategory: + query.groups + .map((group, index, groups) => + index >= 1 && + (group.category !== + groups[index - 1].category)), - groupCountsSortedByDuration: - query.groupCountsSortedByDuration, + groupCounts: + query.groupCounts, - groupDurationsSortedByDuration: - query.groupDurationsSortedByDuration, - - groupDurationsApproximateSortedByDuration: - query.groupDurationsApproximateSortedByDuration, + groupDurations: + query.groupDurations, }), slots: { - title: { - type: 'html', - mutable: false, - }, - - showBothColumns: {type: 'boolean'}, - showSortButton: {type: 'boolean'}, - visible: {type: 'boolean', default: true}, - - sort: {validate: v => v.is('count', 'duration')}, - countUnit: {validate: v => v.is('tracks', 'artworks')}, + string: {type: 'string'}, }, generate: (data, relations, slots, {html, language}) => - language.encapsulate('artistPage.groupContributions', capsule => { - if (slots.sort === 'count' && empty(relations.groupLinksSortedByCount)) { - return html.blank(); - } else if (slots.sort === 'duration' && empty(relations.groupLinksSortedByDuration)) { - return html.blank(); - } - - const getCounts = counts => - counts.map(count => { - switch (slots.countUnit) { - case 'tracks': return language.countTracks(count, {unit: true}); - case 'artworks': return language.countArtworks(count, {unit: true}); - } - }); - - // We aren't displaying the "~" approximate symbol here for now. - // The general notion that these sums aren't going to be 100% accurate - // is made clear by the "XYZ has contributed ~1:23:45 hours of music..." - // line that's always displayed above this table. - const getDurations = (durations, approximate) => - stitchArrays({ - duration: durations, - approximate: approximate, - }).map(({duration}) => language.formatDuration(duration)); - - const topLevelClasses = [ - 'group-contributions-sorted-by-' + slots.sort, - slots.visible && 'visible', - ]; - - // TODO: It feels pretty awkward that this component is the only one that - // has enough knowledge to decide if the sort button is even applicable... - const switchingSortPossible = - !empty(relations.groupLinksSortedByCount) && - !empty(relations.groupLinksSortedByDuration); - - return html.tags([ - html.tag('dt', {class: topLevelClasses}, - language.encapsulate(capsule, 'title', capsule => - (switchingSortPossible && slots.showSortButton - ? language.$(capsule, 'withSortButton', { - title: slots.title, - sort: - html.tag('a', {class: 'group-contributions-sort-button'}, - {href: '#'}, - - (slots.sort === 'count' - ? language.$(capsule, 'sorting.count') - : language.$(capsule, 'sorting.duration'))), - }) - : slots.title))), - - html.tag('dd', {class: topLevelClasses}, - html.tag('table', {class: 'group-contributions-table'}, - (stitchArrays( - (slots.sort === 'count' - ? { - group: relations.groupLinksSortedByCount, - count: getCounts(data.groupCountsSortedByCount), - duration: - getDurations( - data.groupDurationsSortedByCount, - data.groupDurationsApproximateSortedByCount), - } - : { - group: relations.groupLinksSortedByDuration, - count: getCounts(data.groupCountsSortedByDuration), - duration: - getDurations( - data.groupDurationsSortedByDuration, - data.groupDurationsApproximateSortedByDuration), - }) - )).map(({group, count, duration}) => - language.encapsulate(capsule, 'item', capsule => - html.tag('tr', [ - html.tag('td', {class: 'group-contributions-link-cell'}, - html.tag('span', group)), - - html.tag('td', {class: 'group-contributions-metrics-cell'}, - (slots.sort === 'count' - // When sorting by count, duration details aren't necessarily - // available for all items. - ? (slots.showBothColumns && duration - ? language.$(capsule, 'countDurationAccent', {count, duration}) - : language.$(capsule, 'countAccent', {count})) - - // Count details are always available, since they're just the - // number of contributions directly. And duration details are - // guaranteed for every item when sorting by duration. - : (slots.showBothColumns - ? language.$(capsule, 'durationCountAccent', {duration, count}) - : language.$(capsule, 'durationAccent', {duration})))), - ]))))), - ]); - }), + language.encapsulate('artistPage.groupContributions', capsule => + html.tag('table', {class: 'group-contributions-table'}, + {[html.onlyIfContent]: true}, + + [ + html.tag('thead', + {[html.onlyIfSiblings]: true}, + + html.tag('tr', [ + html.tag('th', + language.$(capsule, 'title', slots.string)), + + data.hasCountColumn && + html.tag('th', + language.$(capsule, 'column.count', slots.string)), + + data.hasDurationColumn && + html.tag('th', + language.$(capsule, 'column.duration')), + ])), + + html.tag('tbody', + {[html.onlyIfContent]: true}, + + stitchArrays({ + link: relations.groupLinks, + changesCategory: data.groupsChangeCategory, + count: data.groupCounts, + duration: data.groupDurations, + }).map(({link, changesCategory, count, duration}) => + html.tag('tr', changesCategory && {class: 'split'}, [ + html.tag('td', {class: 'group'}, + link), + + data.hasCountColumn && + html.tag('td', {class: 'count'}, + count), + + data.hasDurationColumn && + html.tag('td', {class: 'duration'}, + language.formatDuration(duration)), + ]), + )), + ])), }; diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js index c3ac0b9f..bf4010a2 100644 --- a/src/content/dependencies/generateArtistInfoPage.js +++ b/src/content/dependencies/generateArtistInfoPage.js @@ -285,25 +285,7 @@ export default { relations.tracksChunkedList.slots({ groupInfo: - language.encapsulate(pageCapsule, 'groupContributions', capsule => [ - relations.tracksGroupInfo.clone() - .slots({ - title: language.$(capsule, 'title.music'), - showSortButton: true, - sort: 'count', - countUnit: 'tracks', - visible: true, - }), - - relations.tracksGroupInfo.clone() - .slots({ - title: language.$(capsule, 'title.music'), - showSortButton: true, - sort: 'duration', - countUnit: 'tracks', - visible: false, - }), - ]), + relations.tracksGroupInfo.slot('string', 'tracks'), }), ]), @@ -328,18 +310,10 @@ export default { }), }))), - relations.artworksChunkedList - .slots({ - groupInfo: - language.encapsulate(pageCapsule, 'groupContributions', capsule => - relations.artworksGroupInfo - .slots({ - title: language.$(capsule, 'title.artworks'), - showBothColumns: false, - sort: 'count', - countUnit: 'artworks', - })), - }), + relations.artworksChunkedList.slots({ + groupInfo: + relations.artworksGroupInfo.slot('string', 'artworks'), + }), html.tags([ language.encapsulate(pageCapsule, 'wikiEditArtworks', capsule => @@ -368,13 +342,7 @@ export default { relations.musicVideosChunkedList.slots({ groupInfo: - language.encapsulate(pageCapsule, 'groupContributions', capsule => - relations.musicVideosGroupInfo.slots({ - title: language.$(capsule, 'title.artworks'), - showBothColumns: false, - sort: 'count', - countUnit: 'artworks', - })), + relations.musicVideosGroupInfo.slot('string', 'musicVideos'), }), ]), diff --git a/src/static/css/features.css b/src/static/css/features.css index de615de2..2d54fe7a 100644 --- a/src/static/css/features.css +++ b/src/static/css/features.css @@ -1243,32 +1243,26 @@ .group-contributions-table { margin-left: 40px; border-collapse: collapse; - } - .group-contributions-table td { - padding: 0 0 4px 0; - } + margin-bottom: 1em; - .group-contributions-table .group-contributions-link-cell { - display: list-item; - } + tr.split td { border-top: 2px solid #fff2; } - .group-contributions-table .group-contributions-metrics-cell { - padding-left: 1.5ch; - white-space: nowrap; - text-align: right; - } -} + tr:has(+ .split) td { padding-bottom: 4px; } + tr.split td { padding-top: 4px; } -@layer interaction { - .group-contributions-sorted-by-count:not(.visible), - .group-contributions-sorted-by-duration:not(.visible) { - display: none; - } + td { + padding: 0 0 4px 0; + } - .group-contributions-sort-button { - text-decoration: underline; - text-decoration-style: dotted; + td:not(:last-child), + th:not(:last-child) { + padding-right: 1.5ch; + } + + td.group { padding-left: 3ch; } + td.count { text-align: center; } + td.duration { text-align: right; } } } diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js index 65254b10..a17f7dab 100644 --- a/src/static/js/client/index.js +++ b/src/static/js/client/index.js @@ -1,5 +1,3 @@ -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'; diff --git a/src/static/js/group-contributions-table.js b/src/static/js/group-contributions-table.js deleted file mode 100644 index bef85fad..00000000 --- a/src/static/js/group-contributions-table.js +++ /dev/null @@ -1,33 +0,0 @@ -// TODO: Update to clientSteps style. - -const groupContributionsTableInfo = - Array.from(document.querySelectorAll('#content dl')) - .filter(dl => dl.querySelector('a.group-contributions-sort-button')) - .map(dl => ({ - sortingByCountLink: dl.querySelector('dt.group-contributions-sorted-by-count a.group-contributions-sort-button'), - sortingByDurationLink: dl.querySelector('dt.group-contributions-sorted-by-duration a.group-contributions-sort-button'), - sortingByCountElements: dl.querySelectorAll('.group-contributions-sorted-by-count'), - sortingByDurationElements: dl.querySelectorAll('.group-contributions-sorted-by-duration'), - })); - -function sortGroupContributionsTableBy(info, sort) { - const [showThese, hideThese] = - (sort === 'count' - ? [info.sortingByCountElements, info.sortingByDurationElements] - : [info.sortingByDurationElements, info.sortingByCountElements]); - - for (const element of showThese) element.classList.add('visible'); - for (const element of hideThese) element.classList.remove('visible'); -} - -for (const info of groupContributionsTableInfo) { - info.sortingByCountLink.addEventListener('click', evt => { - evt.preventDefault(); - sortGroupContributionsTableBy(info, 'duration'); - }); - - info.sortingByDurationLink.addEventListener('click', evt => { - evt.preventDefault(); - sortGroupContributionsTableBy(info, 'count'); - }); -} diff --git a/src/strings-default.yaml b/src/strings-default.yaml index cadd2f02..9a5f324e 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -1940,20 +1940,15 @@ artistPage: groupContributions: title: - music: "Contributed music to groups:" - artworks: "Contributed artworks to groups:" - musicVideos: "Contributed to music videos in groups:" - withSortButton: "{TITLE} ({SORT})" - - sorting: - count: "Sorting by count." - duration: "Sorting by duration." - - item: - countAccent: "({COUNT})" - durationAccent: "({DURATION})" - countDurationAccent: "({COUNT} — {DURATION})" - durationCountAccent: "({DURATION} — {COUNT})" + tracks: "Contributed music to group…" + artworks: "Contributed art to group…" + musicVideos: "Contributed to music videos in group…" + + column: + count.tracks: "Tracks" + count.artworks: "Artworks" + count.musicVideos: "Music Videos" + duration: "Duration" trackList.title: "Tracks" artList.title: "Artworks" |