diff options
author | (quasar) nebula <qznebula@protonmail.com> | 2023-10-13 21:35:05 -0300 |
---|---|---|
committer | (quasar) nebula <qznebula@protonmail.com> | 2025-02-25 20:03:27 -0400 |
commit | fdeb58f2515542c0528ddeaabfbab1b2a895d3d9 (patch) | |
tree | cd14d729675a4bd87b3aeea47c8ba7c20114d899 | |
parent | 3686bdacef66d9bc911b6ad02fdee18cb15ecf56 (diff) |
content: listArtTagNetwork
-rw-r--r-- | src/content/dependencies/listArtTagNetwork.js | 247 | ||||
-rw-r--r-- | src/listing-spec.js | 7 | ||||
-rw-r--r-- | src/strings-default.yaml | 36 |
3 files changed, 289 insertions, 1 deletions
diff --git a/src/content/dependencies/listArtTagNetwork.js b/src/content/dependencies/listArtTagNetwork.js index b3a54747..d0a5f058 100644 --- a/src/content/dependencies/listArtTagNetwork.js +++ b/src/content/dependencies/listArtTagNetwork.js @@ -1 +1,246 @@ -export default {generate() {}}; +import {sortAlphabetically} from '#sort'; +import {empty, stitchArrays, unique} from '#sugar'; + +export default { + contentDependencies: ['generateListingPage', 'linkArtTagInfo'], + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({artTagData}) { + return {artTagData}; + }, + + query(sprawl, spec) { + const artTags = + sprawl.artTagData.filter(artTag => !artTag.isContentWarning); + + const rootArtTags = + artTags + .filter(artTag => !empty(artTag.directDescendantArtTags)) + .filter(artTag => + empty(artTag.directAncestorArtTags) || + artTag.directAncestorArtTags.length >= 2); + + sortAlphabetically(rootArtTags); + + rootArtTags.sort( + ({directAncestorArtTags: ancestorsA}, + {directAncestorArtTags: ancestorsB}) => + ancestorsA.length - ancestorsB.length); + + const recursive = (artTag, asRoot) => { + const descendantNodes = + (empty(artTag.directDescendantArtTags) + ? null + : !asRoot && artTag.directAncestorArtTags.length >= 2 + ? null + : artTag.directDescendantArtTags + .map(artTag => recursive(artTag, false))); + + descendantNodes?.sort( + ({descendantNodes: descendantNodesA}, + {descendantNodes: descendantNodesB}) => + (descendantNodesA ? 1 : 0) + - (descendantNodesB ? 1 : 0)); + + const recursiveGetRootAncestor = ancestorArtTag => + (rootArtTags.includes(ancestorArtTag) + ? ancestorArtTag + : recursiveGetRootAncestor(ancestorArtTag.directAncestorArtTags[0])); + + const ancestorRootArtTags = + (asRoot && !empty(artTag.directAncestorArtTags) + ? unique(artTag.directAncestorArtTags.map(recursiveGetRootAncestor)) + : null); + + return { + artTag, + descendantNodes, + ancestorRootArtTags, + }; + }; + + const uppermostRootTags = + artTags + .filter(artTag => !empty(artTag.directDescendantArtTags)) + .filter(artTag => empty(artTag.directAncestorArtTags)); + + const orphanArtTags = + artTags + .filter(artTag => empty(artTag.directDescendantArtTags)) + .filter(artTag => empty(artTag.directAncestorArtTags)); + + return { + spec, + + rootNodes: + rootArtTags + .map(artTag => recursive(artTag, true)), + + uppermostRootTags, + orphanArtTags, + }; + }, + + relations(relation, query) { + const recursive = queryNode => ({ + artTagLink: + relation('linkArtTagInfo', queryNode.artTag), + + ancestorTagLinks: + queryNode.ancestorRootArtTags + ?.map(artTag => relation('linkArtTagInfo', artTag)) + ?? null, + + descendantNodes: + queryNode.descendantNodes + ?.map(recursive) + ?? null, + }); + + return { + page: + relation('generateListingPage', query.spec), + + rootNodes: + query.rootNodes.map(recursive), + + uppermostRootTagLinks: + query.uppermostRootTags + .map(artTag => relation('linkArtTagInfo', artTag)), + + orphanArtTagLinks: + query.orphanArtTags + .map(artTag => relation('linkArtTagInfo', artTag)), + }; + }, + + data(query) { + const rootArtTags = query.rootNodes.map(({artTag}) => artTag); + + const recursive = queryNode => ({ + directory: + queryNode.artTag.directory, + + representsRoot: + rootArtTags.includes(queryNode.artTag), + + ancestorTagDirectories: + queryNode.ancestorRootArtTags + ?.map(artTag => artTag.directory) + ?? null, + + descendantNodes: + queryNode.descendantNodes + ?.map(recursive) + ?? null, + }); + + return { + rootNodes: + query.rootNodes.map(recursive), + + uppermostRootTagDirectories: + query.uppermostRootTags + .map(artTag => artTag.directory), + }; + }, + + generate(data, relations, {html, language}) { + const prefix = `listingPage.listArtTags.network`; + + const recursive = (dataNode, relationsNode, asRoot) => [ + html.tag('dt', + (asRoot + ? {id: dataNode.directory} + : {}), + + (asRoot + ? (relationsNode.ancestorTagLinks + ? language.$(prefix, 'root.withAncestors', { + tag: relationsNode.artTagLink, + ancestors: + language.formatUnitList( + stitchArrays({ + link: relationsNode.ancestorTagLinks, + directory: dataNode.ancestorTagDirectories, + }).map(({link, directory}) => + link.slots({ + anchor: true, + hash: directory, + }))), + }) + : language.$(prefix, 'root.jumpToTop', { + tag: relationsNode.artTagLink, + link: + html.tag('a', {href: '#top'}, + language.$(prefix, 'root.jumpToTop.link')), + })) + : (dataNode.representsRoot + ? language.$(prefix, 'descendant.jumpToRoot', { + tag: + relationsNode.artTagLink.slots({ + anchor: true, + hash: dataNode.directory, + }), + }) + : language.$(prefix, 'descendant', { + tag: relationsNode.artTagLink, + })))), + + dataNode.descendantNodes && + relationsNode.descendantNodes && + html.tag('dd', + html.tag('dl', + stitchArrays({ + dataNode: dataNode.descendantNodes, + relationsNode: relationsNode.descendantNodes, + }).map(({dataNode, relationsNode}) => + recursive(dataNode, relationsNode, false)))), + ]; + + return relations.page.slots({ + type: 'custom', + + content: [ + html.tag('dl', [ + html.tag('dt', {id: 'top'}, + language.$(prefix, 'jumpToRoot.title')), + + html.tag('dd', + html.tag('ul', + stitchArrays({ + link: relations.uppermostRootTagLinks, + directory: data.uppermostRootTagDirectories, + }).map(({link, directory}) => + html.tag('li', + language.$(prefix, 'jumpToRoot.item', { + tag: + link.slots({ + anchor: true, + hash: directory, + }), + }))))), + + stitchArrays({ + dataNode: data.rootNodes, + relationsNode: relations.rootNodes, + }).map(({dataNode, relationsNode}) => + recursive(dataNode, relationsNode, true)), + + !empty(relations.orphanArtTagLinks) && [ + html.tag('dt', + language.$(prefix, 'orphanArtTags.title')), + + html.tag('dd', + html.tag('ul', + relations.orphanArtTagLinks.map(orphanArtTagLink => + html.tag('li', + language.$(prefix, 'orphanArtTags.item', { + tag: orphanArtTagLink, + }))))), + ], + ]), + ], + }); + }, +}; diff --git a/src/listing-spec.js b/src/listing-spec.js index 024700ea..142c5976 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -209,6 +209,13 @@ listingSpec.push({ }); listingSpec.push({ + directory: 'tags/network', + stringsKey: 'listArtTags.network', + contentFunction: 'listArtTagNetwork', + featureFlag: 'enableArtTagUI', +}); + +listingSpec.push({ directory: 'all-sheet-music-files', stringsKey: 'other.allSheetMusic', contentFunction: 'listAllSheetMusicFiles', diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 3e49197b..0765e1ee 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -1667,6 +1667,42 @@ listingPage: title.short: "...by Uses" item: "{TAG} ({TIMES_USED})" + # listArtTags.network: + # List art tags in a custom networked fashion, showing all + # connections between ancestors and descendants. Each top-level + # tag gets one section. Descendants are generally nested directly + # under their ancestors, except if they have two or more direct + # ancestors *and* at least one direct descendant, in which case + # they're moved to a dedicated section further down the page. + # "Orphan" art tags, if any - tags which don't have any ancestors + # nor any descendants - are displayed in a section at the bottom. + + network: + title: "Art Tag Network" + title.short: "Art Tag Network" + + jumpToRoot: + title: "Jump to one of the upper-most tags:" + item: "{TAG}" + + root: + _: "{TAG}" + + root.jumpToTop: + _: "{TAG} ({LINK})" + link: "Jump back to top" + + root.withAncestors: + _: "{TAG} (descends from {ANCESTORS})" + + descendant: + _: "{TAG}" + jumpToRoot: "Jump to: {TAG}" + + orphanArtTags: + title: "These tags don't have any descendants or ancestors:" + item: "{TAG}" + listArtists: # listArtists.byName: |