From 474e8afe7328b80cda5e437e1d1b8c1191425d72 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Wed, 13 Jul 2022 00:34:36 -0300 Subject: begin htmlifying listings & listing specs --- src/listing-spec.js | 305 +++++++++++++++++++++++----------------------------- src/page/listing.js | 224 ++++++++++++++++++++------------------ src/util/html.js | 18 +++- 3 files changed, 267 insertions(+), 280 deletions(-) diff --git a/src/listing-spec.js b/src/listing-spec.js index 28f4b1a..c1f4434 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -15,237 +15,198 @@ const listingSpec = [ directory: 'albums/by-name', stringsKey: 'listAlbums.byName', - data({wikiData}) { - return sortAlphabetically(wikiData.albumData.slice()); - }, + data: ({wikiData: {albumData}}) => + sortAlphabetically(albumData.slice()), - row(album, {link, language}) { - return language.$('listingPage.listAlbums.byName.item', { + row: (album, {language, link}) => + language.$('listingPage.listAlbums.byName.item', { album: link.album(album), tracks: language.countTracks(album.tracks.length, {unit: true}), - }); - }, + }), }, { directory: 'albums/by-tracks', stringsKey: 'listAlbums.byTracks', - data({wikiData}) { - return wikiData.albumData - .slice() - .sort((a, b) => b.tracks.length - a.tracks.length); - }, + data: ({wikiData: {albumData}}) => + albumData.slice() + .sort((a, b) => b.tracks.length - a.tracks.length), - row(album, {link, language}) { - return language.$('listingPage.listAlbums.byTracks.item', { + row: (album, {language, link}) => + language.$('listingPage.listAlbums.byTracks.item', { album: link.album(album), tracks: language.countTracks(album.tracks.length, {unit: true}), - }); - }, + }), }, { directory: 'albums/by-duration', stringsKey: 'listAlbums.byDuration', - data({wikiData}) { - return wikiData.albumData - .map((album) => ({album, duration: getTotalDuration(album.tracks)})) - .sort((a, b) => b.duration - a.duration); - }, + data: ({wikiData: {albumData}}) => + albumData + .map(album => ({ + album, + duration: getTotalDuration(album.tracks), + })) + .filter(album => album.duration) + .sort((a, b) => b.duration - a.duration), - row({album, duration}, {link, language}) { - return language.$('listingPage.listAlbums.byDuration.item', { + row: ({album, duration}, {language, link}) => + language.$('listingPage.listAlbums.byDuration.item', { album: link.album(album), duration: language.formatDuration(duration), - }); - }, + }), }, { directory: 'albums/by-date', stringsKey: 'listAlbums.byDate', - data({wikiData}) { - return sortChronologically( - wikiData.albumData.filter((album) => album.date) - ); - }, + data: ({wikiData: {albumData}}) => + sortChronologically( + albumData + .filter(album => album.date)), - row(album, {link, language}) { - return language.$('listingPage.listAlbums.byDate.item', { + row: (album, {language, link}) => + language.$('listingPage.listAlbums.byDate.item', { album: link.album(album), date: language.formatDate(album.date), - }); - }, + }), }, { directory: 'albums/by-date-added', stringsKey: 'listAlbums.byDateAdded', - data({wikiData}) { - return chunkByProperties( - wikiData.albumData - .filter((a) => a.dateAddedToWiki) + data: ({wikiData: {albumData}}) => + chunkByProperties( + albumData + .filter(a => a.dateAddedToWiki) .sort((a, b) => { if (a.dateAddedToWiki < b.dateAddedToWiki) return -1; if (a.dateAddedToWiki > b.dateAddedToWiki) return 1; }), - ['dateAddedToWiki'] - ); - }, - - html(chunks, {link, language}) { - return fixWS` -
- ${chunks - .map( - ({dateAddedToWiki, chunk: albums}) => fixWS` -
${language.$( - 'listingPage.listAlbums.byDateAdded.date', - { - date: language.formatDate(dateAddedToWiki), - } - )}
-
    - ${albums - .map((album) => - language.$( - 'listingPage.listAlbums.byDateAdded.album', - { - album: link.album(album), - } - ) - ) - .map((row) => `
  • ${row}
  • `) - .join('\n')} -
- ` - ) - .join('\n')} -
- `; - }, + ['dateAddedToWiki']), + + html: (chunks, {html, language, link}) => + html.tag('dl', + chunks.flatMap(({dateAddedToWiki, chunk: albums}) => [ + html.tag('dt', + language.$('listingPage.listAlbums.byDateAdded.date', { + date: language.formatDate(dateAddedToWiki), + })), + html.tag('dd', + html.tag('ul', + albums.map((album) => + html.tag('li', + language.$('listingPage.listAlbums.byDateAdded.album', { + album: link.album(album), + }))))), + ])), }, { directory: 'artists/by-name', stringsKey: 'listArtists.byName', - data({wikiData}) { - return sortAlphabetically(wikiData.artistData.slice()).map((artist) => ({ - artist, - contributions: getArtistNumContributions(artist), - })); - }, + data: ({wikiData: {artistData}}) => + sortAlphabetically(artistData.slice()) + .map(artist => ({ + artist, + contributions: getArtistNumContributions(artist), + })), - row({artist, contributions}, {link, language}) { - return language.$('listingPage.listArtists.byName.item', { + row: ({artist, contributions}, {link, language}) => + language.$('listingPage.listArtists.byName.item', { artist: link.artist(artist), contributions: language.countContributions(contributions, { unit: true, }), - }); - }, + }), }, { directory: 'artists/by-contribs', stringsKey: 'listArtists.byContribs', - data({wikiData}) { - return { - toTracks: wikiData.artistData - .map((artist) => ({ - artist, - contributions: - (artist.tracksAsContributor?.length ?? 0) + - (artist.tracksAsArtist?.length ?? 0), - })) - .sort((a, b) => b.contributions - a.contributions) - .filter(({contributions}) => contributions), - - toArtAndFlashes: wikiData.artistData - .map((artist) => ({ - artist, - contributions: - (artist.tracksAsCoverArtist?.length ?? 0) + - (artist.albumsAsCoverArtist?.length ?? 0) + - (artist.albumsAsWallpaperArtist?.length ?? 0) + - (artist.albumsAsBannerArtist?.length ?? 0) + - (wikiData.wikiInfo.enableFlashesAndGames - ? artist.flashesAsContributor?.length ?? 0 - : 0), - })) - .sort((a, b) => b.contributions - a.contributions) - .filter(({contributions}) => contributions), + data: ({wikiData: {artistData, wikiInfo}}) => ({ + toTracks: artistData + .map(artist => ({ + artist, + contributions: + (artist.tracksAsContributor?.length ?? 0) + + (artist.tracksAsArtist?.length ?? 0), + })) + .sort((a, b) => b.contributions - a.contributions) + .filter(({contributions}) => contributions), - // This is a kinda naughty hack, 8ut like, it's the only place - // we'd 8e passing wikiData to html() otherwise, so like.... - // (Ok we do do this again once later.) - showAsFlashes: wikiData.wikiInfo.enableFlashesAndGames, - }; - }, + toArtAndFlashes: artistData + .map(artist => ({ + artist, + contributions: + (artist.tracksAsCoverArtist?.length ?? 0) + + (artist.albumsAsCoverArtist?.length ?? 0) + + (artist.albumsAsWallpaperArtist?.length ?? 0) + + (artist.albumsAsBannerArtist?.length ?? 0) + + (wikiInfo.enableFlashesAndGames + ? artist.flashesAsContributor?.length ?? 0 + : 0), + })) + .sort((a, b) => b.contributions - a.contributions) + .filter(({contributions}) => contributions), - html({toTracks, toArtAndFlashes, showAsFlashes}, {link, language}) { - return fixWS` -
-
-

${language.$( - 'listingPage.misc.trackContributors' - )}

-
    - ${toTracks - .map(({artist, contributions}) => - language.$( - 'listingPage.listArtists.byContribs.item', - { - artist: link.artist(artist), - contributions: language.countContributions( - contributions, - { - unit: true, - } - ), - } - ) - ) - .map((row) => `
  • ${row}
  • `) - .join('\n')} -
-
-
-

${language.$( - 'listingPage.misc' + - (showAsFlashes - ? '.artAndFlashContributors' - : '.artContributors') - )}

-
    - ${toArtAndFlashes - .map(({artist, contributions}) => - language.$( - 'listingPage.listArtists.byContribs.item', - { - artist: link.artist(artist), - contributions: language.countContributions( - contributions, - { - unit: true, - } - ), - } - ) - ) - .map((row) => `
  • ${row}
  • `) - .join('\n')} -
-
-
- `; - }, + // This is a kinda naughty hack, 8ut like, it's the only place + // we'd 8e passing wikiData to html() otherwise, so like.... + // (Ok we do do this again once later.) + showAsFlashes: wikiInfo.enableFlashesAndGames, + }), + + html: ( + {toTracks, toArtAndFlashes, showAsFlashes}, + {html, language, link} + ) => + html.tag('div', + {class: 'content-columns'}, + [ + html.tag('div', + {class: 'column'}, + [ + html.tag('h2', + language.$('listingPage.misc.trackContributors')), + + html.tag('ul', + toTracks.map(({artist, contributions}) => + html.tag('li', + language.$('listingPage.listArtists.byContribs.item', { + artist: link.artist(artist), + contributions: language.countContributions(contributions, { + unit: true, + }), + })))), + ]), + + html.tag('div', + {class: 'column'}, + [ + html.tag('h2', + language.$( + 'listingPage.misc' + + (showAsFlashes + ? '.artAndFlashContributors' + : '.artContributors'))), + + html.tag('ul', + toArtAndFlashes.map(({artist, contributions}) => + html.tag('li', + language.$('listingPage.listArtists.byContribs.item', { + artist: link.artist(artist), + contributions: + language.countContributions(contributions, {unit: true}), + })))), + ]), + ]), }, { diff --git a/src/page/listing.js b/src/page/listing.js index 5db6c91..5a2b6d2 100644 --- a/src/page/listing.js +++ b/src/page/listing.js @@ -10,16 +10,8 @@ // Individual listing specs are described in src/listing-spec.js, but are // provided via wikiData like other (normal) data objects. -// Imports - -import fixWS from 'fix-whitespace'; - -import * as html from '../util/html.js'; - import {getTotalDuration} from '../util/wiki-data.js'; -// Page exports - export function condition({wikiData}) { return wikiData.wikiInfo.enableListings; } @@ -39,40 +31,43 @@ export function write(listing, {wikiData}) { type: 'page', path: ['listing', listing.directory], page: (opts) => { - const {getLinkThemeString, link, language} = opts; + const { + getLinkThemeString, + html, + language, + link, + } = opts; + const titleKey = `listingPage.${listing.stringsKey}.title`; return { title: language.$(titleKey), main: { - content: fixWS` -

${language.$(titleKey)}

- ${ - listing.html && - (listing.data - ? listing.html(data, opts) - : listing.html(opts)) - } - ${ - listing.row && - fixWS` - - ` - } - `, + content: [ + html.tag('h1', + language.$(titleKey)), + + ...html.fragment( + listing.html && + (listing.data + ? listing.html(data, opts) + : listing.html(opts))), + + listing.row && + html.tag('ul', + data.map((item) => + html.tag('li', + listing.row(item, opts)))), + ], }, sidebarLeft: { content: generateSidebarForListings(listing, { getLinkThemeString, - link, + html, language, + link, wikiData, }), }, @@ -103,40 +98,58 @@ export function writeTargetless({wikiData}) { const page = { type: 'page', path: ['listingIndex'], - page: ({getLinkThemeString, language, link}) => ({ + page: ({ + getLinkThemeString, + html, + language, + link, + }) => ({ title: language.$('listingIndex.title'), main: { - content: fixWS` -

${language.$('listingIndex.title')}

-

${language.$('listingIndex.infoLine', { - wiki: wikiInfo.name, - tracks: `${language.countTracks(trackData.length, { - unit: true, - })}`, - albums: `${language.countAlbums(albumData.length, { - unit: true, - })}`, - duration: `${language.formatDuration(totalDuration, { - approximate: true, - unit: true, - })}`, - })}

-
-

${language.$('listingIndex.exploreList')}

- ${generateLinkIndexForListings(null, false, { - link, - language, - wikiData, - })} - `, + content: [ + html.tag('h1', + language.$('listingIndex.title')), + + html.tag('p', + language.$('listingIndex.infoLine', { + wiki: wikiInfo.name, + tracks: html.tag('b', + language.countTracks(trackData.length, { + unit: true, + })), + albums: html.tag('b', + language.countAlbums(albumData.length, { + unit: true, + })), + duration: html.tag('b', + language.formatDuration(totalDuration, { + approximate: true, + unit: true, + })), + })), + + html.tag('hr'), + + html.tag('p', + language.$('listingIndex.exploreList')), + + ...html.fragment( + generateLinkIndexForListings(null, false, { + html, + link, + language, + wikiData, + })), + ], }, sidebarLeft: { content: generateSidebarForListings(null, { getLinkThemeString, - link, + html, language, + link, wikiData, }), }, @@ -150,28 +163,37 @@ export function writeTargetless({wikiData}) { // Utility functions -function generateSidebarForListings( - currentListing, - {getLinkThemeString, link, language, wikiData} -) { - return fixWS` -

${link.listingIndex('', { - text: language.$('listingIndex.title'), - })}

- ${generateLinkIndexForListings(currentListing, true, { - getLinkThemeString, - link, - language, - wikiData, - })} - `; +function generateSidebarForListings(currentListing, { + getLinkThemeString, + html, + language, + link, + wikiData, +}) { + return [ + html.tag('h1', + link.listingIndex('', { + text: language.$('listingIndex.title'), + })), + + ...html.fragment( + generateLinkIndexForListings(currentListing, true, { + getLinkThemeString, + html, + language, + link, + wikiData, + })), + ]; } -function generateLinkIndexForListings( - currentListing, - forSidebar, - {getLinkThemeString, link, language, wikiData} -) { +function generateLinkIndexForListings(currentListing, forSidebar, { + getLinkThemeString, + html, + language, + link, + wikiData, +}) { const {listingTargetSpec, wikiInfo} = wikiData; const filteredByCondition = listingTargetSpec @@ -182,46 +204,34 @@ function generateLinkIndexForListings( .filter(({listings}) => listings.length > 0); const genUL = (listings) => - html.tag( - 'ul', + html.tag('ul', listings.map((listing) => - html.tag( - 'li', + html.tag('li', {class: [listing === currentListing && 'current']}, link.listing(listing, { text: language.$(`listingPage.${listing.stringsKey}.title.short`), - }) - ) - ) - ); - - if (forSidebar) { - return filteredByCondition - .map(({title, listings}) => - html.tag( - 'details', + })))); + + return forSidebar + ? filteredByCondition.map(({title, listings}) => + html.tag('details', { - open: !forSidebar || listings.includes(currentListing), + open: listings.includes(currentListing), class: listings.includes(currentListing) && 'current', }, [ - html.tag( - 'summary', + html.tag('summary', {style: getLinkThemeString(wikiInfo.color)}, - html.tag('span', {class: 'group-name'}, title({language})) - ), + html.tag('span', + {class: 'group-name'}, + title({language}))), genUL(listings), - ] - ) - ) - .join('\n'); - } else { - return html.tag( - 'dl', - filteredByCondition.flatMap(({title, listings}) => [ - html.tag('dt', title({language})), - html.tag('dd', genUL(listings)), - ]) - ); - } + ])) + : html.tag('dl', + filteredByCondition.flatMap(({title, listings}) => [ + html.tag('dt', + title({language})), + html.tag('dd', + genUL(listings)), + ])); } diff --git a/src/util/html.js b/src/util/html.js index 338df71..bdb385b 100644 --- a/src/util/html.js +++ b/src/util/html.js @@ -55,7 +55,7 @@ export function tag(tagName, ...args) { } if (attrs) { - const attrString = attributes(args[0]); + const attrString = attributes(attrs); if (attrString) { openTag = `${tagName} ${attrString}`; } @@ -121,3 +121,19 @@ export function attributes(attribs) { ) .join(' '); } + +// Ensures the passed value is an array of elements, for usage in [...spread] +// syntax. This may be used when it's not guaranteed whether the return value of +// an external function is one child or an array, or in combination with +// conditionals, e.g. fragment(cond && [x, y, z]). +export function fragment(childOrChildren) { + if (!childOrChildren) { + return []; + } + + if (Array.isArray(childOrChildren)) { + return childOrChildren; + } + + return [childOrChildren]; +} -- cgit 1.3.0-6-gf8a5