From 357015de21e7e427f25b31a2622fb9182ec292e1 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sat, 15 Apr 2023 14:16:04 -0300 Subject: content: generateAlbumNavLinks, generatePageLayout nav bar --- src/content/dependencies/generateAlbumInfoPage.js | 16 +++ src/content/dependencies/generateAlbumNavLinks.js | 121 +++++++++++++++++++ src/content/dependencies/generatePageLayout.js | 139 +++++++++++++++++++++- src/content/dependencies/generateTrackInfoPage.js | 37 ++++++ src/content/dependencies/linkTemplate.js | 7 ++ src/content/dependencies/linkThing.js | 22 +++- 6 files changed, 336 insertions(+), 6 deletions(-) create mode 100644 src/content/dependencies/generateAlbumNavLinks.js (limited to 'src/content') diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js index 21d5ec87..f5dc6bfd 100644 --- a/src/content/dependencies/generateAlbumInfoPage.js +++ b/src/content/dependencies/generateAlbumInfoPage.js @@ -1,6 +1,7 @@ export default { contentDependencies: [ 'generateAlbumInfoPageContent', + 'generateAlbumNavLinks', 'generateAlbumSidebar', 'generateAlbumSocialEmbed', 'generateAlbumStyleRules', @@ -13,6 +14,7 @@ export default { relations(relation, album) { return { layout: relation('generatePageLayout'), + albumNavLinks: relation('generateAlbumNavLinks', album, null), content: relation('generateAlbumInfoPageContent', album), sidebar: relation('generateAlbumSidebar', album, null), @@ -40,6 +42,20 @@ export default { cover: relations.content.cover, mainContent: relations.content.main.content, + navLinkStyle: 'hierarchical', + navLinks: [ + {auto: 'home'}, + { + auto: 'current', + accent: + relations.albumNavLinks.slots({ + showTrackNavigation: true, + showExtraLinks: true, + }), + }, + ], + navContent: '(Chronology links here)', + ...relations.sidebar, // socialEmbed: relations.socialEmbed, diff --git a/src/content/dependencies/generateAlbumNavLinks.js b/src/content/dependencies/generateAlbumNavLinks.js new file mode 100644 index 00000000..d9645319 --- /dev/null +++ b/src/content/dependencies/generateAlbumNavLinks.js @@ -0,0 +1,121 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkTrack', + 'linkAlbumCommentary', + 'linkAlbumGallery', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, album, track) { + const relations = {}; + + if (track) { + const index = album.tracks.indexOf(track); + + if (index > 0) { + relations.previousTrackLink = + relation('linkTrack', album.tracks[index - 1]); + } + + if (index < album.tracks.length - 1) { + relations.nextTrackLink = + relation('linkTrack', album.tracks[index + 1]); + } + } + + if (album.tracks.some(t => t.hasUniqueCoverArt)) { + relations.albumGalleryLink = + relation('linkAlbumGallery', album); + } + + if (album.commentary || album.tracks.some(t => t.commentary)) { + relations.albumCommentaryLink = + relation('linkAlbumCommentary', album); + } + + return relations; + }, + + data(album, track) { + return { + hasMultipleTracks: album.tracks.length > 1, + isTrackPage: !!track, + }; + }, + + generate(data, relations, {html, language}) { + return html.template({ + annotation: `generateAlbumNavLinks`, + + slots: { + showTrackNavigation: {type: 'boolean', default: false}, + showExtraLinks: {type: 'boolean', default: false}, + + currentExtra: { + validate: v => v.is('gallery', 'commentary'), + }, + }, + + content(slots) { + const extraLinks = + (slots.showExtraLinks + ? [ + relations.albumGalleryLink.slots({ + attributes: {class: slots.currentExtra === 'gallery' && 'current'}, + content: language.$('albumPage.nav.gallery'), + }), + + relations.albumCommentaryLink.slots({ + attributes: {class: slots.currentExtra === 'commentary' && 'current'}, + content: language.$('albumPage.nav.commentary'), + }), + ] + : []); + + const previousNextLinks = + (slots.showTrackNavigation + ? [ + relations.previousTrackLink?.slots({ + tooltip: true, + attributes: {id: 'previous-button'}, + content: language.$('misc.nav.previous'), + }), + relations.nextTrackLink?.slots({ + tooltip: true, + attributes: {id: 'next-button'}, + content: language.$('misc.nav.next'), + }), + ] + : []); + + const randomLink = + slots.showTrackNavigation && + data.hasMultipleTracks && + html.tag('a', + { + href: '#', + 'data-random': 'track-in-album', + id: 'random-button', + }, + (data.isTrackPage + ? language.$('trackPage.nav.random') + : language.$('albumPage.nav.randomTrack'))); + + const allLinks = [ + ...previousNextLinks, + ...extraLinks, + randomLink, + ].filter(Boolean); + + if (empty(allLinks)) { + return html.blank(); + } + + return `(${language.formatUnitList(allLinks)})` + }, + }); + }, +}; diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js index 1ea5ce24..86054437 100644 --- a/src/content/dependencies/generatePageLayout.js +++ b/src/content/dependencies/generatePageLayout.js @@ -1,3 +1,5 @@ +import {empty, openAggregate} from '../../util/sugar.js'; + export default { contentDependencies: [ 'generateFooterLocalizationLinks', @@ -105,6 +107,58 @@ export default { // Nav & Footer + navContent: {type: 'html'}, + navBottomRowContent: {type: 'html'}, + + navLinkStyle: { + validate: v => v.is('hierarchical', 'index'), + default: 'index', + }, + + navLinks: { + validate: v => + v.arrayOf(object => { + v.isObject(object); + + const aggregate = openAggregate({message: `Errors validating navigation link`}); + + aggregate.call(v.validateProperties({ + auto: () => true, + html: () => true, + + path: () => true, + title: () => true, + accent: () => true, + }), object); + + if (object.auto || object.html) { + if (object.auto && object.html) { + aggregate.push(new TypeError(`Don't specify both auto and html`)); + } else if (object.auto) { + aggregate.call(v.is('home', 'current'), object.auto); + } else { + aggregate.call(v.isHTML, object.html); + } + + if (object.path || object.title) { + aggregate.push(new TypeError(`Don't specify path or title along with auto or html`)); + } + } else { + aggregate.call(v.validateProperties({ + path: v.arrayOf(v.isString), + title: v.isString, + }), { + path: object.path, + title: object.title, + }); + } + + aggregate.close(); + + return true; + }) + }, + footerContent: {type: 'html'}, }, @@ -166,6 +220,87 @@ export default { relations.footerLocalizationLinks, ]); + const navHTML = html.tag('nav', + { + [html.onlyIfContent]: true, + id: 'header', + class: [ + !empty(slots.navLinks) && 'nav-has-main-links', + !html.isBlank(slots.navContent) && 'nav-has-content', + !html.isBlank(slots.navBottomRowContent) && 'nav-has-bottom-row', + ], + }, + [ + html.tag('div', + { + [html.onlyIfContent]: true, + class: [ + 'nav-main-links', + 'nav-links-' + slots.navLinkStyle, + ], + }, + slots.navLinks?.map((cur, i) => { + let content; + + if (cur.html) { + content = cur.html; + } else { + let title; + let href; + + switch (cur.auto) { + case 'home': + title = wikiInfo.nameShort; + href = to('localized.home'); + break; + case 'current': + title = slots.title; + href = ''; + break; + case null: + case undefined: + title = cur.title; + href = to(...cur.path); + break; + } + + content = html.tag('a', + {href}, + title); + } + + let className; + + if (cur.auto === 'current') { + className = 'current'; + } else if ( + slots.navLinkStyle === 'hierarchical' && + i === slots.navLinks.length - 1 + ) { + className = 'current'; + } + + return html.tag('span', + {class: className}, + [ + html.tag('span', + {class: 'nav-link-content'}, + content), + html.tag('span', + {[html.onlyIfContent]: true, class: 'nav-link-accent'}, + cur.accent), + ]); + })), + + html.tag('div', + {[html.onlyIfContent]: true, class: 'nav-bottom-row'}, + slots.navBottomRowContent), + + html.tag('div', + {[html.onlyIfContent]: true, class: 'nav-content'}, + slots.navContent), + ]) + const generateSidebarHTML = (side, id) => { const content = slots[side + 'Content']; const multiple = slots[side + 'Multiple']; @@ -213,7 +348,7 @@ export default { const collapseSidebars = slots.leftSidebarCollapse && slots.rightSidebarCollapse; const layoutHTML = [ - // navHTML, + navHTML, // banner.position === 'top' && bannerHTML, // secondaryNavHTML, html.tag('div', @@ -301,7 +436,7 @@ export default { html.tag('link', { rel: 'stylesheet', - href: to('shared.staticFile', `site3.css?${cachebust}`), + href: to('shared.staticFile', `site4.css?${cachebust}`), }), html.tag('style', diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 0519b7e8..acf8461b 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -1,10 +1,13 @@ export default { contentDependencies: [ 'generateTrackInfoPageContent', + 'generateAlbumNavLinks', 'generateAlbumSidebar', 'generateAlbumStyleRules', 'generateColorStyleRules', 'generatePageLayout', + 'linkAlbum', + 'linkTrack', ], extraDependencies: ['language'], @@ -13,6 +16,10 @@ export default { return { layout: relation('generatePageLayout'), + albumLink: relation('linkAlbum', track.album), + trackLink: relation('linkTrack', track), + albumNavLinks: relation('generateAlbumNavLinks', track.album, track), + content: relation('generateTrackInfoPageContent', track), sidebar: relation('generateAlbumSidebar', track.album, track), albumStyleRules: relation('generateAlbumStyleRules', track.album), @@ -23,6 +30,9 @@ export default { data(track) { return { name: track.name, + + hasTrackNumbers: track.album.hasTrackNumbers, + trackNumber: track.album.tracks.indexOf(track) + 1, }; }, @@ -38,6 +48,33 @@ export default { cover: relations.content.cover, mainContent: relations.content.main.content, + navLinkStyle: 'hierarchical', + navLinks: [ + {auto: 'home'}, + {html: relations.albumLink}, + { + html: + (data.hasTrackNumbers + ? language.$('trackPage.nav.track.withNumber', { + number: data.trackNumber, + track: relations.trackLink + .slot('attributes', {class: 'current'}), + }) + : language.$('trackPage.nav.track', { + track: relations.trackLink + .slot('attributes', {class: 'current'}), + })), + }, + ], + + navContent: '(Chronology links here)', + + navBottomRowContent: + relations.albumNavLinks.slots({ + showTrackNavigation: true, + showExtraLinks: false, + }), + ...relations.sidebar, }); }, diff --git a/src/content/dependencies/linkTemplate.js b/src/content/dependencies/linkTemplate.js index b87f3180..9109ab50 100644 --- a/src/content/dependencies/linkTemplate.js +++ b/src/content/dependencies/linkTemplate.js @@ -22,6 +22,7 @@ export default { path: {validate: v => v.validateArrayItems(v.isString)}, hash: {type: 'string'}, + tooltip: {validate: v => v.isString}, attributes: {validate: v => v.isAttributes}, color: {validate: v => v.isColor}, content: {type: 'html'}, @@ -30,6 +31,7 @@ export default { content(slots) { let href = slots.href; let style; + let title; if (!href && !empty(slots.path)) { href = to(...slots.path); @@ -53,11 +55,16 @@ export default { style = `--primary-color: ${primary}; --dim-color: ${dim}`; } + if (slots.tooltip) { + title = slots.tooltip; + } + return html.tag('a', { ...slots.attributes ?? {}, href, style, + title, }, slots.content); }, diff --git a/src/content/dependencies/linkThing.js b/src/content/dependencies/linkThing.js index 70c86fc4..fea68ae5 100644 --- a/src/content/dependencies/linkThing.js +++ b/src/content/dependencies/linkThing.js @@ -35,6 +35,10 @@ export default { content: relations.linkTemplate.getSlotDescription('content'), preferShortName: {type: 'boolean', default: false}, + tooltip: { + validate: v => v.oneOf(v.isBoolean, v.isString), + }, + color: relations.linkTemplate.getSlotDescription('color'), attributes: relations.linkTemplate.getSlotDescription('attributes'), hash: relations.linkTemplate.getSlotDescription('hash'), @@ -43,20 +47,30 @@ export default { content(slots) { let content = slots.content; + const name = + (slots.preferShortName + ? data.nameShort ?? data.name + : data.name); + if (html.isBlank(content)) { - content = - (slots.preferShortName - ? data.nameShort ?? data.name - : data.name); + content = name; } const color = slots.color ?? data.color ?? null; + let tooltip = null; + if (slots.tooltip === true) { + tooltip = name; + } else if (typeof slots.tooltip === 'string') { + tooltip = slots.tooltip; + } + return relations.linkTemplate .slots({ path, content, color, + tooltip, attributes: slots.attributes, hash: slots.hash, -- cgit 1.3.0-6-gf8a5