diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/content/dependencies/generateGroupGalleryPage.js | 169 | ||||
-rw-r--r-- | src/content/dependencies/generateGroupInfoPage.js | 170 | ||||
-rw-r--r-- | src/content/dependencies/generateGroupNavLinks.js | 142 | ||||
-rw-r--r-- | src/content/dependencies/generateGroupSidebar.js | 35 | ||||
-rw-r--r-- | src/content/dependencies/generateGroupSidebarCategoryDetails.js | 77 | ||||
-rw-r--r-- | src/content/dependencies/linkGroupExtra.js | 34 | ||||
-rw-r--r-- | src/page/group.js | 315 | ||||
-rw-r--r-- | src/page/index.js | 2 |
8 files changed, 646 insertions, 298 deletions
diff --git a/src/content/dependencies/generateGroupGalleryPage.js b/src/content/dependencies/generateGroupGalleryPage.js new file mode 100644 index 00000000..ab8a06ae --- /dev/null +++ b/src/content/dependencies/generateGroupGalleryPage.js @@ -0,0 +1,169 @@ +import { + getTotalDuration, + sortChronologically, +} from '../../util/wiki-data.js'; + +export default { + contentDependencies: [ + 'generateColorStyleRules', + 'generateCoverGrid', + 'generateGroupNavLinks', + 'generateGroupSidebar', + 'generatePageLayout', + 'image', + 'linkAlbum', + 'linkListing', + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({listingSpec, wikiInfo}) { + const sprawl = {}; + sprawl.enableGroupUI = wikiInfo.enableGroupUI; + + if (wikiInfo.enableListings && wikiInfo.enableGroupUI) { + sprawl.groupsByCategoryListing = + listingSpec + .find(l => l.directory === 'groups/by-category'); + } + + return sprawl; + }, + + relations(relation, sprawl, group) { + const relations = {}; + + const albums = + sortChronologically(group.albums.slice(), {latestFirst: true}); + + relations.layout = + relation('generatePageLayout'); + + relations.navLinks = + relation('generateGroupNavLinks', group); + + if (sprawl.enableGroupUI) { + relations.sidebar = + relation('generateGroupSidebar', group); + } + + relations.colorStyleRules = + relation('generateColorStyleRules', group.color); + + if (sprawl.groupsByCategoryListing) { + relations.groupListingLink = + relation('linkListing', sprawl.groupsByCategoryListing); + } + + relations.coverGrid = + relation('generateCoverGrid'); + + relations.links = + albums + .map(album => relation('linkAlbum', album)); + + relations.images = + albums.map(album => + (album.hasCoverArt + ? relation('image', album.artTags) + : relation('iamge'))); + + return relations; + }, + + data(sprawl, group) { + const albums = + sortChronologically(group.albums.slice(), {latestFirst: true}); + + const tracks = albums.flatMap((album) => album.tracks); + const totalDuration = getTotalDuration(tracks, {originalReleasesOnly: true}); + + return { + name: group.name, + + numAlbums: albums.length, + numTracks: tracks.length, + totalDuration, + + names: albums.map(album => album.name), + paths: albums.map(album => + (album.hasCoverArt + ? ['media.albumCover', album.directory, album.coverArtFileExtension] + : null)), + }; + }, + + generate(data, relations, {html, language}) { + return relations.layout + .slots({ + title: language.$('groupGalleryPage.title', {group: data.name}), + headingMode: 'static', + + colorStyleRules: [relations.colorStyleRules], + + mainClasses: ['top-index'], + mainContent: [ + /* + getCarouselHTML({ + items: group.featuredAlbums.slice(0, 12 + 1), + srcFn: getAlbumCover, + linkFn: link.album, + }), + */ + + html.tag('p', + {class: 'quick-info'}, + language.$('groupGalleryPage.infoLine', { + tracks: html.tag('b', + language.countTracks(data.numTracks, { + unit: true, + })), + albums: html.tag('b', + language.countAlbums(data.numAlbums, { + unit: true, + })), + time: html.tag('b', + language.formatDuration(data.totalDuration, { + unit: true, + })), + })), + + relations.groupListingLink && + html.tag('p', + {class: 'quick-info'}, + language.$('groupGalleryPage.anotherGroupLine', { + link: + relations.groupListingLink + .slot('content', language.$('groupGalleryPage.anotherGroupLine.link')), + })), + + relations.coverGrid + .slots({ + links: relations.links, + names: data.names, + images: + relations.images.map((image, i) => + image.slots({ + path: data.paths[i], + missingSourceContent: + language.$('misc.albumGrid.noCoverArt', { + album: data.names[i], + }), + })), + }), + ], + + ...( + relations.sidebar + ?.slot('currentExtra', 'gallery') + ?.content + ?? {}), + + navLinkStyle: 'hierarchical', + navLinks: + relations.navLinks + .slot('currentExtra', 'gallery') + .content, + }); + }, +}; diff --git a/src/content/dependencies/generateGroupInfoPage.js b/src/content/dependencies/generateGroupInfoPage.js new file mode 100644 index 00000000..3cffb748 --- /dev/null +++ b/src/content/dependencies/generateGroupInfoPage.js @@ -0,0 +1,170 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generateColorStyleRules', + 'generateContentHeading', + 'generateGroupNavLinks', + 'generateGroupSidebar', + 'generatePageLayout', + 'linkAlbum', + 'linkExternal', + 'linkGroupGallery', + 'linkGroup', + 'transformContent', + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({wikiInfo}) { + return { + enableGroupUI: wikiInfo.enableGroupUI, + }; + }, + + relations(relation, sprawl, group) { + const relations = {}; + const sec = relations.sections = {}; + + relations.layout = + relation('generatePageLayout'); + + relations.navLinks = + relation('generateGroupNavLinks', group); + + if (sprawl.enableGroupUI) { + relations.sidebar = + relation('generateGroupSidebar', group); + } + + relations.colorStyleRules = + relation('generateColorStyleRules', group.color); + + sec.info = {}; + + if (!empty(group.urls)) { + sec.info.visitLinks = + group.urls + .map(url => relation('linkExternal', url)); + } + + if (group.description) { + sec.info.description = + relation('transformContent', group.description); + } + + if (!empty(group.albums)) { + sec.albums = {}; + + sec.albums.heading = + relation('generateContentHeading'); + + sec.albums.galleryLink = + relation('linkGroupGallery', group); + + sec.albums.entries = + group.albums.map(album => { + const links = {}; + links.albumLink = relation('linkAlbum', album); + + const otherGroup = album.groups.find(g => g !== group); + if (otherGroup) { + links.groupLink = relation('linkGroup', otherGroup); + } + + return links; + }); + } + + return relations; + }, + + data(sprawl, group) { + const data = {}; + + data.name = group.name; + + if (!empty(group.albums)) { + data.albumYears = + group.albums + .map(album => album.date?.getFullYear()); + } + + return data; + }, + + generate(data, relations, {html, language}) { + const {sections: sec} = relations; + + return relations.layout + .slots({ + title: language.$('groupInfoPage.title', {group: data.name}), + headingMode: 'sticky', + + colorStyleRules: [relations.colorStyleRules], + + mainContent: [ + sec.info.visitLinks && + html.tag('p', + language.$('releaseInfo.visitOn', { + links: language.formatDisjunctionList(sec.info.visitLinks), + })), + + html.tag('blockquote', + {[html.onlyIfContent]: true}, + sec.info.description + ?.slot('mode', 'multiline')), + + sec.albums && [ + sec.albums.heading + .slots({ + tag: 'h2', + title: language.$('groupInfoPage.albumList.title'), + }), + + html.tag('p', + language.$('groupInfoPage.viewAlbumGallery', { + link: + sec.albums.galleryLink + .slot('content', language.$('groupInfoPage.viewAlbumGallery.link')), + })), + + html.tag('ul', + sec.albums.entries.map(({albumLink, groupLink}, index) => { + // All these strings are really jank, and should probably + // be implemented with the same 'const parts = [], opts = {}' + // form used elsewhere... + const year = data.albumYears[index]; + const item = + (year + ? language.$('groupInfoPage.albumList.item', { + year, + album: albumLink, + }) + : language.$('groupInfoPage.albumList.item.withoutYear', { + album: albumLink, + })); + + return html.tag('li', + (groupLink + ? language.$('groupInfoPage.albumList.item.withAccent', { + item, + accent: + html.tag('span', {class: 'other-group-accent'}, + language.$('groupInfoPage.albumList.item.otherGroupAccent', { + group: + groupLink.slot('color', false), + })), + }) + : item)); + })), + ], + ], + + ...relations.sidebar?.content ?? {}, + + navLinkStyle: 'hierarchical', + navLinks: relations.navLinks.content, + }); + }, +}; diff --git a/src/content/dependencies/generateGroupNavLinks.js b/src/content/dependencies/generateGroupNavLinks.js new file mode 100644 index 00000000..0b525363 --- /dev/null +++ b/src/content/dependencies/generateGroupNavLinks.js @@ -0,0 +1,142 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generatePreviousNextLinks', + 'linkGroup', + 'linkGroupGallery', + 'linkGroupExtra', + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({groupCategoryData, wikiInfo}) { + return { + groupCategoryData, + enableGroupUI: wikiInfo.enableGroupUI, + enableListings: wikiInfo.enableListings, + }; + }, + + relations(relation, sprawl, group) { + if (!sprawl.enableGroupUI) { + return {}; + } + + const relations = {}; + + relations.mainLink = + relation('linkGroup', group); + + relations.previousNextLinks = + relation('generatePreviousNextLinks'); + + const groups = sprawl.groupCategoryData + .flatMap(category => category.groups); + + const index = groups.indexOf(group); + + if (index > 0) { + relations.previousLink = + relation('linkGroupExtra', groups[index - 1]); + } + + if (index < groups.length - 1) { + relations.nextLink = + relation('linkGroupExtra', groups[index + 1]); + } + + relations.infoLink = + relation('linkGroup', group); + + if (!empty(group.albums)) { + relations.galleryLink = + relation('linkGroupGallery', group); + } + + return relations; + }, + + data(sprawl) { + return { + enableGroupUI: sprawl.enableGroupUI, + enableListings: sprawl.enableListings, + }; + }, + + slots: { + showExtraLinks: {type: 'boolean', default: false}, + + currentExtra: { + validate: v => v.is('gallery'), + }, + }, + + generate(data, relations, slots, {language}) { + if (!data.enableGroupUI) { + return [ + {auto: 'home'}, + {auto: 'current'}, + ]; + } + + const previousNextLinks = + (relations.previousLink || relations.nextLink) && + relations.previousNextLinks.slots({ + previousLink: + relations.previousLink + ?.slot('extra', slots.currentExtra) + ?.content + ?? null, + nextLink: + relations.nextLink + ?.slot('extra', slots.currentExtra) + ?.content + ?? null, + }); + + const previousNextPart = + previousNextLinks && + language.formatUnitList( + previousNextLinks.content.filter(Boolean)); + + const infoLink = + relations.infoLink.slots({ + attributes: {class: slots.currentExtra === null && 'current'}, + content: language.$('misc.nav.info'), + }); + + const extraLinks = [ + relations.galleryLink?.slots({ + attributes: {class: slots.currentExtra === 'gallery' && 'current'}, + content: language.$('misc.nav.gallery'), + }), + ]; + + const extrasPart = + (empty(extraLinks) + ? '' + : language.formatUnitList([infoLink, ...extraLinks])); + + const accent = + `(${[extrasPart, previousNextPart].filter(Boolean).join('; ')})`; + + return [ + {auto: 'home'}, + + data.enableListings && + { + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), + }, + + { + accent, + html: + language.$('groupPage.nav.group', { + group: relations.mainLink, + }), + }, + ].filter(Boolean); + }, +}; diff --git a/src/content/dependencies/generateGroupSidebar.js b/src/content/dependencies/generateGroupSidebar.js new file mode 100644 index 00000000..6baf37f4 --- /dev/null +++ b/src/content/dependencies/generateGroupSidebar.js @@ -0,0 +1,35 @@ +export default { + contentDependencies: ['generateGroupSidebarCategoryDetails'], + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({groupCategoryData}) { + return {groupCategoryData}; + }, + + relations(relation, sprawl, group) { + return { + categoryDetails: + sprawl.groupCategoryData.map(category => + relation('generateGroupSidebarCategoryDetails', category, group)), + }; + }, + + slots: { + currentExtra: { + validate: v => v.is('gallery'), + }, + }, + + generate(relations, slots, {html, language}) { + return { + leftSidebarContent: [ + html.tag('h1', + language.$('groupSidebar.title')), + + relations.categoryDetails + .map(details => + details.slot('currentExtra', slots.currentExtra)), + ], + }; + }, +}; diff --git a/src/content/dependencies/generateGroupSidebarCategoryDetails.js b/src/content/dependencies/generateGroupSidebarCategoryDetails.js new file mode 100644 index 00000000..ec707e39 --- /dev/null +++ b/src/content/dependencies/generateGroupSidebarCategoryDetails.js @@ -0,0 +1,77 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generateColorStyleVariables', + 'linkGroup', + 'linkGroupGallery', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, category) { + return { + colorVariables: relation('generateColorStyleVariables', category.color), + + // Which of these is used depends on the currentExtra slot, so all + // available links are included here. + groupLinks: category.groups.map(group => { + const links = {}; + links.info = relation('linkGroup', group); + + if (!empty(group.albums)) { + links.gallery = relation('linkGroupGallery', group); + } + + return links; + }), + }; + }, + + data(category, group) { + const data = {}; + + data.name = category.name; + data.isCurrentCategory = category === group.category; + + if (data.isCurrentCategory) { + data.currentGroupIndex = category.groups.indexOf(group); + } + + return data; + }, + + slots: { + currentExtra: { + validate: v => v.is('gallery'), + }, + }, + + generate(data, relations, slots, {html, language}) { + return html.tag('details', + { + open: data.isCurrentCategory, + class: data.isCurrentCategory && 'current', + }, + [ + html.tag('summary', + {style: relations.colorVariables}, + html.tag('span', + language.$('groupSidebar.groupList.category', { + category: + html.tag('span', {class: 'group-name'}, + data.name), + }))), + + html.tag('ul', + relations.groupLinks.map((links, index) => + html.tag('li', + {class: index === data.currentGroupIndex && 'current'}, + language.$('groupSidebar.groupList.item', { + group: + links[slots.currentExtra ?? 'info'] ?? + links.info, + })))), + ]); + }, +}; diff --git a/src/content/dependencies/linkGroupExtra.js b/src/content/dependencies/linkGroupExtra.js new file mode 100644 index 00000000..ee6a3b1d --- /dev/null +++ b/src/content/dependencies/linkGroupExtra.js @@ -0,0 +1,34 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkGroup', + 'linkGroupGallery', + ], + + extraDependencies: ['html'], + + relations(relation, group) { + const relations = {}; + + relations.info = + relation('linkGroup', group); + + if (!empty(group.albums)) { + relations.gallery = + relation('linkGroupGallery', group); + } + + return relations; + }, + + slots: { + extra: { + validate: v => v.is('gallery'), + }, + }, + + generate(relations, slots) { + return relations[slots.extra ?? 'info'] ?? relations.info; + }, +}; diff --git a/src/page/group.js b/src/page/group.js index 81e1728d..4d5f91c8 100644 --- a/src/page/group.js +++ b/src/page/group.js @@ -15,307 +15,28 @@ export function targets({wikiData}) { return wikiData.groupData; } -export function write(group, {wikiData}) { - const {listingSpec, wikiInfo} = wikiData; +export function pathsForTarget(group) { + const hasGalleryPage = !empty(group.albums); - const tracks = group.albums.flatMap((album) => album.tracks); - const totalDuration = getTotalDuration(tracks, {originalReleasesOnly: true}); + return [ + { + type: 'page', + path: ['groupInfo', group.directory], - const albumLines = group.albums.map((album) => ({ - album, - otherGroup: album.groups.find((g) => g !== group), - })); - - const infoPage = { - type: 'page', - path: ['groupInfo', group.directory], - page: ({ - fancifyURL, - generateInfoGalleryLinks, - generateNavigationLinks, - getLinkThemeString, - getThemeString, - html, - language, - link, - transformMultiline, - }) => ({ - title: language.$('groupInfoPage.title', {group: group.name}), - - themeColor: group.color, - theme: getThemeString(group.color), - - main: { - headingMode: 'sticky', - - content: [ - !empty(group.urls) && - html.tag('p', - language.$('releaseInfo.visitOn', { - links: language.formatDisjunctionList( - group.urls.map(url => fancifyURL(url, {language}))), - })), - - group.description && - html.tag('blockquote', - transformMultiline(group.description)), - - ...html.fragment( - !empty(group.albums) && [ - html.tag('h2', - {class: ['content-heading']}, - language.$('groupInfoPage.albumList.title')), - - html.tag('p', - language.$('groupInfoPage.viewAlbumGallery', { - link: link.groupGallery(group, { - text: language.$('groupInfoPage.viewAlbumGallery.link'), - }), - })), - - html.tag('ul', - albumLines.map(({album, otherGroup}) => { - const item = album.date - ? language.$('groupInfoPage.albumList.item', { - year: album.date.getFullYear(), - album: link.album(album), - }) - : language.$('groupInfoPage.albumList.item.withoutYear', { - album: link.album(album), - }); - return html.tag('li', - otherGroup - ? language.$('groupInfoPage.albumList.item.withAccent', { - item, - accent: html.tag('span', - {class: 'other-group-accent'}, - language.$('groupInfoPage.albumList.item.otherGroupAccent', { - group: link.groupInfo(otherGroup, { - color: false, - }), - })), - }) - : item); - })), - ]), - ], - }, - - sidebarLeft: generateGroupSidebar(group, false, { - getLinkThemeString, - html, - language, - link, - wikiData, - }), - - nav: generateGroupNav(group, false, { - generateInfoGalleryLinks, - generateNavigationLinks, - language, - link, - wikiData, - }), - }), - }; - - const galleryPage = !empty(group.albums) && { - type: 'page', - path: ['groupGallery', group.directory], - page: ({ - generateInfoGalleryLinks, - generateNavigationLinks, - getAlbumCover, - getAlbumGridHTML, - getCarouselHTML, - getLinkThemeString, - getThemeString, - html, - language, - link, - }) => ({ - title: language.$('groupGalleryPage.title', {group: group.name}), - - themeColor: group.color, - theme: getThemeString(group.color), - - main: { - classes: ['top-index'], - headingMode: 'static', - - content: [ - getCarouselHTML({ - items: group.featuredAlbums.slice(0, 12 + 1), - srcFn: getAlbumCover, - linkFn: link.album, - }), - - html.tag('p', - {class: 'quick-info'}, - language.$('groupGalleryPage.infoLine', { - tracks: html.tag('b', - language.countTracks(tracks.length, { - unit: true, - })), - albums: html.tag('b', - language.countAlbums(group.albums.length, { - unit: true, - })), - time: html.tag('b', - language.formatDuration(totalDuration, { - unit: true, - })), - })), - - wikiInfo.enableGroupUI && - wikiInfo.enableListings && - html.tag('p', - {class: 'quick-info'}, - language.$('groupGalleryPage.anotherGroupLine', { - link: link.listing( - listingSpec.find(l => l.directory === 'groups/by-category'), - { - text: language.$('groupGalleryPage.anotherGroupLine.link'), - }), - })), - - html.tag('div', - {class: 'grid-listing'}, - getAlbumGridHTML({ - entries: sortChronologically( - group.albums - .filter(album => album.isListedInGalleries) - .map(album => ({ - item: album, - directory: album.directory, - name: album.name, - date: album.date, - })) - ).reverse(), - details: true, - })), - ], + contentFunction: { + name: 'generateGroupInfoPage', + args: [group], }, + }, - sidebarLeft: generateGroupSidebar(group, true, { - getLinkThemeString, - html, - language, - link, - wikiData, - }), + hasGalleryPage && { + type: 'page', + path: ['groupGallery', group.directory], - nav: generateGroupNav(group, true, { - generateInfoGalleryLinks, - generateNavigationLinks, - language, - link, - wikiData, - }), - }), - }; - - return [infoPage, galleryPage].filter(Boolean); -} - -// Utility functions - -function generateGroupSidebar(currentGroup, isGallery, { - getLinkThemeString, - html, - language, - link, - wikiData, -}) { - const {groupCategoryData, wikiInfo} = wikiData; - - if (!wikiInfo.enableGroupUI) { - return null; - } - - return { - content: [ - html.tag('h1', - language.$('groupSidebar.title')), - - ...groupCategoryData.map((category) => - html.tag('details', - { - open: category === currentGroup.category, - class: category === currentGroup.category && 'current', - }, - [ - html.tag('summary', - {style: getLinkThemeString(category.color)}, - html.tag('span', - language.$('groupSidebar.groupList.category', { - category: `<span class="group-name">${category.name}</span>`, - }))), - html.tag('ul', - category.groups.map((group) => { - const linkKey = ( - isGallery && !empty(group.albums) - ? 'groupGallery' - : 'groupInfo'); - - return html.tag('li', - { - class: group === currentGroup && 'current', - style: getLinkThemeString(group.color), - }, - language.$('groupSidebar.groupList.item', { - group: link[linkKey](group), - })); - })), - ])), - ], - }; -} - -function generateGroupNav(currentGroup, isGallery, { - generateInfoGalleryLinks, - generateNavigationLinks, - link, - language, - wikiData, -}) { - const {groupData, wikiInfo} = wikiData; - - if (!wikiInfo.enableGroupUI) { - return {simple: true}; - } - - const linkKey = isGallery ? 'groupGallery' : 'groupInfo'; - - const infoGalleryLinks = generateInfoGalleryLinks(currentGroup, isGallery, { - linkKeyGallery: 'groupGallery', - linkKeyInfo: 'groupInfo', - }); - - const previousNextLinks = generateNavigationLinks(currentGroup, { - data: groupData, - linkKey, - }); - - return { - linkContainerClasses: ['nav-links-hierarchy'], - links: [ - {toHome: true}, - wikiInfo.enableListings && { - path: ['localized.listingIndex'], - title: language.$('listingIndex.title'), - }, - { - html: language.$('groupPage.nav.group', { - group: link[linkKey](currentGroup, {class: 'current'}), - }), - }, - { - divider: false, - html: previousNextLinks - ? `(${infoGalleryLinks}; ${previousNextLinks})` - : `(${previousNextLinks})`, + contentFunction: { + name: 'generateGroupGalleryPage', + args: [group], }, - ], - }; + }, + ]; } diff --git a/src/page/index.js b/src/page/index.js index 311fcd6c..5a622132 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -11,7 +11,7 @@ export * as album from './album.js'; export * as artist from './artist.js'; export * as artistAlias from './artist-alias.js'; // export * as flash from './flash.js'; -// export * as group from './group.js'; +export * as group from './group.js'; // export * as homepage from './homepage.js'; // export * as listing from './listing.js'; // export * as news from './news.js'; |