diff options
-rw-r--r-- | src/content/dependencies/generateAdditionalNamesBox.js | 48 | ||||
-rw-r--r-- | src/content/dependencies/generatePageLayout.js | 38 | ||||
-rw-r--r-- | src/content/dependencies/generateTrackInfoPage.js | 8 | ||||
-rw-r--r-- | src/static/client3.js | 90 | ||||
-rw-r--r-- | src/static/site5.css | 66 | ||||
-rw-r--r-- | src/strings-default.yaml | 15 |
6 files changed, 249 insertions, 16 deletions
diff --git a/src/content/dependencies/generateAdditionalNamesBox.js b/src/content/dependencies/generateAdditionalNamesBox.js new file mode 100644 index 00000000..f7fa3b00 --- /dev/null +++ b/src/content/dependencies/generateAdditionalNamesBox.js @@ -0,0 +1,48 @@ +import {stitchArrays} from '#sugar'; + +export default { + contentDependencies: ['transformContent'], + extraDependencies: ['html', 'language'], + + relations: (relation, additionalNames) => ({ + names: + additionalNames.map(({name}) => + relation('transformContent', name)), + + annotations: + additionalNames.map(({annotation}) => + (annotation + ? relation('transformContent', annotation) + : null)), + }), + + generate: (relations, {html, language}) => { + const names = + relations.names.map(name => + html.tag('span', {class: 'additional-name'}, + name.slot('mode', 'inline'))); + + const annotations = + relations.annotations.map(annotation => + (annotation + ? html.tag('span', {class: 'annotation'}, + language.$('misc.additionalNames.item.annotation', { + annotation: + annotation.slot('mode', 'inline'), + })) + : null)); + + return html.tag('div', {id: 'additional-names-box'}, [ + html.tag('p', + language.$('misc.additionalNames.title')), + + html.tag('ul', + stitchArrays({name: names, annotation: annotations}) + .map(({name, annotation}) => + html.tag('li', + (annotation + ? language.$('misc.additionalNames.item.withAnnotation', {name, annotation}) + : language.$('misc.additionalNames.item', {name}))))), + ]); + }, +}; diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js index 95551f3e..7dee8cc3 100644 --- a/src/content/dependencies/generatePageLayout.js +++ b/src/content/dependencies/generatePageLayout.js @@ -108,6 +108,8 @@ export default { title: {type: 'html'}, showWikiNameInTitle: {type: 'boolean', default: true}, + additionalNames: {type: 'html'}, + cover: {type: 'html'}, socialEmbed: {type: 'html'}, @@ -222,22 +224,25 @@ export default { const colors = getColors(slots.color ?? data.wikiColor); const hasSocialEmbed = !html.isBlank(slots.socialEmbed); - let titleHTML = null; - - if (!html.isBlank(slots.title)) { - switch (slots.headingMode) { - case 'sticky': - titleHTML = - relations.stickyHeadingContainer.slots({ - title: slots.title, - cover: slots.cover, - }); - break; - case 'static': - titleHTML = html.tag('h1', slots.title); - break; - } - } + const titleContentsHTML = + (html.isBlank(slots.title) + ? null + : html.isBlank(slots.additionalNames) + ? language.sanitize(slots.title) + : html.tag('a', { + href: '#additional-names-box', + title: language.$('misc.additionalNames.tooltip').toString(), + }, language.sanitize(slots.title))); + + const titleHTML = + (html.isBlank(slots.title) + ? null + : slots.headingMode === 'sticky' + ? relations.stickyHeadingContainer.slots({ + title: titleContentsHTML, + cover: slots.cover, + }) + : html.tag('h1', titleContentsHTML)); let footerContent = slots.footerContent; @@ -254,6 +259,7 @@ export default { titleHTML, slots.cover, + slots.additionalNames, html.tag('div', { diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 93334948..180e5c29 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -6,6 +6,7 @@ import getChronologyRelations from '../util/getChronologyRelations.js'; export default { contentDependencies: [ 'generateAdditionalFilesShortcut', + 'generateAdditionalNamesBox', 'generateAlbumAdditionalFilesList', 'generateAlbumNavAccent', 'generateAlbumSidebar', @@ -106,6 +107,11 @@ export default { list: relation('generateAlbumAdditionalFilesList', album, additionalFiles), }); + if (!empty(track.additionalNames)) { + relations.additionalNamesBox = + relation('generateAdditionalNamesBox', track.additionalNames); + } + if (track.hasUniqueCoverArt || album.hasCoverArt) { relations.cover = relation('generateTrackCoverArtwork', track); @@ -300,6 +306,8 @@ export default { title: language.$('trackPage.title', {track: data.name}), headingMode: 'sticky', + additionalNames: relations.additionalNamesBox ?? null, + color: data.color, styleRules: [relations.albumStyleRules], diff --git a/src/static/client3.js b/src/static/client3.js index 4a5dffc2..94ba4a23 100644 --- a/src/static/client3.js +++ b/src/static/client3.js @@ -1260,6 +1260,96 @@ function loadImage(imageUrl, onprogress) { }); } +// "Additional names" box --------------------------------- + +const additionalNamesBoxInfo = clientInfo.additionalNamesBox = { + box: null, + links: null, + mainContentContainer: null, + + state: { + visible: false, + }, +}; + +function getAdditionalNamesBoxReferences() { + const info = additionalNamesBoxInfo; + + info.box = + document.getElementById('additional-names-box'); + + info.links = + document.querySelectorAll('a[href="#additional-names-box"]'); + + info.mainContentContainer = + document.querySelector('#content .main-content-container'); +} + +function addAdditionalNamesBoxInternalListeners() { + const info = additionalNamesBoxInfo; + + hashLinkInfo.event.beforeHashLinkScrolls.push(({target}) => { + if (target === info.box) { + return false; + } + }); +} + +function addAdditionalNamesBoxListeners() { + const info = additionalNamesBoxInfo; + + for (const link of info.links) { + link.addEventListener('click', domEvent => { + handleAdditionalNamesBoxLinkClicked(domEvent); + }); + } +} + +function handleAdditionalNamesBoxLinkClicked(domEvent) { + const info = additionalNamesBoxInfo; + const {state} = info; + + domEvent.preventDefault(); + + if (!info.box || !info.mainContentContainer) return; + + const margin = + +(cssProp(info.box, 'scroll-margin-top').replace('px', '')); + + const {top} = + (state.visible + ? info.box.getBoundingClientRect() + : info.mainContentContainer.getBoundingClientRect()); + + if (top + 20 < margin || top > 0.4 * window.innerHeight) { + if (!state.visible) { + toggleAdditionalNamesBox(); + } + + window.scrollTo({ + top: window.scrollY + top - margin, + behavior: 'smooth', + }); + } else { + toggleAdditionalNamesBox(); + } +} + +function toggleAdditionalNamesBox() { + const info = additionalNamesBoxInfo; + const {state} = info; + + state.visible = !state.visible; + info.box.style.display = + (state.visible + ? 'block' + : 'none'); +} + +clientSteps.getPageReferences.push(getAdditionalNamesBoxReferences); +clientSteps.addInternalListeners.push(addAdditionalNamesBoxInternalListeners); +clientSteps.addPageListeners.push(addAdditionalNamesBoxListeners); + // Group contributions table ------------------------------ const groupContributionsTableInfo = diff --git a/src/static/site5.css b/src/static/site5.css index ea27e35e..31b2995b 100644 --- a/src/static/site5.css +++ b/src/static/site5.css @@ -802,6 +802,68 @@ html[data-url-key="localized.listing"][data-url-value0="random"] #content a:not( opacity: 0.7; } +/* Additional names (heading and box) */ + +h1 a[href="#additional-names-box"] { + color: inherit; + text-decoration: underline; + text-decoration-style: dotted; +} + +h1 a[href="#additional-names-box"]:hover { + text-decoration-style: solid; +} + +#additional-names-box { + --custom-scroll-offset: calc(0.5em - 2px); + + margin: 1em 0 1em -10px; + padding: 15px 20px 10px 20px; + width: max-content; + max-width: min(60vw, 600px); + + border: 1px dotted var(--primary-color); + border-radius: 6px; + + background: + linear-gradient(var(--bg-color), var(--bg-color)), + linear-gradient(#000000bb, #000000bb), + var(--primary-color); + + box-shadow: 0 -2px 6px -1px var(--dim-color) inset; + + display: none; +} + +#additional-names-box > :first-child { margin-top: 0; } +#additional-names-box > :last-child { margin-bottom: 0; } + +#additional-names-box p { + padding-left: 10px; + padding-right: 10px; + margin-bottom: 0; + font-style: oblique; +} + +#additional-names-box ul { + padding-left: 10px; + margin-top: 0.5em; +} + +#additional-names-box li .additional-name { + margin-right: 0.25em; +} + +#additional-names-box li .additional-name .content-image { + margin-bottom: 0.25em; + margin-top: 0.5em; +} + +#additional-names-box li .annotation { + opacity: 0.8; + display: inline-block; +} + /* Images */ .image-container { @@ -1760,6 +1822,10 @@ html[data-language-code="preview-en"][data-url-key="localized.home"] #content max-width: unset; } + #additional-names-box { + max-width: unset; + } + /* Show sticky heading above cover art */ .content-sticky-heading-container { diff --git a/src/strings-default.yaml b/src/strings-default.yaml index e6b8d6db..7562af84 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -338,6 +338,21 @@ trackList: # misc: + # additionalNames: + # "Drop"-styled box that catalogues a variety of additional or + # alternate names for the current thing; toggled by clicking on the + # thing's title, which is styled interactively and gets a tooltip + # (hover text), since it isn't usually an interactive element. + + additionalNames: + title: "Additional or alternate names:" + tooltip: "Click to view additional or alternate names" + + item: + _: "{NAME}" + withAnnotation: "{NAME} {ANNOTATION}" + annotation: "({ANNOTATION})" + # alt: # Fallback text for the alt text of images and artworks - these # are read aloud by screen readers. |