Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up artist nav links + add "imaginary-sibling" metatag #577

Merged
merged 7 commits into from
Nov 15, 2024
Merged
136 changes: 65 additions & 71 deletions src/content/dependencies/generateArtistNavLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,44 @@ import {empty} from '#sugar';

export default {
contentDependencies: [
'generateInterpageDotSwitcher',
'linkArtist',
'linkArtistGallery',
],

extraDependencies: ['html', 'language', 'wikiData'],

sprawl({wikiInfo}) {
return {
enableListings: wikiInfo.enableListings,
};
},
sprawl: ({wikiInfo}) => ({
enableListings:
wikiInfo.enableListings,
}),

relations(relation, sprawl, artist) {
const relations = {};
query: (_sprawl, artist) => ({
hasGallery:
!empty(artist.albumCoverArtistContributions) ||
!empty(artist.trackCoverArtistContributions),
}),

relations.artistMainLink =
relation('linkArtist', artist);
relations: (relation, query, _sprawl, artist) => ({
switcher:
relation('generateInterpageDotSwitcher'),

relations.artistInfoLink =
relation('linkArtist', artist);
artistMainLink:
relation('linkArtist', artist),

if (
!empty(artist.albumCoverArtistContributions) ||
!empty(artist.trackCoverArtistContributions)
) {
relations.artistGalleryLink =
relation('linkArtistGallery', artist);
}
artistInfoLink:
relation('linkArtist', artist),

return relations;
},
artistGalleryLink:
(query.hasGallery
? relation('linkArtistGallery', artist)
: null),
}),

data(sprawl) {
return {
enableListings: sprawl.enableListings,
};
},
data: (_query, sprawl) => ({
enableListings:
sprawl.enableListings,
}),

slots: {
showExtraLinks: {type: 'boolean', default: false},
Expand All @@ -48,53 +49,46 @@ export default {
},
},

generate(data, relations, slots, {html, language}) {
const infoLink =
relations.artistInfoLink?.slots({
attributes: {class: slots.currentExtra === null && 'current'},
content: language.$('misc.nav.info'),
});

const {content: extraLinks = []} =
slots.showExtraLinks &&
{content: [
relations.artistGalleryLink?.slots({
attributes: {class: slots.currentExtra === 'gallery' && 'current'},
content: language.$('misc.nav.gallery'),
}),
]};

const mostAccentLinks = [
...extraLinks,
].filter(Boolean);

// Don't show the info accent link all on its own.
const allAccentLinks =
(empty(mostAccentLinks)
? []
: [infoLink, ...mostAccentLinks]);

const accent =
(empty(allAccentLinks)
? html.blank()
: `(${language.formatUnitList(allAccentLinks)})`);

return [
{auto: 'home'},

data.enableListings &&
{
path: ['localized.listingIndex'],
title: language.$('listingIndex.title'),
},
generate: (data, relations, slots, {html, language}) => [
{auto: 'home'},

data.enableListings &&
{
accent,
html:
language.$('artistPage.nav.artist', {
artist: relations.artistMainLink,
}),
path: ['localized.listingIndex'],
title: language.$('listingIndex.title'),
},
];
},

{
html:
language.$('artistPage.nav.artist', {
artist: relations.artistMainLink,
}),

accent:
relations.switcher.slots({
links: [
relations.artistInfoLink.slots({
attributes: [
slots.currentExtra === null &&
{class: 'current'},

{[html.onlyIfSiblings]: true},
],

content: language.$('misc.nav.info'),
}),

slots.showExtraLinks &&
relations.artistGalleryLink?.slots({
attributes: [
slots.currentExtra === 'gallery' &&
{class: 'current'},
],

content: language.$('misc.nav.gallery'),
}),
],
}),
},
],
};
10 changes: 9 additions & 1 deletion src/content/dependencies/generateDotSwitcherTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default {

generate: (slots, {html}) =>
html.tag('span', {class: 'dot-switcher'},
{[html.onlyIfContent]: true},
{[html.noEdgeWhitespace]: true},
{[html.joinChildren]: ''},

Expand All @@ -26,8 +27,15 @@ export default {
html.tag('span',
{[html.onlyIfContent]: true},

html.resolve(option, {normalize: 'tag'})
.onlyIfSiblings &&
{[html.onlyIfSiblings]: true},

index === slots.initialOptionIndex &&
{class: 'current'},

option))),
[
html.metatag('imaginary-sibling'),
option,
]))),
};
54 changes: 46 additions & 8 deletions src/util/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ export const blockwrap = Symbol();
// considered wrappable units, not the entire element!
export const chunkwrap = Symbol();

// Don't pass this directly, use html.metatag('imaginary-sibling') instead.
// A tag without any content, which is completely ignored when serializing,
// but makes siblings with [onlyIfSiblings] feel less shy and show up on
// their own, even without a non-blank (and non-onlyIfSiblings) sibling.
export const imaginarySibling = Symbol();

// Recursive helper function for isBlank, which basically flattens an array
// and returns as soon as it finds any content - a non-blank case - and doesn't
// traverse templates of its own accord. If it doesn't find directly non-blank
Expand Down Expand Up @@ -322,6 +328,9 @@ export function metatag(identifier, ...args) {
case 'chunkwrap':
return new Tag(null, {[chunkwrap]: true, ...opts}, content);

case 'imaginary-sibling':
return new Tag(null, {[imaginarySibling]: true}, content);

default:
throw new Error(`Unknown metatag "${identifier}"`);
}
Expand Down Expand Up @@ -392,16 +401,22 @@ export class Tag {
}

set content(value) {
if (
this.selfClosing &&
!(value === null ||
value === undefined ||
!value ||
Array.isArray(value) && value.filter(Boolean).length === 0)
) {
const contentful =
value !== null &&
value !== undefined &&
value &&
(Array.isArray(value)
? !empty(value.filter(Boolean))
: true);

if (this.selfClosing && contentful) {
throw new Error(`Tag <${this.tagName}> is self-closing but got content`);
}

if (this.imaginarySibling && contentful) {
throw new Error(`html.metatag('imaginary-sibling') can't have content`);
}

const contentArray =
(Array.isArray(value)
? value.flat(Infinity).filter(Boolean)
Expand Down Expand Up @@ -440,6 +455,10 @@ export class Tag {
// something about this tag's own content or attributes. It does *not*
// account for [html.onlyIfSiblings]!

if (this.imaginarySibling) {
return true;
}

if (this.onlyIfContent && isBlank(this.content)) {
return true;
}
Expand Down Expand Up @@ -544,7 +563,7 @@ export class Tag {
this.#setAttributeFlag(chunkwrap, value);

try {
this.content = content;
this.content = this.content;
} catch (error) {
this.#setAttributeFlag(chunkwrap, false);
throw error;
Expand All @@ -555,6 +574,20 @@ export class Tag {
return this.#getAttributeFlag(chunkwrap);
}

set imaginarySibling(value) {
this.#setAttributeFlag(imaginarySibling, value);

try {
this.content = this.content;
} catch (error) {
this.#setAttributeFlag(imaginarySibling, false);
}
}

get imaginarySibling() {
return this.#getAttributeFlag(imaginarySibling);
}

toString() {
if (this.onlyIfContent && isBlank(this.content)) {
return '';
Expand Down Expand Up @@ -652,6 +685,11 @@ export class Tag {
const nonTemplateItem =
Template.resolve(item);

if (nonTemplateItem instanceof Tag && nonTemplateItem.imaginarySibling) {
seenSiblingIndependentContent = true;
continue;
}

let itemContent;
try {
itemContent = nonTemplateItem.toString();
Expand Down