From ab99bc518483e5825d799e85365c6455b5529f92 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 13:17:07 -0300 Subject: [PATCH 01/29] data: drop "More list utilities", organize data index au Scratch --- src/data/composite/data/excludeFromList.js | 5 ---- .../composite/data/fillMissingListItems.js | 5 ---- src/data/composite/data/index.js | 28 ++++++++++++++----- src/data/composite/data/withFilteredList.js | 6 ---- src/data/composite/data/withFlattenedList.js | 6 ---- src/data/composite/data/withMappedList.js | 6 ---- .../composite/data/withPropertiesFromList.js | 6 ---- .../composite/data/withPropertyFromList.js | 6 ---- src/data/composite/data/withSortedList.js | 6 ---- .../composite/data/withUnflattenedList.js | 6 ---- 10 files changed, 21 insertions(+), 59 deletions(-) diff --git a/src/data/composite/data/excludeFromList.js b/src/data/composite/data/excludeFromList.js index d798dcdcf..2a3e818eb 100644 --- a/src/data/composite/data/excludeFromList.js +++ b/src/data/composite/data/excludeFromList.js @@ -5,11 +5,6 @@ // See also: // - fillMissingListItems // -// More list utilities: -// - withFilteredList, withMappedList, withSortedList -// - withFlattenedList, withUnflattenedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; import {empty} from '#sugar'; diff --git a/src/data/composite/data/fillMissingListItems.js b/src/data/composite/data/fillMissingListItems.js index 4f818a791..356b1119e 100644 --- a/src/data/composite/data/fillMissingListItems.js +++ b/src/data/composite/data/fillMissingListItems.js @@ -4,11 +4,6 @@ // See also: // - excludeFromList // -// More list utilities: -// - withFilteredList, withMappedList, withSortedList -// - withFlattenedList, withUnflattenedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/index.js b/src/data/composite/data/index.js index 0a47c43c4..0fc840618 100644 --- a/src/data/composite/data/index.js +++ b/src/data/composite/data/index.js @@ -3,16 +3,30 @@ // Entries here may depend on entries in #composite/control-flow. // +// Utilities which act on generic objects + +export {default as withPropertiesFromObject} from './withPropertiesFromObject.js'; +export {default as withPropertyFromObject} from './withPropertyFromObject.js'; + +// Utilities which act on generic lists + export {default as excludeFromList} from './excludeFromList.js'; + export {default as fillMissingListItems} from './fillMissingListItems.js'; +export {default as withUniqueItemsOnly} from './withUniqueItemsOnly.js'; + export {default as withFilteredList} from './withFilteredList.js'; -export {default as withFlattenedList} from './withFlattenedList.js'; export {default as withMappedList} from './withMappedList.js'; -export {default as withPropertiesFromList} from './withPropertiesFromList.js'; -export {default as withPropertiesFromObject} from './withPropertiesFromObject.js'; -export {default as withPropertyFromList} from './withPropertyFromList.js'; -export {default as withPropertyFromObject} from './withPropertyFromObject.js'; export {default as withSortedList} from './withSortedList.js'; -export {default as withSum} from './withSum.js'; + +export {default as withPropertyFromList} from './withPropertyFromList.js'; +export {default as withPropertiesFromList} from './withPropertiesFromList.js'; + +export {default as withFlattenedList} from './withFlattenedList.js'; export {default as withUnflattenedList} from './withUnflattenedList.js'; -export {default as withUniqueItemsOnly} from './withUniqueItemsOnly.js'; + + +// Utilities which act on slightly more particular data forms +// (probably, containers of particular kinds of values) + +export {default as withSum} from './withSum.js'; diff --git a/src/data/composite/data/withFilteredList.js b/src/data/composite/data/withFilteredList.js index 82e569033..60fe66f41 100644 --- a/src/data/composite/data/withFilteredList.js +++ b/src/data/composite/data/withFilteredList.js @@ -16,12 +16,6 @@ // - withMappedList // - withSortedList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFlattenedList, withUnflattenedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/withFlattenedList.js b/src/data/composite/data/withFlattenedList.js index edfa34038..31b1a742b 100644 --- a/src/data/composite/data/withFlattenedList.js +++ b/src/data/composite/data/withFlattenedList.js @@ -5,12 +5,6 @@ // See also: // - withUnflattenedList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFilteredList, withMappedList, withSortedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/withMappedList.js b/src/data/composite/data/withMappedList.js index e0a700b26..0bc63a927 100644 --- a/src/data/composite/data/withMappedList.js +++ b/src/data/composite/data/withMappedList.js @@ -5,12 +5,6 @@ // - withFilteredList // - withSortedList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFlattenedList, withUnflattenedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/withPropertiesFromList.js b/src/data/composite/data/withPropertiesFromList.js index 08907babd..fb4134bc3 100644 --- a/src/data/composite/data/withPropertiesFromList.js +++ b/src/data/composite/data/withPropertiesFromList.js @@ -8,12 +8,6 @@ // - withPropertiesFromObject // - withPropertyFromList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFilteredList, withMappedList, withSortedList -// - withFlattenedList, withUnflattenedList -// import {input, templateCompositeFrom} from '#composite'; import {isString, validateArrayItems} from '#validators'; diff --git a/src/data/composite/data/withPropertyFromList.js b/src/data/composite/data/withPropertyFromList.js index a2c66d77f..65ebf77b3 100644 --- a/src/data/composite/data/withPropertyFromList.js +++ b/src/data/composite/data/withPropertyFromList.js @@ -9,12 +9,6 @@ // - withPropertiesFromList // - withPropertyFromObject // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFilteredList, withMappedList, withSortedList -// - withFlattenedList, withUnflattenedList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/withSortedList.js b/src/data/composite/data/withSortedList.js index dd8107869..a7d217684 100644 --- a/src/data/composite/data/withSortedList.js +++ b/src/data/composite/data/withSortedList.js @@ -27,12 +27,6 @@ // - withFilteredList // - withMappedList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFlattenedList, withUnflattenedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; diff --git a/src/data/composite/data/withUnflattenedList.js b/src/data/composite/data/withUnflattenedList.js index 39a666dc8..820d628aa 100644 --- a/src/data/composite/data/withUnflattenedList.js +++ b/src/data/composite/data/withUnflattenedList.js @@ -7,12 +7,6 @@ // See also: // - withFlattenedList // -// More list utilities: -// - excludeFromList -// - fillMissingListItems -// - withFilteredList, withMappedList, withSortedList -// - withPropertyFromList, withPropertiesFromList -// import {input, templateCompositeFrom} from '#composite'; import {isWholeNumber, validateArrayItems} from '#validators'; From 4a5212a573632c29bb3e1148520a0e9b98b29d22 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:09:20 -0300 Subject: [PATCH 02/29] data: withIndexInList, withNearbyItemFromList --- src/data/composite/data/index.js | 2 + src/data/composite/data/withIndexInList.js | 38 ++++++++++ .../composite/data/withNearbyItemFromList.js | 73 +++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 src/data/composite/data/withIndexInList.js create mode 100644 src/data/composite/data/withNearbyItemFromList.js diff --git a/src/data/composite/data/index.js b/src/data/composite/data/index.js index 0fc840618..c80bb3503 100644 --- a/src/data/composite/data/index.js +++ b/src/data/composite/data/index.js @@ -25,6 +25,8 @@ export {default as withPropertiesFromList} from './withPropertiesFromList.js'; export {default as withFlattenedList} from './withFlattenedList.js'; export {default as withUnflattenedList} from './withUnflattenedList.js'; +export {default as withIndexInList} from './withIndexInList.js'; +export {default as withNearbyItemFromList} from './withNearbyItemFromList.js'; // Utilities which act on slightly more particular data forms // (probably, containers of particular kinds of values) diff --git a/src/data/composite/data/withIndexInList.js b/src/data/composite/data/withIndexInList.js new file mode 100644 index 000000000..b1af20333 --- /dev/null +++ b/src/data/composite/data/withIndexInList.js @@ -0,0 +1,38 @@ +// Gets the index of the provided item in the provided list. Note that this +// will output -1 if the item is not found, and this may be detected using +// any availability check with type: 'index'. If the list includes the item +// twice, the output index will be of the first match. +// +// Both the list and item must be provided. +// +// See also: +// - withNearbyItemFromList +// - exitWithoutDependency +// - raiseOutputWithoutDependency +// + +import {input, templateCompositeFrom} from '#composite'; + +export default templateCompositeFrom({ + annotation: `withIndexInList`, + + inputs: { + list: input({acceptsNull: false, type: 'array'}), + item: input({acceptsNull: false}), + }, + + outputs: ['#index'], + + steps: () => [ + { + dependencies: [input('list'), input('item')], + compute: (continuation, { + [input('list')]: list, + [input('item')]: item, + }) => continuation({ + ['#index']: + list.indexOf(item), + }), + }, + ], +}); diff --git a/src/data/composite/data/withNearbyItemFromList.js b/src/data/composite/data/withNearbyItemFromList.js new file mode 100644 index 000000000..83a8cc21e --- /dev/null +++ b/src/data/composite/data/withNearbyItemFromList.js @@ -0,0 +1,73 @@ +// Gets a nearby (typically adjacent) item in a list, meaning the item which is +// placed at a particular offset compared to the provided item. This is null if +// the provided list doesn't include the provided item at all, and also if the +// offset would read past either end of the list - except if configured: +// +// - If the 'wrap' input is provided (as true), the offset will loop around +// and continue from the opposing end. +// +// - If the 'valuePastEdge' input is provided, that value will be output +// instead of null. +// +// Both the list and item must be provided. +// +// See also: +// - withIndexInList +// + +import {input, templateCompositeFrom} from '#composite'; +import {atOffset} from '#sugar'; + +import {raiseOutputWithoutDependency} from '#composite/control-flow'; + +import withIndexInList from './withIndexInList.js'; + +export default templateCompositeFrom({ + annotation: `withNearbyItemFromList`, + + inputs: { + list: input({acceptsNull: false, type: 'array'}), + item: input({acceptsNull: false}), + + offset: input({type: 'number'}), + wrap: input({type: 'boolean', defaultValue: false}), + }, + + outputs: ['#nearbyItem'], + + steps: () => [ + withIndexInList({ + list: input('list'), + item: input('item'), + }), + + raiseOutputWithoutDependency({ + dependency: '#index', + mode: input.value('index'), + + output: input.value({ + ['#nearbyItem']: + null, + }), + }), + + { + dependencies: [ + input('list'), + input('offset'), + input('wrap'), + '#index', + ], + + compute: (continuation, { + [input('list')]: list, + [input('offset')]: offset, + [input('wrap')]: wrap, + ['#index']: index, + }) => continuation({ + ['#nearbyItem']: + atOffset(list, index, offset, {wrap}), + }), + }, + ], +}); From 2b10259a2446334cdc1be3340ef0c4e681b59152 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:13:19 -0300 Subject: [PATCH 03/29] data: Contribution.artistProperty --- .../withRecontextualizedContributionList.js | 28 +++++++++++++++---- .../wiki-data/withResolvedContribs.js | 8 ++++++ .../wiki-properties/contributionList.js | 8 +++++- src/data/things/contribution.js | 5 ++++ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/data/composite/wiki-data/withRecontextualizedContributionList.js b/src/data/composite/wiki-data/withRecontextualizedContributionList.js index 06c997b5f..d2401eacd 100644 --- a/src/data/composite/wiki-data/withRecontextualizedContributionList.js +++ b/src/data/composite/wiki-data/withRecontextualizedContributionList.js @@ -1,12 +1,14 @@ // Clones all the contributions in a list, with thing and thingProperty both // updated to match the current thing. Overwrites the provided dependency. -// Doesn't do anything if the provided dependency is null. +// Optionally updates artistProperty as well. Doesn't do anything if +// the provided dependency is null. // // See also: // - withRedatedContributionList // import {input, templateCompositeFrom} from '#composite'; +import {isStringNonEmpty} from '#validators'; import {raiseOutputWithoutDependency} from '#composite/control-flow'; import {withClonedThings} from '#composite/wiki-data'; @@ -19,6 +21,11 @@ export default templateCompositeFrom({ type: 'array', acceptsNull: true, }), + + artistProperty: input({ + validate: isStringNonEmpty, + defaultValue: null, + }), }, outputs: ({ @@ -47,16 +54,25 @@ export default templateCompositeFrom({ }, { - dependencies: [input.myself(), input.thisProperty()], + dependencies: [ + input.myself(), + input.thisProperty(), + input('artistProperty'), + ], compute: (continuation, { [input.myself()]: myself, [input.thisProperty()]: thisProperty, + [input('artistProperty')]: artistProperty, }) => continuation({ - ['#assignment']: { - thing: myself, - thingProperty: thisProperty, - }, + ['#assignment']: + Object.assign( + {thing: myself}, + {thingProperty: thisProperty}, + + (artistProperty + ? {artistProperty} + : {})), }), }, diff --git a/src/data/composite/wiki-data/withResolvedContribs.js b/src/data/composite/wiki-data/withResolvedContribs.js index 23b916919..b5d7255ba 100644 --- a/src/data/composite/wiki-data/withResolvedContribs.js +++ b/src/data/composite/wiki-data/withResolvedContribs.js @@ -36,6 +36,11 @@ export default templateCompositeFrom({ validate: isStringNonEmpty, defaultValue: null, }), + + artistProperty: input({ + validate: isStringNonEmpty, + defaultValue: null, + }), }, outputs: ['#resolvedContribs'], @@ -103,12 +108,14 @@ export default templateCompositeFrom({ dependencies: [ '#details', '#thingProperty', + input('artistProperty'), input.myself(), ], compute: (continuation, { ['#details']: details, ['#thingProperty']: thingProperty, + [input('artistProperty')]: artistProperty, [input.myself()]: myself, }) => continuation({ ['#contributions']: @@ -119,6 +126,7 @@ export default templateCompositeFrom({ ...details, thing: myself, thingProperty: thingProperty, + artistProperty: artistProperty, }); return contrib; diff --git a/src/data/composite/wiki-properties/contributionList.js b/src/data/composite/wiki-properties/contributionList.js index a0e6e52b7..d9a6b417f 100644 --- a/src/data/composite/wiki-properties/contributionList.js +++ b/src/data/composite/wiki-properties/contributionList.js @@ -15,7 +15,7 @@ // import {input, templateCompositeFrom} from '#composite'; -import {isContributionList, isDate} from '#validators'; +import {isContributionList, isDate, isStringNonEmpty} from '#validators'; import {exposeConstant, exposeDependencyOrContinue} from '#composite/control-flow'; import {withResolvedContribs} from '#composite/wiki-data'; @@ -30,6 +30,11 @@ export default templateCompositeFrom({ validate: isDate, acceptsNull: true, }), + + artistProperty: input({ + validate: isStringNonEmpty, + defaultValue: null, + }), }, update: {validate: isContributionList}, @@ -38,6 +43,7 @@ export default templateCompositeFrom({ withResolvedContribs({ from: input.updateValue(), thingProperty: input.thisProperty(), + artistProperty: input('artistProperty'), date: input('date'), }), diff --git a/src/data/things/contribution.js b/src/data/things/contribution.js index 9d6a97114..b542dcf39 100644 --- a/src/data/things/contribution.js +++ b/src/data/things/contribution.js @@ -35,6 +35,11 @@ export class Contribution extends Thing { update: {validate: isStringNonEmpty}, }, + artistProperty: { + flags: {update: true, expose: true}, + update: {validate: isStringNonEmpty}, + }, + date: simpleDate(), artist: [ From 8f70f3f5907384928bde5dae708126da421157db Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:14:26 -0300 Subject: [PATCH 04/29] data: set artistProperty on forwards contribution lists --- src/data/things/album.js | 8 ++++++++ src/data/things/flash.js | 1 + src/data/things/track.js | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/src/data/things/album.js b/src/data/things/album.js index ae5226ba7..a00219465 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -142,6 +142,7 @@ export class Album extends Thing { artistContribs: contributionList({ date: 'date', + artistProperty: input.value('albumArtistContributions'), }), coverArtistContribs: [ @@ -151,6 +152,7 @@ export class Album extends Thing { contributionList({ date: '#coverArtDate', + artistProperty: input.value('albumCoverArtistContributions'), }), ], @@ -158,6 +160,10 @@ export class Album extends Thing { // May be null, indicating cover art was added for tracks on the date // each track specifies, or else the track's own release date. date: 'trackArtDate', + + // This is the "correct" value, but it gets overwritten - with the same + // value - regardless. + artistProperty: input.value('trackCoverArtistContributions'), }), wallpaperArtistContribs: [ @@ -167,6 +173,7 @@ export class Album extends Thing { contributionList({ date: '#coverArtDate', + artistProperty: input.value('albumWallpaperArtistContributions'), }), ], @@ -177,6 +184,7 @@ export class Album extends Thing { contributionList({ date: '#coverArtDate', + artistProperty: input.value('albumBannerArtistContributions'), }), ], diff --git a/src/data/things/flash.js b/src/data/things/flash.js index 7d37ed81a..89e59fe7f 100644 --- a/src/data/things/flash.js +++ b/src/data/things/flash.js @@ -100,6 +100,7 @@ export class Flash extends Thing { contributorContribs: contributionList({ date: 'date', + artistProperty: input.value('flashContributorContributions'), }), featuredTracks: referenceList({ diff --git a/src/data/things/track.js b/src/data/things/track.js index 28a784f5b..4aaf364ce 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -203,6 +203,7 @@ export class Track extends Thing { withResolvedContribs({ from: input.updateValue({validate: isContributionList}), thingProperty: input.thisProperty(), + artistProperty: input.value('trackArtistContributions'), date: '#date', }).outputs({ '#resolvedContribs': '#artistContribs', @@ -219,6 +220,7 @@ export class Track extends Thing { withRecontextualizedContributionList({ list: '#album.artistContribs', + artistProperty: input.value('trackArtistContributions'), }), withRedatedContributionList({ @@ -236,6 +238,7 @@ export class Track extends Thing { contributionList({ date: '#date', + artistProperty: input.value('trackContributorContributions'), }), ], @@ -254,6 +257,7 @@ export class Track extends Thing { withResolvedContribs({ from: input.updateValue({validate: isContributionList}), thingProperty: input.thisProperty(), + artistProperty: input.value('trackCoverArtistContributions'), date: '#trackArtDate', }).outputs({ '#resolvedContribs': '#coverArtistContribs', @@ -270,6 +274,7 @@ export class Track extends Thing { withRecontextualizedContributionList({ list: '#album.trackCoverArtistContribs', + artistProperty: input.value('trackCoverArtistContributions'), }), withRedatedContributionList({ From 1c81eeda84cb0c0a7b009b1bf8d38cf059ec690f Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:15:18 -0300 Subject: [PATCH 05/29] data: withContributionArtist: depend on 'artist' prop by default --- .../things/contribution/withContributionArtist.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/data/composite/things/contribution/withContributionArtist.js b/src/data/composite/things/contribution/withContributionArtist.js index 9e588936a..5a611c1af 100644 --- a/src/data/composite/things/contribution/withContributionArtist.js +++ b/src/data/composite/things/contribution/withContributionArtist.js @@ -5,10 +5,13 @@ import {withPropertyFromObject} from '#composite/data'; import {withResolvedReference} from '#composite/wiki-data'; export default templateCompositeFrom({ - annotation: `withOwnContributionArtist`, + annotation: `withContributionArtist`, inputs: { - ref: input({type: 'string'}), + ref: input({ + type: 'string', + defaultDependency: 'artist', + }), }, outputs: ['#artist'], From 3a0701f3c76da228d27da2140673620615004037 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:16:07 -0300 Subject: [PATCH 06/29] data: withContainingReverseContributionList --- .../composite/things/contribution/index.js | 1 + .../withContainingReverseContributionList.js | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/data/composite/things/contribution/withContainingReverseContributionList.js diff --git a/src/data/composite/things/contribution/index.js b/src/data/composite/things/contribution/index.js index 2c812644d..9b22be2ef 100644 --- a/src/data/composite/things/contribution/index.js +++ b/src/data/composite/things/contribution/index.js @@ -1,6 +1,7 @@ export {default as inheritFromContributionPresets} from './inheritFromContributionPresets.js'; export {default as thingPropertyMatches} from './thingPropertyMatches.js'; export {default as thingReferenceTypeMatches} from './thingReferenceTypeMatches.js'; +export {default as withContainingReverseContributionList} from './withContainingReverseContributionList.js'; export {default as withContributionArtist} from './withContributionArtist.js'; export {default as withContributionContext} from './withContributionContext.js'; export {default as withMatchingContributionPresets} from './withMatchingContributionPresets.js'; diff --git a/src/data/composite/things/contribution/withContainingReverseContributionList.js b/src/data/composite/things/contribution/withContainingReverseContributionList.js new file mode 100644 index 000000000..56704c8b9 --- /dev/null +++ b/src/data/composite/things/contribution/withContainingReverseContributionList.js @@ -0,0 +1,40 @@ +// Get the artist's contribution list containing this property. + +import {input, templateCompositeFrom} from '#composite'; + +import {raiseOutputWithoutDependency} from '#composite/control-flow'; +import {withPropertyFromObject} from '#composite/data'; + +import withContributionArtist from './withContributionArtist.js'; + +export default templateCompositeFrom({ + annotation: `withContainingReverseContributionList`, + + inputs: { + artistProperty: input({ + defaultDependency: 'artistProperty', + acceptsNull: true, + }), + }, + + outputs: ['#containingReverseContributionList'], + + steps: () => [ + raiseOutputWithoutDependency({ + dependency: input('artistProperty'), + output: input.value({ + ['#containingReverseContributionList']: + null, + }), + }), + + withContributionArtist(), + + withPropertyFromObject({ + object: '#artist', + property: input('artistProperty'), + }).outputs({ + ['#value']: '#containingReverseContributionList', + }), + ], +}); From d2422a1ea2c00a49358294b414ce874c744e7fc1 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:16:41 -0300 Subject: [PATCH 07/29] data: Contribution.{previous,next}BySameArtist --- src/data/things/contribution.js | 43 ++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/data/things/contribution.js b/src/data/things/contribution.js index b542dcf39..79acf1e19 100644 --- a/src/data/things/contribution.js +++ b/src/data/things/contribution.js @@ -8,7 +8,7 @@ import Thing from '#thing'; import {isStringNonEmpty, isThing, validateReference} from '#validators'; import {exitWithoutDependency, exposeDependency} from '#composite/control-flow'; -import {withPropertyFromObject} from '#composite/data'; +import {withNearbyItemFromList, withPropertyFromObject} from '#composite/data'; import {withResolvedReference} from '#composite/wiki-data'; import {flag, simpleDate} from '#composite/wiki-properties'; @@ -16,6 +16,7 @@ import { inheritFromContributionPresets, thingPropertyMatches, thingReferenceTypeMatches, + withContainingReverseContributionList, withContributionArtist, withContributionContext, withMatchingContributionPresets, @@ -160,6 +161,46 @@ export class Contribution extends Thing { isForFlash: thingReferenceTypeMatches({ value: input.value('flash'), }), + + previousBySameArtist: [ + withContainingReverseContributionList().outputs({ + '#containingReverseContributionList': '#list', + }), + + exitWithoutDependency({ + dependency: '#list', + }), + + withNearbyItemFromList({ + list: '#list', + item: input.myself(), + offset: input.value(-1), + }), + + exposeDependency({ + dependency: '#nearbyItem', + }), + ], + + nextBySameArtist: [ + withContainingReverseContributionList().outputs({ + '#containingReverseContributionList': '#list', + }), + + exitWithoutDependency({ + dependency: '#list', + }), + + withNearbyItemFromList({ + list: '#list', + item: input.myself(), + offset: input.value(+1), + }), + + exposeDependency({ + dependency: '#nearbyItem', + }), + ], }); [inspect.custom](depth, options, inspect) { From 4a4fe9e06a2f2471a860e5b4d83b22b74d1b86dc Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:17:08 -0300 Subject: [PATCH 08/29] content: linkAnythingMan It lives! --- src/content/dependencies/linkAnythingMan.js | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/content/dependencies/linkAnythingMan.js diff --git a/src/content/dependencies/linkAnythingMan.js b/src/content/dependencies/linkAnythingMan.js new file mode 100644 index 000000000..d46974039 --- /dev/null +++ b/src/content/dependencies/linkAnythingMan.js @@ -0,0 +1,25 @@ +export default { + contentDependencies: [ + 'linkAlbum', + 'linkFlash', + 'linkTrack', + ], + + query: (thing) => ({ + referenceType: thing.constructor[Symbol.for('Thing.referenceType')], + }), + + relations: (relation, query, thing) => ({ + link: + (query.referenceType === 'album' + ? relation('linkAlbum', thing) + : query.referenceType === 'flash' + ? relation('linkFlash', thing) + : query.referenceType === 'track' + ? relation('linkTrack', thing) + : null), + }), + + generate: (relations) => + relations.link, +}; From 319963e80ed98b5900102772b20c14c49dda96b5 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:19:07 -0300 Subject: [PATCH 09/29] content: linkContribution: refactor for multipart tooltip content --- src/content/dependencies/linkContribution.js | 78 +++++++++++--------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index 1a51c3878..c17cc45a8 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -50,15 +50,55 @@ export default { }, generate(data, relations, slots, {html, language}) { + const capsule = language.encapsulate('misc.artistLink'); + const hasContribution = !!(slots.showContribution && data.contribution); const hasExternalIcons = !!(slots.showIcons && relations.artistIcons); const parts = ['misc.artistLink']; const options = {}; + const tooltipContent = []; + + if (hasExternalIcons && slots.iconMode === 'tooltip') { + tooltipContent.push( + stitchArrays({ + icon: relations.artistIcons, + url: data.urls, + }).map(({icon, url}) => { + icon.setSlots({ + context: 'artist', + withText: true, + }); + + let platformText = + language.formatExternalLink(url, { + context: 'artist', + style: 'platform', + }); + + // This is a pretty ridiculous hack, but we currently + // don't have a way of telling formatExternalLink to *not* + // use the fallback string, which just formats the URL as + // its host/domain... so is technically detectable. + if (platformText.toString() === (new URL(url)).host) { + platformText = + language.$(capsule, 'noExternalLinkPlatformName'); + } + + const platformSpan = + html.tag('span', {class: 'icon-platform'}, + platformText); + + return [icon, platformSpan]; + })); + } + + // TODO: It probably shouldn't be necessary to do an isBlank call here. options.artist = - (hasExternalIcons && slots.iconMode === 'tooltip' - ? relations.textWithTooltip.slots({ + (html.isBlank(tooltipContent) + ? relations.artistLink + : relations.textWithTooltip.slots({ customInteractionCue: true, text: @@ -75,39 +115,9 @@ export default { {[html.joinChildren]: ''}, content: - stitchArrays({ - icon: relations.artistIcons, - url: data.urls, - }).map(({icon, url}) => { - icon.setSlots({ - context: 'artist', - withText: true, - }); - - let platformText = - language.formatExternalLink(url, { - context: 'artist', - style: 'platform', - }); - - // This is a pretty ridiculous hack, but we currently - // don't have a way of telling formatExternalLink to *not* - // use the fallback string, which just formats the URL as - // its host/domain... so is technically detectable. - if (platformText.toString() === (new URL(url)).host) { - platformText = - language.$('misc.artistLink.noExternalLinkPlatformName'); - } - - const platformSpan = - html.tag('span', {class: 'icon-platform'}, - platformText); - - return [icon, platformSpan]; - }), + tooltipContent, }), - }) - : relations.artistLink); + })); if (hasContribution) { parts.push('withContribution'); From 2e44819833bdb64f8ada15faa8331368355f13ed Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:20:01 -0300 Subject: [PATCH 10/29] content, css: linkContribution: 'showChronology' basic impl. --- src/content/dependencies/linkContribution.js | 45 ++++++++++++++++++++ src/static/css/site.css | 14 ++++++ src/strings-default.yaml | 4 ++ 3 files changed, 63 insertions(+) diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index c17cc45a8..aee93d60c 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -5,6 +5,7 @@ export default { 'generateTextWithTooltip', 'generateTooltip', 'linkArtist', + 'linkAnythingMan', 'linkExternalAsIcon', ], @@ -22,6 +23,26 @@ export default { relations.tooltip = relation('generateTooltip'); + let previous = contribution; + while (previous && previous.thing === contribution.thing) { + previous = previous.previousBySameArtist; + } + + let next = contribution; + while (next && next.thing === contribution.thing) { + next = next.nextBySameArtist; + } + + if (previous) { + relations.previousLink = + relation('linkAnythingMan', previous.thing); + } + + if (next) { + relations.nextLink = + relation('linkAnythingMan', next.thing); + } + if (!empty(contribution.artist.urls)) { relations.artistIcons = contribution.artist.urls @@ -41,6 +62,7 @@ export default { slots: { showContribution: {type: 'boolean', default: false}, showIcons: {type: 'boolean', default: false}, + showChronology: {type: 'boolean', default: false}, preventWrapping: {type: 'boolean', default: true}, iconMode: { @@ -94,6 +116,29 @@ export default { })); } + if (slots.showChronology) { + tooltipContent.push( + language.encapsulate(capsule, 'chronology', capsule => [ + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, + + language.$(capsule, 'previous', { + [language.onlyIfOptions]: ['thing'], + + thing: relations.previousLink, + })), + + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, + + language.$(capsule, 'next', { + [language.onlyIfOptions]: ['thing'], + + thing: relations.nextLink, + })), + ])); + } + // TODO: It probably shouldn't be necessary to do an isBlank call here. options.artist = (html.isBlank(tooltipContent) diff --git a/src/static/css/site.css b/src/static/css/site.css index dc99bcc5b..84ed2ea74 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -950,6 +950,20 @@ li:not(:first-child:last-child) .tooltip, grid-column-end: icon-end; } +.icons-tooltip .chronology-link { + grid-column-start: icon-start; + grid-column-end: domain-end; + padding-left: 5px; + padding-right: 3px; + font-size: 0.85em; +} + +.icons-tooltip .chronology-link:nth-child(1 of .chronology-link) { + border-top: 1px dotted var(--primary-color); + margin-top: 2px; + padding-top: 2px; +} + .icons-tooltip .icon-platform { display: none; diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 8429c4cd9..cb3b5e0c2 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -486,6 +486,10 @@ misc: # isn't a specially detected web platform. noExternalLinkPlatformName: "Other" + chronology: + previous: "← {THING}" + next: "→ {THING}" + # chronology: # # "Chronology links" are a section that appear in the nav bar for From f7211335ce22916bc3665098e4a6570defe114eb Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 17 Jun 2024 21:20:28 -0300 Subject: [PATCH 11/29] content: show chronology links in artist tooltips --- src/content/dependencies/generateContributionList.js | 1 + .../dependencies/generateReleaseInfoContributionsLine.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/content/dependencies/generateContributionList.js b/src/content/dependencies/generateContributionList.js index 0c4ef87a0..b52afc6e5 100644 --- a/src/content/dependencies/generateContributionList.js +++ b/src/content/dependencies/generateContributionList.js @@ -18,6 +18,7 @@ export default { contributionLink.slots({ showIcons: true, showContribution: true, + showChronology: true, preventWrapping: false, iconMode: 'tooltip', })))), diff --git a/src/content/dependencies/generateReleaseInfoContributionsLine.js b/src/content/dependencies/generateReleaseInfoContributionsLine.js index 2e6c47091..3aa511277 100644 --- a/src/content/dependencies/generateReleaseInfoContributionsLine.js +++ b/src/content/dependencies/generateReleaseInfoContributionsLine.js @@ -21,6 +21,7 @@ export default { showContribution: {type: 'boolean', default: true}, showIcons: {type: 'boolean', default: true}, + showChronology: {type: 'boolean', default: true}, }, generate(relations, slots, {html, language}) { @@ -33,8 +34,9 @@ export default { language.formatConjunctionList( relations.contributionLinks.map(link => link.slots({ - showContribution: slots.showContribution, showIcons: slots.showIcons, + showContribution: slots.showContribution, + showChronology: slots.showChronology, iconMode: 'tooltip', }))), }); From 6ab709f2ff923f6bc0116b413f014f0e2017a009 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 09:17:10 -0300 Subject: [PATCH 12/29] content: generateExternalIcon, extract from linkExternalAsIcon --- .../dependencies/generateExternalIcon.js | 28 +++++++++++++++ src/content/dependencies/linkContribution.js | 1 - .../dependencies/linkExternalAsIcon.js | 34 ++++++++----------- 3 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 src/content/dependencies/generateExternalIcon.js diff --git a/src/content/dependencies/generateExternalIcon.js b/src/content/dependencies/generateExternalIcon.js new file mode 100644 index 000000000..cb65a00ac --- /dev/null +++ b/src/content/dependencies/generateExternalIcon.js @@ -0,0 +1,28 @@ +import {isExternalLinkContext} from '#external-links'; + +export default { + extraDependencies: ['html', 'language', 'to'], + + data: (url) => ({url}), + + slots: { + context: { + // This awkward syntax is because the slot descriptor validator can't + // differentiate between a function that returns a validator (the usual + // syntax) and a function that is itself a validator. + validate: () => isExternalLinkContext, + default: 'generic', + }, + }, + + generate: (data, slots, {html, language, to}) => + html.tag('svg', + html.tag('use', { + href: + to('staticMisc.icon', + language.formatExternalLink(data.url, { + style: 'icon-id', + context: slots.context, + })), + })), +}; diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index aee93d60c..4e1c89773 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -90,7 +90,6 @@ export default { }).map(({icon, url}) => { icon.setSlots({ context: 'artist', - withText: true, }); let platformText = diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js index e2ce4b3ce..9b146a4cd 100644 --- a/src/content/dependencies/linkExternalAsIcon.js +++ b/src/content/dependencies/linkExternalAsIcon.js @@ -1,7 +1,13 @@ import {isExternalLinkContext} from '#external-links'; export default { - extraDependencies: ['html', 'language', 'to'], + contentDependencies: ['generateExternalIcon'], + extraDependencies: ['html', 'language'], + + relations: (relation, url) => ({ + icon: + relation('generateExternalIcon', url), + }), data: (url) => ({url}), @@ -13,39 +19,27 @@ export default { validate: () => isExternalLinkContext, default: 'generic', }, - - withText: {type: 'boolean'}, }, - generate(data, slots, {html, language, to}) { + generate(data, relations, slots, {html, language}) { const format = style => language.formatExternalLink(data.url, {style, context: slots.context}); const platformText = format('platform'); const handleText = format('handle'); - const iconId = format('icon-id'); - return html.tag('a', {class: 'icon'}, - {href: data.url}, - - slots.withText && + return ( + html.tag('a', {class: 'icon'}, + {href: data.url}, {class: 'has-text'}, - [ - html.tag('svg', [ - !slots.withText && - html.tag('title', platformText), - - html.tag('use', { - href: to('staticMisc.icon', iconId), - }), - ]), + [ + relations.icon, - slots.withText && html.tag('span', {class: 'icon-text'}, (html.isBlank(handleText) ? platformText : handleText)), - ]); + ])); }, }; From 78b59295d0e2419b52b047921754b0ba9c9c7f27 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 09:25:11 -0300 Subject: [PATCH 13/29] content: generateExternal{Handle,Platform} --- .../dependencies/generateExternalHandle.js | 20 ++++++++++ .../dependencies/generateExternalIcon.js | 3 -- .../dependencies/generateExternalPlatform.js | 20 ++++++++++ .../dependencies/linkExternalAsIcon.js | 38 ++++++++++++------- 4 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 src/content/dependencies/generateExternalHandle.js create mode 100644 src/content/dependencies/generateExternalPlatform.js diff --git a/src/content/dependencies/generateExternalHandle.js b/src/content/dependencies/generateExternalHandle.js new file mode 100644 index 000000000..8c0368a4a --- /dev/null +++ b/src/content/dependencies/generateExternalHandle.js @@ -0,0 +1,20 @@ +import {isExternalLinkContext} from '#external-links'; + +export default { + extraDependencies: ['html', 'language'], + + data: (url) => ({url}), + + slots: { + context: { + validate: () => isExternalLinkContext, + default: 'generic', + }, + }, + + generate: (data, slots, {language}) => + language.formatExternalLink(data.url, { + style: 'handle', + context: slots.context, + }), +}; diff --git a/src/content/dependencies/generateExternalIcon.js b/src/content/dependencies/generateExternalIcon.js index cb65a00ac..9f65a2751 100644 --- a/src/content/dependencies/generateExternalIcon.js +++ b/src/content/dependencies/generateExternalIcon.js @@ -7,9 +7,6 @@ export default { slots: { context: { - // This awkward syntax is because the slot descriptor validator can't - // differentiate between a function that returns a validator (the usual - // syntax) and a function that is itself a validator. validate: () => isExternalLinkContext, default: 'generic', }, diff --git a/src/content/dependencies/generateExternalPlatform.js b/src/content/dependencies/generateExternalPlatform.js new file mode 100644 index 000000000..c4f63ecf9 --- /dev/null +++ b/src/content/dependencies/generateExternalPlatform.js @@ -0,0 +1,20 @@ +import {isExternalLinkContext} from '#external-links'; + +export default { + extraDependencies: ['html', 'language'], + + data: (url) => ({url}), + + slots: { + context: { + validate: () => isExternalLinkContext, + default: 'generic', + }, + }, + + generate: (data, slots, {language}) => + language.formatExternalLink(data.url, { + style: 'platform', + context: slots.context, + }), +}; diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js index 9b146a4cd..0217e9d6d 100644 --- a/src/content/dependencies/linkExternalAsIcon.js +++ b/src/content/dependencies/linkExternalAsIcon.js @@ -1,32 +1,42 @@ import {isExternalLinkContext} from '#external-links'; export default { - contentDependencies: ['generateExternalIcon'], - extraDependencies: ['html', 'language'], + contentDependencies: [ + 'generateExternalHandle', + 'generateExternalIcon', + 'generateExternalPlatform', + ], + + extraDependencies: ['html'], relations: (relation, url) => ({ icon: relation('generateExternalIcon', url), + + handle: + relation('generateExternalHandle', url), + + platform: + relation('generateExternalPlatform', url), }), data: (url) => ({url}), slots: { context: { - // This awkward syntax is because the slot descriptor validator can't - // differentiate between a function that returns a validator (the usual - // syntax) and a function that is itself a validator. validate: () => isExternalLinkContext, default: 'generic', }, }, - generate(data, relations, slots, {html, language}) { - const format = style => - language.formatExternalLink(data.url, {style, context: slots.context}); - - const platformText = format('platform'); - const handleText = format('handle'); + generate(data, relations, slots, {html}) { + for (const template of [ + relations.icon, + relations.handle, + relations.platform, + ]) { + template.setSlot('context', slots.context); + } return ( html.tag('a', {class: 'icon'}, @@ -37,9 +47,9 @@ export default { relations.icon, html.tag('span', {class: 'icon-text'}, - (html.isBlank(handleText) - ? platformText - : handleText)), + (html.isBlank(relations.handle) + ? relations.platform + : relations.handle)), ])); }, }; From 89b967e966335dcd8b228d3ae80c8e7fdb82d96e Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 09:53:04 -0300 Subject: [PATCH 14/29] content: generateTooltip: blank if no content --- src/content/dependencies/generateTooltip.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/content/dependencies/generateTooltip.js b/src/content/dependencies/generateTooltip.js index 81f74aec1..8314d33ce 100644 --- a/src/content/dependencies/generateTooltip.js +++ b/src/content/dependencies/generateTooltip.js @@ -21,10 +21,13 @@ export default { generate: (slots, {html}) => html.tag('span', {class: 'tooltip'}, {[html.noEdgeWhitespace]: true}, + {[html.onlyIfContent]: true}, slots.attributes, html.tag('span', {class: 'tooltip-content'}, {[html.noEdgeWhitespace]: true}, + {[html.onlyIfContent]: true}, slots.contentAttributes, + slots.content)), }; From 65690d6c64298ba505192054b97add314091485e Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 09:57:51 -0300 Subject: [PATCH 15/29] content: generateContributionTooltip, etc --- .../dependencies/generateContributionList.js | 3 +- .../generateContributionTooltip.js | 42 ++++ ...ateContributionTooltipChronologySection.js | 51 +++++ ...eContributionTooltipExternalLinkSection.js | 48 +++++ .../generateReleaseInfoContributionsLine.js | 5 +- src/content/dependencies/generateTrackList.js | 14 +- src/content/dependencies/linkContribution.js | 199 +++--------------- src/strings-default.yaml | 6 - 8 files changed, 180 insertions(+), 188 deletions(-) create mode 100644 src/content/dependencies/generateContributionTooltip.js create mode 100644 src/content/dependencies/generateContributionTooltipChronologySection.js create mode 100644 src/content/dependencies/generateContributionTooltipExternalLinkSection.js diff --git a/src/content/dependencies/generateContributionList.js b/src/content/dependencies/generateContributionList.js index b52afc6e5..f22740b02 100644 --- a/src/content/dependencies/generateContributionList.js +++ b/src/content/dependencies/generateContributionList.js @@ -16,10 +16,9 @@ export default { .map(contributionLink => html.tag('li', contributionLink.slots({ - showIcons: true, + showExternalLinks: true, showContribution: true, showChronology: true, preventWrapping: false, - iconMode: 'tooltip', })))), }; diff --git a/src/content/dependencies/generateContributionTooltip.js b/src/content/dependencies/generateContributionTooltip.js new file mode 100644 index 000000000..c4df875cb --- /dev/null +++ b/src/content/dependencies/generateContributionTooltip.js @@ -0,0 +1,42 @@ +export default { + contentDependencies: [ + 'generateContributionTooltipChronologySection', + 'generateContributionTooltipExternalLinkSection', + 'generateTooltip', + ], + + extraDependencies: ['html'], + + relations: (relation, contribution) => ({ + tooltip: + relation('generateTooltip'), + + externalLinkSection: + relation('generateContributionTooltipExternalLinkSection', contribution), + + chronologySection: + relation('generateContributionTooltipChronologySection', contribution), + }), + + slots: { + showExternalLinks: {type: 'boolean'}, + showChronology: {type: 'boolean'}, + }, + + generate: (relations, slots, {html}) => + relations.tooltip.slots({ + attributes: + {class: ['icons', 'icons-tooltip']}, + + contentAttributes: + {[html.joinChildren]: ''}, + + content: [ + slots.showExternalLinks && + relations.externalLinkSection, + + slots.showChronology && + relations.chronologySection, + ], + }), +}; diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js new file mode 100644 index 000000000..85b19be94 --- /dev/null +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -0,0 +1,51 @@ +export default { + contentDependencies: ['linkAnythingMan'], + extraDependencies: ['html', 'language'], + + query(contribution) { + let previous = contribution; + while (previous && previous.thing === contribution.thing) { + previous = previous.previousBySameArtist; + } + + let next = contribution; + while (next && next.thing === contribution.thing) { + next = next.nextBySameArtist; + } + + return {previous, next}; + }, + + relations: (relation, query, _contribution) => ({ + previousLink: + (query.previous + ? relation('linkAnythingMan', query.previous.thing) + : null), + + nextLink: + (query.next + ? relation('linkAnythingMan', query.next.thing) + : null), + }), + + generate: (relations, {html, language}) => + language.encapsulate('misc.artistLink.chronology', capsule => [ + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, + + language.$(capsule, 'previous', { + [language.onlyIfOptions]: ['thing'], + + thing: relations.previousLink, + })), + + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, + + language.$(capsule, 'next', { + [language.onlyIfOptions]: ['thing'], + + thing: relations.nextLink, + })), + ]), +}; diff --git a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js new file mode 100644 index 000000000..3a1244126 --- /dev/null +++ b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js @@ -0,0 +1,48 @@ +import {stitchArrays} from '#sugar'; + +export default { + contentDependencies: ['linkExternalAsIcon'], + extraDependencies: ['html', 'language'], + + relations: (relation, contribution) => ({ + artistIcons: + contribution.artist.urls + .map(url => relation('linkExternalAsIcon', url)), + }), + + data: (contribution) => ({ + urls: contribution.artist.urls, + }), + + generate: (data, relations, {html, language}) => + language.encapsulate('misc.artistLink', capsule => + stitchArrays({ + icon: relations.artistIcons, + url: data.urls, + }).map(({icon, url}) => { + icon.setSlots({ + context: 'artist', + }); + + let platformText = + language.formatExternalLink(url, { + context: 'artist', + style: 'platform', + }); + + // This is a pretty ridiculous hack, but we currently + // don't have a way of telling formatExternalLink to *not* + // use the fallback string, which just formats the URL as + // its host/domain... so is technically detectable. + if (platformText.toString() === (new URL(url)).host) { + platformText = + language.$(capsule, 'noExternalLinkPlatformName'); + } + + const platformSpan = + html.tag('span', {class: 'icon-platform'}, + platformText); + + return [icon, platformSpan]; + })), +}; diff --git a/src/content/dependencies/generateReleaseInfoContributionsLine.js b/src/content/dependencies/generateReleaseInfoContributionsLine.js index 3aa511277..ed60886b3 100644 --- a/src/content/dependencies/generateReleaseInfoContributionsLine.js +++ b/src/content/dependencies/generateReleaseInfoContributionsLine.js @@ -20,7 +20,7 @@ export default { stringKey: {type: 'string'}, showContribution: {type: 'boolean', default: true}, - showIcons: {type: 'boolean', default: true}, + showExternalLinks: {type: 'boolean', default: true}, showChronology: {type: 'boolean', default: true}, }, @@ -34,10 +34,9 @@ export default { language.formatConjunctionList( relations.contributionLinks.map(link => link.slots({ - showIcons: slots.showIcons, showContribution: slots.showContribution, + showExternalLinks: slots.showExternalLinks, showChronology: slots.showChronology, - iconMode: 'tooltip', }))), }); }, diff --git a/src/content/dependencies/generateTrackList.js b/src/content/dependencies/generateTrackList.js index bc3b6035f..7c3b11c15 100644 --- a/src/content/dependencies/generateTrackList.js +++ b/src/content/dependencies/generateTrackList.js @@ -17,12 +17,7 @@ export default { .map(contrib => relation('linkContribution', contrib))), }), - slots: { - showContribution: {type: 'boolean', default: false}, - showIcons: {type: 'boolean', default: false}, - }, - - generate: (relations, slots, {html, language}) => + generate: (relations, {html, language}) => html.tag('ul', {[html.onlyIfContent]: true}, @@ -42,12 +37,7 @@ export default { html.metatag('chunkwrap', {split: ','}, language.$(itemCapsule, 'withArtists.by', { artists: - language.formatConjunctionList( - contributionLinks.map(link => - link.slots({ - showContribution: slots.showContribution, - showIcons: slots.showIcons, - }))), + language.formatConjunctionList(contributionLinks), }))); } diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index 4e1c89773..67a50e5b9 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -1,148 +1,48 @@ -import {empty, stitchArrays} from '#sugar'; - export default { contentDependencies: [ + 'generateContributionTooltip', 'generateTextWithTooltip', - 'generateTooltip', 'linkArtist', - 'linkAnythingMan', - 'linkExternalAsIcon', ], extraDependencies: ['html', 'language'], - relations(relation, contribution) { - const relations = {}; - - relations.artistLink = - relation('linkArtist', contribution.artist); - - relations.textWithTooltip = - relation('generateTextWithTooltip'); - - relations.tooltip = - relation('generateTooltip'); - - let previous = contribution; - while (previous && previous.thing === contribution.thing) { - previous = previous.previousBySameArtist; - } - - let next = contribution; - while (next && next.thing === contribution.thing) { - next = next.nextBySameArtist; - } - - if (previous) { - relations.previousLink = - relation('linkAnythingMan', previous.thing); - } - - if (next) { - relations.nextLink = - relation('linkAnythingMan', next.thing); - } + relations: (relation, contribution) => ({ + artistLink: + relation('linkArtist', contribution.artist), - if (!empty(contribution.artist.urls)) { - relations.artistIcons = - contribution.artist.urls - .map(url => relation('linkExternalAsIcon', url)); - } + textWithTooltip: + relation('generateTextWithTooltip'), - return relations; - }, + tooltip: + relation('generateContributionTooltip', contribution), + }), - data(contribution) { - return { - contribution: contribution.annotation, - urls: contribution.artist.urls, - }; - }, + data: (contribution) => ({ + contribution: contribution.annotation, + urls: contribution.artist.urls, + }), slots: { showContribution: {type: 'boolean', default: false}, - showIcons: {type: 'boolean', default: false}, + showExternalLinks: {type: 'boolean', default: false}, showChronology: {type: 'boolean', default: false}, - preventWrapping: {type: 'boolean', default: true}, - iconMode: { - validate: v => v.is('inline', 'tooltip'), - default: 'inline' - }, + preventWrapping: {type: 'boolean', default: true}, }, - generate(data, relations, slots, {html, language}) { - const capsule = language.encapsulate('misc.artistLink'); - - const hasContribution = !!(slots.showContribution && data.contribution); - const hasExternalIcons = !!(slots.showIcons && relations.artistIcons); - - const parts = ['misc.artistLink']; - const options = {}; - - const tooltipContent = []; - - if (hasExternalIcons && slots.iconMode === 'tooltip') { - tooltipContent.push( - stitchArrays({ - icon: relations.artistIcons, - url: data.urls, - }).map(({icon, url}) => { - icon.setSlots({ - context: 'artist', - }); + generate: (data, relations, slots, {html, language}) => + html.tag('span', {class: 'contribution'}, + {[html.noEdgeWhitespace]: true}, - let platformText = - language.formatExternalLink(url, { - context: 'artist', - style: 'platform', - }); + slots.preventWrapping && + {class: 'nowrap'}, - // This is a pretty ridiculous hack, but we currently - // don't have a way of telling formatExternalLink to *not* - // use the fallback string, which just formats the URL as - // its host/domain... so is technically detectable. - if (platformText.toString() === (new URL(url)).host) { - platformText = - language.$(capsule, 'noExternalLinkPlatformName'); - } + language.encapsulate('misc.artistLink', workingCapsule => { + const workingOptions = {}; - const platformSpan = - html.tag('span', {class: 'icon-platform'}, - platformText); - - return [icon, platformSpan]; - })); - } - - if (slots.showChronology) { - tooltipContent.push( - language.encapsulate(capsule, 'chronology', capsule => [ - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, - - language.$(capsule, 'previous', { - [language.onlyIfOptions]: ['thing'], - - thing: relations.previousLink, - })), - - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, - - language.$(capsule, 'next', { - [language.onlyIfOptions]: ['thing'], - - thing: relations.nextLink, - })), - ])); - } - - // TODO: It probably shouldn't be necessary to do an isBlank call here. - options.artist = - (html.isBlank(tooltipContent) - ? relations.artistLink - : relations.textWithTooltip.slots({ + workingOptions.artist = + relations.textWithTooltip.slots({ customInteractionCue: true, text: @@ -152,48 +52,17 @@ export default { tooltip: relations.tooltip.slots({ - attributes: - {class: ['icons', 'icons-tooltip']}, - - contentAttributes: - {[html.joinChildren]: ''}, - - content: - tooltipContent, + showExternalLinks: slots.showExternalLinks, + showChronology: slots.showChronology, }), - })); - - if (hasContribution) { - parts.push('withContribution'); - options.contrib = data.contribution; - } - - if (hasExternalIcons && slots.iconMode === 'inline') { - parts.push('withExternalLinks'); - options.links = - html.tag('span', {class: ['icons', 'icons-inline']}, - {[html.noEdgeWhitespace]: true}, - language.formatUnitList( - relations.artistIcons - .slice(0, 4) - .map(icon => icon.slot('context', 'artist')))); - } + }); - const contributionPart = - language.formatString(...parts, options); + if (slots.showContribution && data.contribution) { + workingCapsule += '.withContribution'; + workingOptions.contrib = + data.contribution; + } - if (!hasContribution && !hasExternalIcons) { - return contributionPart; - } - - return ( - html.tag('span', {class: 'contribution'}, - {[html.noEdgeWhitespace]: true}, - - parts.length > 1 && - slots.preventWrapping && - {class: 'nowrap'}, - - contributionPart)); - }, + return language.formatString(workingCapsule, workingOptions); + })), }; diff --git a/src/strings-default.yaml b/src/strings-default.yaml index cb3b5e0c2..29aba5535 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -476,12 +476,6 @@ misc: # Contribution to a track, artwork, or other thing. withContribution: "{ARTIST} ({CONTRIB})" - # External links to visit the artist's own websites or profiles. - withExternalLinks: "{ARTIST} ({LINKS})" - - # Combination of above. - withContribution.withExternalLinks: "{ARTIST} ({CONTRIB}) ({LINKS})" - # Displayed in an artist's tooltip, if one of their URLs # isn't a specially detected web platform. noExternalLinkPlatformName: "Other" From 2fb746215da85fc7e596605b490cbbdf44d51b95 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 10:05:03 -0300 Subject: [PATCH 16/29] content, css: generateContributionTooltip: dynamic divider line --- .../generateContributionTooltip.js | 6 ++- ...ateContributionTooltipChronologySection.js | 29 +++++----- ...eContributionTooltipExternalLinkSection.js | 53 ++++++++++--------- src/static/css/site.css | 9 ++-- 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/content/dependencies/generateContributionTooltip.js b/src/content/dependencies/generateContributionTooltip.js index c4df875cb..67d6166e1 100644 --- a/src/content/dependencies/generateContributionTooltip.js +++ b/src/content/dependencies/generateContributionTooltip.js @@ -28,8 +28,10 @@ export default { attributes: {class: ['icons', 'icons-tooltip']}, - contentAttributes: - {[html.joinChildren]: ''}, + contentAttributes: { + [html.joinChildren]: + html.tag('span', {class: 'tooltip-divider'}), + }, content: [ slots.showExternalLinks && diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js index 85b19be94..49e5d7836 100644 --- a/src/content/dependencies/generateContributionTooltipChronologySection.js +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -29,23 +29,24 @@ export default { }), generate: (relations, {html, language}) => - language.encapsulate('misc.artistLink.chronology', capsule => [ - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, + language.encapsulate('misc.artistLink.chronology', capsule => + html.tags([ + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, - language.$(capsule, 'previous', { - [language.onlyIfOptions]: ['thing'], + language.$(capsule, 'previous', { + [language.onlyIfOptions]: ['thing'], - thing: relations.previousLink, - })), + thing: relations.previousLink, + })), - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, + html.tag('span', {class: 'chronology-link'}, + {[html.onlyIfContent]: true}, - language.$(capsule, 'next', { - [language.onlyIfOptions]: ['thing'], + language.$(capsule, 'next', { + [language.onlyIfOptions]: ['thing'], - thing: relations.nextLink, - })), - ]), + thing: relations.nextLink, + })), + ])), }; diff --git a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js index 3a1244126..2a2b760f9 100644 --- a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js +++ b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js @@ -16,33 +16,34 @@ export default { generate: (data, relations, {html, language}) => language.encapsulate('misc.artistLink', capsule => - stitchArrays({ - icon: relations.artistIcons, - url: data.urls, - }).map(({icon, url}) => { - icon.setSlots({ - context: 'artist', - }); - - let platformText = - language.formatExternalLink(url, { + html.tags( + stitchArrays({ + icon: relations.artistIcons, + url: data.urls, + }).map(({icon, url}) => { + icon.setSlots({ context: 'artist', - style: 'platform', }); - // This is a pretty ridiculous hack, but we currently - // don't have a way of telling formatExternalLink to *not* - // use the fallback string, which just formats the URL as - // its host/domain... so is technically detectable. - if (platformText.toString() === (new URL(url)).host) { - platformText = - language.$(capsule, 'noExternalLinkPlatformName'); - } - - const platformSpan = - html.tag('span', {class: 'icon-platform'}, - platformText); - - return [icon, platformSpan]; - })), + let platformText = + language.formatExternalLink(url, { + context: 'artist', + style: 'platform', + }); + + // This is a pretty ridiculous hack, but we currently + // don't have a way of telling formatExternalLink to *not* + // use the fallback string, which just formats the URL as + // its host/domain... so is technically detectable. + if (platformText.toString() === (new URL(url)).host) { + platformText = + language.$(capsule, 'noExternalLinkPlatformName'); + } + + const platformSpan = + html.tag('span', {class: 'icon-platform'}, + platformText); + + return [icon, platformSpan]; + }))), }; diff --git a/src/static/css/site.css b/src/static/css/site.css index 84ed2ea74..321746ca1 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -958,10 +958,13 @@ li:not(:first-child:last-child) .tooltip, font-size: 0.85em; } -.icons-tooltip .chronology-link:nth-child(1 of .chronology-link) { +.icons-tooltip .tooltip-divider { + grid-column-start: icon-start; + grid-column-end: domain-end; + border-top: 1px dotted var(--primary-color); - margin-top: 2px; - padding-top: 2px; + margin-top: 3px; + margin-bottom: 4px; } .icons-tooltip .icon-platform { From 0f6eea20e520dfe2715cfd35c434e69a2cb60e71 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 10:21:42 -0300 Subject: [PATCH 17/29] content: gCTExternalLinkSection: inline linkExternalAsIcon behavior --- ...eContributionTooltipExternalLinkSection.js | 70 ++++++++++++------- .../dependencies/linkExternalAsIcon.js | 55 --------------- 2 files changed, 45 insertions(+), 80 deletions(-) delete mode 100644 src/content/dependencies/linkExternalAsIcon.js diff --git a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js index 2a2b760f9..a9d17457d 100644 --- a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js +++ b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js @@ -1,13 +1,26 @@ import {stitchArrays} from '#sugar'; export default { - contentDependencies: ['linkExternalAsIcon'], + contentDependencies: [ + 'generateExternalHandle', + 'generateExternalIcon', + 'generateExternalPlatform', + ], + extraDependencies: ['html', 'language'], relations: (relation, contribution) => ({ - artistIcons: + icons: + contribution.artist.urls + .map(url => relation('generateExternalIcon', url)), + + handles: + contribution.artist.urls + .map(url => relation('generateExternalHandle', url)), + + platforms: contribution.artist.urls - .map(url => relation('linkExternalAsIcon', url)), + .map(url => relation('generateExternalPlatform', url)), }), data: (contribution) => ({ @@ -18,32 +31,39 @@ export default { language.encapsulate('misc.artistLink', capsule => html.tags( stitchArrays({ - icon: relations.artistIcons, + icon: relations.icons, + handle: relations.handles, + platform: relations.platforms, url: data.urls, - }).map(({icon, url}) => { - icon.setSlots({ - context: 'artist', - }); - - let platformText = - language.formatExternalLink(url, { - context: 'artist', - style: 'platform', - }); - - // This is a pretty ridiculous hack, but we currently - // don't have a way of telling formatExternalLink to *not* - // use the fallback string, which just formats the URL as - // its host/domain... so is technically detectable. - if (platformText.toString() === (new URL(url)).host) { - platformText = - language.$(capsule, 'noExternalLinkPlatformName'); + }).map(({icon, handle, platform, url}) => { + for (const template of [icon, handle, platform]) { + template.setSlot('context', 'artist'); } - const platformSpan = + return [ + html.tag('a', {class: 'icon'}, + {href: url}, + {class: 'has-text'}, + + [ + icon, + + html.tag('span', {class: 'icon-text'}, + (html.isBlank(handle) + ? platform + : handle)), + ]), + html.tag('span', {class: 'icon-platform'}, - platformText); + // This is a pretty ridiculous hack, but we currently + // don't have a way of telling formatExternalLink to *not* + // use the fallback string, which just formats the URL as + // its host/domain... so is technically detectable. + ((html.resolve(platform, {normalize: 'string'}) === + (new URL(url)).host) - return [icon, platformSpan]; + ? language.$(capsule, 'noExternalLinkPlatformName') + : platform)), + ]; }))), }; diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js deleted file mode 100644 index 0217e9d6d..000000000 --- a/src/content/dependencies/linkExternalAsIcon.js +++ /dev/null @@ -1,55 +0,0 @@ -import {isExternalLinkContext} from '#external-links'; - -export default { - contentDependencies: [ - 'generateExternalHandle', - 'generateExternalIcon', - 'generateExternalPlatform', - ], - - extraDependencies: ['html'], - - relations: (relation, url) => ({ - icon: - relation('generateExternalIcon', url), - - handle: - relation('generateExternalHandle', url), - - platform: - relation('generateExternalPlatform', url), - }), - - data: (url) => ({url}), - - slots: { - context: { - validate: () => isExternalLinkContext, - default: 'generic', - }, - }, - - generate(data, relations, slots, {html}) { - for (const template of [ - relations.icon, - relations.handle, - relations.platform, - ]) { - template.setSlot('context', slots.context); - } - - return ( - html.tag('a', {class: 'icon'}, - {href: data.url}, - {class: 'has-text'}, - - [ - relations.icon, - - html.tag('span', {class: 'icon-text'}, - (html.isBlank(relations.handle) - ? relations.platform - : relations.handle)), - ])); - }, -}; From 868ec75f76394a2ee75e01d964c62c44bf30df6f Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 10:46:44 -0300 Subject: [PATCH 18/29] content, css: general tooltip/icon css cleanup --- .../generateContributionTooltip.js | 2 +- ...eContributionTooltipExternalLinkSection.js | 7 +- .../dependencies/generateExternalIcon.js | 19 ++-- src/static/css/site.css | 98 +++++++++---------- src/static/js/client.js | 2 +- 5 files changed, 62 insertions(+), 66 deletions(-) diff --git a/src/content/dependencies/generateContributionTooltip.js b/src/content/dependencies/generateContributionTooltip.js index 67d6166e1..5df881217 100644 --- a/src/content/dependencies/generateContributionTooltip.js +++ b/src/content/dependencies/generateContributionTooltip.js @@ -26,7 +26,7 @@ export default { generate: (relations, slots, {html}) => relations.tooltip.slots({ attributes: - {class: ['icons', 'icons-tooltip']}, + {class: 'contribution-tooltip'}, contentAttributes: { [html.joinChildren]: diff --git a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js index a9d17457d..d43420984 100644 --- a/src/content/dependencies/generateContributionTooltipExternalLinkSection.js +++ b/src/content/dependencies/generateContributionTooltipExternalLinkSection.js @@ -41,20 +41,19 @@ export default { } return [ - html.tag('a', {class: 'icon'}, + html.tag('a', {class: 'external-link'}, {href: url}, - {class: 'has-text'}, [ icon, - html.tag('span', {class: 'icon-text'}, + html.tag('span', {class: 'external-handle'}, (html.isBlank(handle) ? platform : handle)), ]), - html.tag('span', {class: 'icon-platform'}, + html.tag('span', {class: 'external-platform'}, // This is a pretty ridiculous hack, but we currently // don't have a way of telling formatExternalLink to *not* // use the fallback string, which just formats the URL as diff --git a/src/content/dependencies/generateExternalIcon.js b/src/content/dependencies/generateExternalIcon.js index 9f65a2751..637af6587 100644 --- a/src/content/dependencies/generateExternalIcon.js +++ b/src/content/dependencies/generateExternalIcon.js @@ -13,13 +13,14 @@ export default { }, generate: (data, slots, {html, language, to}) => - html.tag('svg', - html.tag('use', { - href: - to('staticMisc.icon', - language.formatExternalLink(data.url, { - style: 'icon-id', - context: slots.context, - })), - })), + html.tag('span', {class: 'external-icon'}, + html.tag('svg', + html.tag('use', { + href: + to('staticMisc.icon', + language.formatExternalLink(data.url, { + style: 'icon-id', + context: slots.context, + })), + }))), }; diff --git a/src/static/css/site.css b/src/static/css/site.css index 321746ca1..d226b2815 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -910,7 +910,7 @@ li:not(:first-child:last-child) .tooltip, 0 -2px 4px -2px var(--primary-color) inset; } -.icons-tooltip { +.contribution-tooltip { padding: 3px 6px 6px 6px; left: -34px; } @@ -931,7 +931,7 @@ li:not(:first-child:last-child) .tooltip, margin-right: -120px; } -.icons-tooltip .tooltip-content { +.contribution-tooltip .tooltip-content { padding: 6px 2px 2px 2px; -webkit-user-select: none; @@ -942,59 +942,84 @@ li:not(:first-child:last-child) .tooltip, display: grid; grid-template-columns: - [icon-start] auto [icon-end domain-start] auto [domain-end]; + [icon-start] auto [icon-end handle-start] auto [handle-end platform-start] auto [platform-end]; } -.icons-tooltip .icon { +.contribution-tooltip .external-link { grid-column-start: icon-start; - grid-column-end: icon-end; + grid-column-end: handle-end; + + height: 1.4em; +} + +.contribution-tooltip .external-icon { + width: 18px; +} + +.contribution-tooltip .external-icon svg { + width: 18px; + height: 18px; + top: -0.1em; +} + +.contribution-tooltip .external-handle { + padding-right: 8px; } -.icons-tooltip .chronology-link { +.contribution-tooltip .chronology-link { grid-column-start: icon-start; - grid-column-end: domain-end; + grid-column-end: handle-end; padding-left: 5px; padding-right: 3px; font-size: 0.85em; } -.icons-tooltip .tooltip-divider { +.contribution-tooltip .tooltip-divider { grid-column-start: icon-start; - grid-column-end: domain-end; + grid-column-end: platform-end; border-top: 1px dotted var(--primary-color); margin-top: 3px; margin-bottom: 4px; } -.icons-tooltip .icon-platform { +.contribution-tooltip .external-platform { display: none; - grid-column-start: domain-start; - grid-column-end: domain-end; + grid-column-start: platform-start; + grid-column-end: platform-end; - --icon-platform-opacity: 0.8; + --external-platform-opacity: 0.8; padding-right: 4px; opacity: 0.8; } -.icons-tooltip.show-info .icon-platform { +.contribution-tooltip.show-info .external-platform { display: inline; - animation: icon-platform 0.2s forwards linear; + animation: external-platform 0.2s forwards linear; } -@keyframes icon-platform { +@keyframes external-platform { from { opacity: 0; } to { - opacity: var(--icon-platform-opacity); + opacity: var(--external-platform-opacity); } } -.icons-tooltip .icon:hover + .icon-platform { - --icon-platform-opacity: 1; +.contribution-tooltip .external-link:hover { + filter: brightness(1.4); + text-decoration: none; +} + +.contribution-tooltip .external-link:hover .external-handle { + text-decoration: underline; +} + +.contribution-tooltip .external-link:hover + .external-platform { + --external-platform-opacity: 1; text-decoration: underline; text-decoration-color: #ffffffaa; } @@ -1010,27 +1035,15 @@ li:not(:first-child:last-child) .tooltip, padding: 3px 4.5px; } -.icons { - font-style: normal; - white-space: nowrap; -} - -.icons a:hover { - filter: brightness(1.4); -} - -.icons a { - padding: 0 3px; -} - -.icon { +.external-icon { display: inline-block; + padding: 0 3px; width: 24px; height: 1em; position: relative; } -.icon > svg { +.external-icon svg { width: 24px; height: 24px; top: -0.25em; @@ -1038,23 +1051,6 @@ li:not(:first-child:last-child) .tooltip, fill: var(--primary-color); } -.icon.has-text { - display: block; - width: unset; - height: 1.4em; -} - -.icon.has-text > svg { - width: 18px; - height: 18px; - top: -0.1em; -} - -.icon.has-text > .icon-text { - margin-left: 24px; - padding-right: 8px; -} - .rerelease, .other-group-accent { opacity: 0.7; diff --git a/src/static/js/client.js b/src/static/js/client.js index 8299b63e0..38060999b 100644 --- a/src/static/js/client.js +++ b/src/static/js/client.js @@ -3465,7 +3465,7 @@ function getArtistExternalLinkTooltipPageReferences() { const info = artistExternalLinkTooltipInfo; info.tooltips = - Array.from(document.getElementsByClassName('icons-tooltip')); + Array.from(document.getElementsByClassName('contribution-tooltip')); info.tooltipRows = info.tooltips.map(tooltip => From d95599765d50007d7f4a82f8916a022b26bfefa1 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 10:47:09 -0300 Subject: [PATCH 19/29] test: update linkContribution (snapshot, unit) Sorry, this is just maintaining the test paths that exist and dropping the ones which don't apply anymore. --- .../snapshot/linkContribution.js.test.cjs | 179 +++++++----------- test/snapshot/linkContribution.js | 45 +---- .../content/dependencies/linkContribution.js | 41 ++-- 3 files changed, 102 insertions(+), 163 deletions(-) diff --git a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs index 92d697e72..c1631d2b3 100644 --- a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs +++ b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs @@ -5,159 +5,114 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > loads of links (inline) 1`] = ` -Lorem Ipsum Lover ( - - loremipsum.io - - - , - - loremipsum.io - - - , - - loremipsum.io - - - , - - loremipsum.io - - - ) -` - -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > loads of links (tooltip) 1`] = ` -Lorem Ipsum Lover +exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > loads of links 1`] = ` +Lorem Ipsum Lover loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other + loremipsum.io - Other + + Other ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no accents 1`] = ` -Clark Powell -Grounder & Scratch -Toby Fox +Clark Powell +Grounder & Scratch +Toby Fox ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no preventWrapping 1`] = ` -Clark Powell ( - - SoundCloud - - - ) -Grounder & Scratch (Snooping) -Toby Fox (Arrangement) ( - - Bandcamp - - - , - - toby.fox - - - ) +Clark Powell + + plazmataz + + SoundCloud +Grounder & Scratch (Snooping) +Toby Fox + + tobyfox + + Bandcamp + + + toby.fox + + Other (Arrangement) ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showContribution 1`] = ` -Clark Powell -Grounder & Scratch (Snooping) -Toby Fox (Arrangement) -` - -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showIcons (inline) 1`] = ` -Clark Powell ( - - SoundCloud - - - ) -Grounder & Scratch -Toby Fox ( - - Bandcamp - - - , - - toby.fox - - - ) +Clark Powell +Grounder & Scratch (Snooping) +Toby Fox (Arrangement) ` -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showIcons (tooltip) 1`] = ` -Clark Powell +exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showExternalLinks 1`] = ` +Clark Powell plazmataz - SoundCloud -Grounder & Scratch (Snooping) + + SoundCloud +Grounder & Scratch Toby Fox tobyfox - Bandcamp + + Bandcamp + toby.fox - Other (Arrangement) + + Other ` -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > showContribution & showIcons (inline) 1`] = ` -Clark Powell ( - - SoundCloud - - - ) -Grounder & Scratch (Snooping) -Toby Fox (Arrangement) ( - - Bandcamp - - - , - - toby.fox - - - ) -` - -exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > showContribution & showIcons (tooltip) 1`] = ` -Clark Powell +exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > showContribution & showExternalLinks 1`] = ` +Clark Powell plazmataz - SoundCloud -Grounder & Scratch (Snooping) + + SoundCloud +Grounder & Scratch (Snooping) Toby Fox tobyfox - Bandcamp + + Bandcamp + toby.fox - Other (Arrangement) + + Other (Arrangement) ` diff --git a/test/snapshot/linkContribution.js b/test/snapshot/linkContribution.js index 1043ddc67..5844b0b96 100644 --- a/test/snapshot/linkContribution.js +++ b/test/snapshot/linkContribution.js @@ -33,53 +33,22 @@ testContentFunctions(t, 'linkContribution (snapshot)', async (t, evaluate) => { slots, }); - quickSnapshot('showContribution & showIcons (inline)', { + quickSnapshot('showContribution & showExternalLinks', { showContribution: true, - showIcons: true, - iconMode: 'inline', - }); - - quickSnapshot('showContribution & showIcons (tooltip)', { - showContribution: true, - showIcons: true, - iconMode: 'tooltip', + showExternalLinks: true, }); quickSnapshot('only showContribution', { showContribution: true, }); - quickSnapshot('only showIcons (inline)', { - showIcons: true, - iconMode: 'inline', - }); - - quickSnapshot('only showIcons (tooltip)', { - showContribution: true, - showIcons: true, - iconMode: 'tooltip', + quickSnapshot('only showExternalLinks', { + showExternalLinks: true, }); quickSnapshot('no accents', {}); - evaluate.snapshot('loads of links (inline)', { - name: 'linkContribution', - args: [ - {artist: {name: 'Lorem Ipsum Lover', directory: 'lorem-ipsum-lover', urls: [ - 'https://loremipsum.io', - 'https://loremipsum.io/generator/', - 'https://loremipsum.io/#meaning', - 'https://loremipsum.io/#usage-and-examples', - 'https://loremipsum.io/#controversy', - 'https://loremipsum.io/#when-to-use-lorem-ipsum', - 'https://loremipsum.io/#lorem-ipsum-all-the-things', - 'https://loremipsum.io/#original-source', - ]}, annotation: null}, - ], - slots: {showIcons: true}, - }); - - evaluate.snapshot('loads of links (tooltip)', { + evaluate.snapshot('loads of links', { name: 'linkContribution', args: [ {artist: {name: 'Lorem Ipsum Lover', directory: 'lorem-ipsum-lover', urls: [ @@ -93,12 +62,12 @@ testContentFunctions(t, 'linkContribution (snapshot)', async (t, evaluate) => { 'https://loremipsum.io/#original-source', ]}, annotation: null}, ], - slots: {showIcons: true, iconMode: 'tooltip'}, + slots: {showExternalLinks: true}, }); quickSnapshot('no preventWrapping', { showContribution: true, - showIcons: true, + showExternalLinks: true, preventWrapping: false, }); }); diff --git a/test/unit/content/dependencies/linkContribution.js b/test/unit/content/dependencies/linkContribution.js index ab45b03af..e7a29310f 100644 --- a/test/unit/content/dependencies/linkContribution.js +++ b/test/unit/content/dependencies/linkContribution.js @@ -27,18 +27,20 @@ t.test('generateContributionLinks (unit)', async t => { await testContentFunctions(t, 'generateContributionLinks (unit 1)', async (t, evaluate) => { const slots = { showContribution: true, - showIcons: true, + showExternalLinks: true, }; await evaluate.load({ mock: evaluate.mock(mock => ({ linkArtist: { - relations: mock.function('linkArtist.relations', () => ({})) + relations: mock + .function('linkArtist.relations', () => ({})) .args([undefined, artist1]).next() .args([undefined, artist2]).next() .args([undefined, artist3]), - data: mock.function('linkArtist.data', () => ({})) + data: mock + .function('linkArtist.data', () => ({})) .args([artist1]).next() .args([artist2]).next() .args([artist3]), @@ -49,13 +51,18 @@ t.test('generateContributionLinks (unit)', async t => { .repeat(3), }, - linkExternalAsIcon: { - data: mock.function('linkExternalAsIcon.data', () => ({})) + generateExternalIcon: { + data: mock + .function('generateExternalIcon.data', () => ({})) .args([artist1.urls[0]]).next() .args([artist3.urls[0]]).next() .args([artist3.urls[1]]), - generate: mock.function('linkExternalAsIcon.generate', () => 'icon') + generate: mock + .function('generateExternalIcon.generate', () => ({ + toString: () => 'icon', + setSlot: () => {}, + })) .repeat(3), } })), @@ -75,23 +82,26 @@ t.test('generateContributionLinks (unit)', async t => { await testContentFunctions(t, 'generateContributionLinks (unit 2)', async (t, evaluate) => { const slots = { showContribution: false, - showIcons: false, + showExternalLinks: false, }; await evaluate.load({ mock: evaluate.mock(mock => ({ linkArtist: { - relations: mock.function('linkArtist.relations', () => ({})) + relations: mock + .function('linkArtist.relations', () => ({})) .args([undefined, artist1]).next() .args([undefined, artist2]).next() .args([undefined, artist3]), - data: mock.function('linkArtist.data', () => ({})) + data: mock + .function('linkArtist.data', () => ({})) .args([artist1]).next() .args([artist2]).next() .args([artist3]), - generate: mock.function(() => 'artist link') + generate: mock + .function(() => 'artist link') .repeat(3), }, @@ -99,11 +109,16 @@ t.test('generateContributionLinks (unit)', async t => { // tree is the same since whether or not the external icon links are // shown is dependent on a slot, which is undefined and arbitrary at // relations/data time (it might change on a whim at generate time). - linkExternalAsIcon: { - data: mock.function('linkExternalAsIcon.data', () => ({})) + generateExternalIcon: { + data: mock + .function('generateExternalIcon.data', () => ({})) .repeat(3), - generate: mock.function('linkExternalAsIcon.generate', () => 'icon') + generate: mock + .function('generateExternalIcon.generate', () => ({ + toString: () => 'icon', + setSlot: () => {}, + })) .repeat(3), }, })), From e31330aa2c077e4cae0e020ad139043ffc734491 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 10:57:51 -0300 Subject: [PATCH 20/29] css: external links subgrid --- src/static/css/site.css | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/static/css/site.css b/src/static/css/site.css index d226b2815..da2b93744 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -942,18 +942,21 @@ li:not(:first-child:last-child) .tooltip, display: grid; grid-template-columns: - [icon-start] auto [icon-end handle-start] auto [handle-end platform-start] auto [platform-end]; + [icon-start] 26px [icon-end handle-start] auto [handle-end platform-start] auto [platform-end]; } .contribution-tooltip .external-link { + display: grid; grid-column-start: icon-start; grid-column-end: handle-end; + grid-template-columns: subgrid; height: 1.4em; } .contribution-tooltip .external-icon { - width: 18px; + grid-column-start: icon-start; + grid-column-end: icon-end; } .contribution-tooltip .external-icon svg { @@ -963,6 +966,9 @@ li:not(:first-child:last-child) .tooltip, } .contribution-tooltip .external-handle { + grid-column-start: handle-start; + grid-column-end: handle-end; + padding-right: 8px; } From 6f96994c1a0a6468322a8351d436510d7970509a Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 11:14:49 -0300 Subject: [PATCH 21/29] content, css: chronology links subgrid --- ...ateContributionTooltipChronologySection.js | 46 +++++++++++++------ src/static/css/site.css | 26 +++++++++-- src/strings-default.yaml | 7 +-- 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js index 49e5d7836..09f409f51 100644 --- a/src/content/dependencies/generateContributionTooltipChronologySection.js +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -28,25 +28,41 @@ export default { : null), }), - generate: (relations, {html, language}) => - language.encapsulate('misc.artistLink.chronology', capsule => - html.tags([ - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, + data: (query, _contribution) => ({ + previousName: + (query.previous + ? query.previous.thing.name + : null), - language.$(capsule, 'previous', { - [language.onlyIfOptions]: ['thing'], + nextName: + (query.next + ? query.next.thing.name + : null), + }), - thing: relations.previousLink, - })), + generate: (data, relations, {html, language}) => + language.encapsulate('misc.artistLink', capsule => + html.tags([ + relations.previousLink?.slots({ + attributes: {class: 'chronology-link'}, + content: [ + html.tag('span', {class: 'chronology-symbol'}, + language.$(capsule, 'previousSymbol')), - html.tag('span', {class: 'chronology-link'}, - {[html.onlyIfContent]: true}, + html.tag('span', {class: 'chronology-text'}, + language.sanitize(data.previousName)), + ], + }), - language.$(capsule, 'next', { - [language.onlyIfOptions]: ['thing'], + relations.nextLink?.slots({ + attributes: {class: 'chronology-link'}, + content: [ + html.tag('span', {class: 'chronology-symbol'}, + language.$(capsule, 'nextSymbol')), - thing: relations.nextLink, - })), + html.tag('span', {class: 'chronology-text'}, + language.sanitize(data.nextName)), + ], + }), ])), }; diff --git a/src/static/css/site.css b/src/static/css/site.css index da2b93744..35364fdf8 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -973,10 +973,26 @@ li:not(:first-child:last-child) .tooltip, } .contribution-tooltip .chronology-link { + display: grid; grid-column-start: icon-start; grid-column-end: handle-end; - padding-left: 5px; - padding-right: 3px; + grid-template-columns: subgrid; + + height: 1.2em; +} + +.contribution-tooltip .chronology-symbol { + grid-column-start: icon-start; + grid-column-end: icon-end; + + text-align: center; +} + +.contribution-tooltip .chronology-text { + grid-column-start: handle-start; + grid-column-end: handle-end; + + padding-right: 6px; font-size: 0.85em; } @@ -1015,12 +1031,14 @@ li:not(:first-child:last-child) .tooltip, } } -.contribution-tooltip .external-link:hover { +.contribution-tooltip .external-link:hover, +.contribution-tooltip .chronology-link:hover { filter: brightness(1.4); text-decoration: none; } -.contribution-tooltip .external-link:hover .external-handle { +.contribution-tooltip .external-link:hover .external-handle, +.contribution-tooltip .chronology-link:hover .chronology-text { text-decoration: underline; } diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 29aba5535..8bbfb1af3 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -480,9 +480,10 @@ misc: # isn't a specially detected web platform. noExternalLinkPlatformName: "Other" - chronology: - previous: "← {THING}" - next: "→ {THING}" + # Displayed in an artist's toolitp, as the symbol + # for chronology links. + previousSymbol: "←" + nextSymbol: "→" # chronology: # From f9ff3fb1eaf463b17ae8ccacb053249c75b76cc2 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 11:27:29 -0300 Subject: [PATCH 22/29] content, css: chronology link info --- ...ateContributionTooltipChronologySection.js | 92 ++++++++++++++----- src/static/css/site.css | 44 ++++----- src/strings-default.yaml | 23 ++++- 3 files changed, 110 insertions(+), 49 deletions(-) diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js index 09f409f51..5803b406b 100644 --- a/src/content/dependencies/generateContributionTooltipChronologySection.js +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -40,29 +40,75 @@ export default { : null), }), - generate: (data, relations, {html, language}) => - language.encapsulate('misc.artistLink', capsule => + slots: { + kind: { + validate: v => + v.is( + 'album', + 'coverArt', + 'flash', + 'track', + 'trackArt'), + }, + }, + + generate: (data, relations, slots, {html, language}) => + language.encapsulate('misc.artistLink.chronology', capsule => html.tags([ - relations.previousLink?.slots({ - attributes: {class: 'chronology-link'}, - content: [ - html.tag('span', {class: 'chronology-symbol'}, - language.$(capsule, 'previousSymbol')), - - html.tag('span', {class: 'chronology-text'}, - language.sanitize(data.previousName)), - ], - }), - - relations.nextLink?.slots({ - attributes: {class: 'chronology-link'}, - content: [ - html.tag('span', {class: 'chronology-symbol'}, - language.$(capsule, 'nextSymbol')), - - html.tag('span', {class: 'chronology-text'}, - language.sanitize(data.nextName)), - ], - }), + html.tags([ + relations.previousLink?.slots({ + attributes: {class: 'chronology-link'}, + content: [ + html.tag('span', {class: 'chronology-symbol'}, + language.$(capsule, 'previous.symbol')), + + html.tag('span', {class: 'chronology-text'}, + language.sanitize(data.previousName)), + ], + }), + + html.tag('span', {class: 'chronology-info'}, + {[html.onlyIfSiblings]: true}, + + language.encapsulate(capsule, 'previous.info', workingCapsule => { + const workingOptions = {}; + + if (slots.kind) { + workingCapsule += '.withKind'; + workingOptions.kind = + language.$(capsule, 'kind', slots.kind); + } + + return language.$(workingCapsule, workingOptions); + })), + ]), + + html.tags([ + relations.nextLink?.slots({ + attributes: {class: 'chronology-link'}, + content: [ + html.tag('span', {class: 'chronology-symbol'}, + language.$(capsule, 'next.symbol')), + + html.tag('span', {class: 'chronology-text'}, + language.sanitize(data.nextName)), + ], + }), + + html.tag('span', {class: 'chronology-info'}, + {[html.onlyIfSiblings]: true}, + + language.encapsulate(capsule, 'next.info', workingCapsule => { + const workingOptions = {}; + + if (slots.kind) { + workingCapsule += '.withKind'; + workingOptions.kind = + language.$(capsule, 'kind', slots.kind); + } + + return language.$(workingCapsule, workingOptions); + })) + ]), ])), }; diff --git a/src/static/css/site.css b/src/static/css/site.css index 35364fdf8..ddd285b93 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -954,24 +954,6 @@ li:not(:first-child:last-child) .tooltip, height: 1.4em; } -.contribution-tooltip .external-icon { - grid-column-start: icon-start; - grid-column-end: icon-end; -} - -.contribution-tooltip .external-icon svg { - width: 18px; - height: 18px; - top: -0.1em; -} - -.contribution-tooltip .external-handle { - grid-column-start: handle-start; - grid-column-end: handle-end; - - padding-right: 8px; -} - .contribution-tooltip .chronology-link { display: grid; grid-column-start: icon-start; @@ -981,18 +963,33 @@ li:not(:first-child:last-child) .tooltip, height: 1.2em; } +.contribution-tooltip .external-icon, .contribution-tooltip .chronology-symbol { grid-column-start: icon-start; grid-column-end: icon-end; +} + +.contribution-tooltip .external-icon svg { + width: 18px; + height: 18px; + top: -0.1em; +} +.contribution-tooltip .chronology-symbol { text-align: center; } +.contribution-tooltip .external-handle, .contribution-tooltip .chronology-text { grid-column-start: handle-start; grid-column-end: handle-end; - padding-right: 6px; + padding-right: 8px; +} + + +.contribution-tooltip .chronology-text, +.contribution-tooltip .chronology-info { font-size: 0.85em; } @@ -1005,7 +1002,8 @@ li:not(:first-child:last-child) .tooltip, margin-bottom: 4px; } -.contribution-tooltip .external-platform { +.contribution-tooltip .external-platform, +.contribution-tooltip .chronology-info { display: none; grid-column-start: platform-start; @@ -1016,7 +1014,8 @@ li:not(:first-child:last-child) .tooltip, opacity: 0.8; } -.contribution-tooltip.show-info .external-platform { +.contribution-tooltip.show-info .external-platform, +.contribution-tooltip.show-info .chronology-info { display: inline; animation: external-platform 0.2s forwards linear; } @@ -1042,7 +1041,8 @@ li:not(:first-child:last-child) .tooltip, text-decoration: underline; } -.contribution-tooltip .external-link:hover + .external-platform { +.contribution-tooltip .external-link:hover + .external-platform, +.contribution-tooltip .chronology-link:hover + .chronology-info { --external-platform-opacity: 1; text-decoration: underline; text-decoration-color: #ffffffaa; diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 8bbfb1af3..63d981dfd 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -480,10 +480,25 @@ misc: # isn't a specially detected web platform. noExternalLinkPlatformName: "Other" - # Displayed in an artist's toolitp, as the symbol - # for chronology links. - previousSymbol: "←" - nextSymbol: "→" + chronology: + previous: + symbol: "←" + info: + _: "Previous by this artist" + withKind: "Previous {KIND} by this artist" + + next: + symbol: "→" + info: + _: "Next by this artist" + withKind: "Next {KIND} by this artist" + + kind: + album: "album" + coverArt: "cover art" + flash: "flash" + track: "track" + trackArt: "track art" # chronology: # From 4f248d1017673eb45438f3f3933b79960ab85799 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 11:36:28 -0300 Subject: [PATCH 23/29] content: chronology labels in release info lines --- .../dependencies/generateAlbumReleaseInfo.js | 24 ++++++++++++------- .../generateContributionTooltip.js | 6 ++++- ...ateContributionTooltipChronologySection.js | 4 +++- .../generateReleaseInfoContributionsLine.js | 6 +++-- .../dependencies/generateTrackReleaseInfo.js | 12 ++++++---- src/content/dependencies/linkContribution.js | 2 ++ src/strings-default.yaml | 2 ++ 7 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/content/dependencies/generateAlbumReleaseInfo.js b/src/content/dependencies/generateAlbumReleaseInfo.js index e4c184c01..28227f450 100644 --- a/src/content/dependencies/generateAlbumReleaseInfo.js +++ b/src/content/dependencies/generateAlbumReleaseInfo.js @@ -67,17 +67,25 @@ export default { {[html.joinChildren]: html.tag('br')}, [ - relations.artistContributionsLine - .slots({stringKey: capsule + '.by'}), + relations.artistContributionsLine.slots({ + stringKey: capsule + '.by', + chronologyKind: 'album', + }), - relations.coverArtistContributionsLine - .slots({stringKey: capsule + '.coverArtBy'}), + relations.coverArtistContributionsLine.slots({ + stringKey: capsule + '.coverArtBy', + chronologyKind: 'coverArt', + }), - relations.wallpaperArtistContributionsLine - .slots({stringKey: capsule + '.wallpaperArtBy'}), + relations.wallpaperArtistContributionsLine.slots({ + stringKey: capsule + '.wallpaperArtBy', + chronologyKind: 'wallpaperArt', + }), - relations.bannerArtistContributionsLine - .slots({stringKey: capsule + '.bannerArtBy'}), + relations.bannerArtistContributionsLine.slots({ + stringKey: capsule + '.bannerArtBy', + chronologyKind: 'bannerArt', + }), language.$(capsule, 'released', { [language.onlyIfOptions]: ['date'], diff --git a/src/content/dependencies/generateContributionTooltip.js b/src/content/dependencies/generateContributionTooltip.js index 5df881217..3a31014d7 100644 --- a/src/content/dependencies/generateContributionTooltip.js +++ b/src/content/dependencies/generateContributionTooltip.js @@ -21,6 +21,8 @@ export default { slots: { showExternalLinks: {type: 'boolean'}, showChronology: {type: 'boolean'}, + + chronologyKind: {type: 'string'}, }, generate: (relations, slots, {html}) => @@ -38,7 +40,9 @@ export default { relations.externalLinkSection, slots.showChronology && - relations.chronologySection, + relations.chronologySection.slots({ + kind: slots.chronologyKind, + }), ], }), }; diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js index 5803b406b..ef5c75b45 100644 --- a/src/content/dependencies/generateContributionTooltipChronologySection.js +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -45,10 +45,12 @@ export default { validate: v => v.is( 'album', + 'bannerArt', 'coverArt', 'flash', 'track', - 'trackArt'), + 'trackArt', + 'wallpaperArt'), }, }, diff --git a/src/content/dependencies/generateReleaseInfoContributionsLine.js b/src/content/dependencies/generateReleaseInfoContributionsLine.js index ed60886b3..3e96ed448 100644 --- a/src/content/dependencies/generateReleaseInfoContributionsLine.js +++ b/src/content/dependencies/generateReleaseInfoContributionsLine.js @@ -17,11 +17,12 @@ export default { }, slots: { - stringKey: {type: 'string'}, - showContribution: {type: 'boolean', default: true}, showExternalLinks: {type: 'boolean', default: true}, showChronology: {type: 'boolean', default: true}, + + stringKey: {type: 'string'}, + chronologyKind: {type: 'string'}, }, generate(relations, slots, {html, language}) { @@ -37,6 +38,7 @@ export default { showContribution: slots.showContribution, showExternalLinks: slots.showExternalLinks, showChronology: slots.showChronology, + chronologyKind: slots.chronologyKind, }))), }); }, diff --git a/src/content/dependencies/generateTrackReleaseInfo.js b/src/content/dependencies/generateTrackReleaseInfo.js index e234dd5d6..8a0810466 100644 --- a/src/content/dependencies/generateTrackReleaseInfo.js +++ b/src/content/dependencies/generateTrackReleaseInfo.js @@ -54,11 +54,15 @@ export default { {[html.joinChildren]: html.tag('br')}, [ - relations.artistContributionLinks - .slots({stringKey: capsule + '.by'}), + relations.artistContributionLinks.slots({ + stringKey: capsule + '.by', + chronologyKind: 'track', + }), - relations.coverArtistContributionsLine - ?.slots({stringKey: capsule + '.coverArtBy'}), + relations.coverArtistContributionsLine?.slots({ + stringKey: capsule + '.coverArtBy', + chronologyKind: 'trackArt', + }), language.$(capsule, 'released', { [language.onlyIfOptions]: ['date'], diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index 67a50e5b9..c43f31ca5 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -29,6 +29,7 @@ export default { showChronology: {type: 'boolean', default: false}, preventWrapping: {type: 'boolean', default: true}, + chronologyKind: {type: 'string'}, }, generate: (data, relations, slots, {html, language}) => @@ -54,6 +55,7 @@ export default { relations.tooltip.slots({ showExternalLinks: slots.showExternalLinks, showChronology: slots.showChronology, + chronologyKind: slots.chronologyKind, }), }); diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 63d981dfd..0958c06c3 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -495,10 +495,12 @@ misc: kind: album: "album" + bannerArt: "banner art" coverArt: "cover art" flash: "flash" track: "track" trackArt: "track art" + wallpaperArt: "wallpaper art" # chronology: # From 0495e2111c9cd2a5b76e6b2d27d0924f85351205 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 18:05:32 -0300 Subject: [PATCH 24/29] content: chronology labels in contribution lists --- src/content/dependencies/generateContributionList.js | 7 ++++++- .../generateContributionTooltipChronologySection.js | 1 + src/content/dependencies/generateFlashInfoPage.js | 4 +++- src/content/dependencies/generateTrackInfoPage.js | 4 +++- src/strings-default.yaml | 1 + 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/content/dependencies/generateContributionList.js b/src/content/dependencies/generateContributionList.js index f22740b02..8e8c5020d 100644 --- a/src/content/dependencies/generateContributionList.js +++ b/src/content/dependencies/generateContributionList.js @@ -8,7 +8,11 @@ export default { .map(contrib => relation('linkContribution', contrib)), }), - generate: (relations, {html}) => + slots: { + chronologyKind: {type: 'string'}, + }, + + generate: (relations, slots, {html}) => html.tag('ul', {[html.onlyIfContent]: true}, @@ -20,5 +24,6 @@ export default { showContribution: true, showChronology: true, preventWrapping: false, + chronologyKind: slots.chronologyKind, })))), }; diff --git a/src/content/dependencies/generateContributionTooltipChronologySection.js b/src/content/dependencies/generateContributionTooltipChronologySection.js index ef5c75b45..78c9051c3 100644 --- a/src/content/dependencies/generateContributionTooltipChronologySection.js +++ b/src/content/dependencies/generateContributionTooltipChronologySection.js @@ -50,6 +50,7 @@ export default { 'flash', 'track', 'trackArt', + 'trackContribution', 'wallpaperArt'), }, }, diff --git a/src/content/dependencies/generateFlashInfoPage.js b/src/content/dependencies/generateFlashInfoPage.js index 96337d831..d06f0c01d 100644 --- a/src/content/dependencies/generateFlashInfoPage.js +++ b/src/content/dependencies/generateFlashInfoPage.js @@ -147,7 +147,9 @@ export default { title: language.$('releaseInfo.contributors'), }), - relations.contributorContributionList, + relations.contributorContributionList.slots({ + chronologyKind: 'flash', + }), ]), relations.artistCommentarySection, diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 7337b987d..09cf55f65 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -217,7 +217,9 @@ export default { title: language.$('releaseInfo.contributors'), }), - relations.contributorContributionList, + relations.contributorContributionList.slots({ + chronologyKind: 'trackContribution', + }), ]), html.tags([ diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 0958c06c3..26107c0b1 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -500,6 +500,7 @@ misc: flash: "flash" track: "track" trackArt: "track art" + trackContribution: "track contribution" wallpaperArt: "wallpaper art" # chronology: From 7020b8be2dfed1d23a192ecefeb7c78f71296615 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 13:24:58 -0300 Subject: [PATCH 25/29] css: limit handle/thing tooltip width, overflow ellipsis --- src/static/css/site.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/static/css/site.css b/src/static/css/site.css index ddd285b93..374d25461 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -984,9 +984,21 @@ li:not(:first-child:last-child) .tooltip, grid-column-start: handle-start; grid-column-end: handle-end; + width: max-content; + max-width: 200px; + + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.contribution-tooltip .external-handle { padding-right: 8px; } +.contribution-tooltip .chronology-text { + padding-right: 6px; +} .contribution-tooltip .chronology-text, .contribution-tooltip .chronology-info { From a4626eeacc06b7c7d72a54be78971983de2250ec Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 18:08:02 -0300 Subject: [PATCH 26/29] css: fix surprise wrapping in contribution list tooltips --- src/static/css/site.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/static/css/site.css b/src/static/css/site.css index 374d25461..b4c997881 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -1022,8 +1022,10 @@ li:not(:first-child:last-child) .tooltip, grid-column-end: platform-end; --external-platform-opacity: 0.8; - padding-right: 4px; opacity: 0.8; + padding-right: 4px; + + white-space: nowrap; } .contribution-tooltip.show-info .external-platform, From a337c55a2bbdb7b4eeb43ab346100bb3f3464d46 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 14:28:15 -0300 Subject: [PATCH 27/29] content: remove old chronology links --- .../generateAlbumChronologyLinks.js | 53 ------ .../dependencies/generateAlbumInfoPage.js | 7 - .../dependencies/generateChronologyLinks.js | 112 ----------- .../generateChronologyLinksScopeSwitcher.js | 68 ------- .../generateTrackChronologyLinks.js | 177 ------------------ .../dependencies/generateTrackInfoPage.js | 7 - src/content/util/getChronologyRelations.js | 57 ------ src/static/css/site.css | 18 -- src/static/js/client.js | 108 ----------- 9 files changed, 607 deletions(-) delete mode 100644 src/content/dependencies/generateAlbumChronologyLinks.js delete mode 100644 src/content/dependencies/generateChronologyLinks.js delete mode 100644 src/content/dependencies/generateChronologyLinksScopeSwitcher.js delete mode 100644 src/content/dependencies/generateTrackChronologyLinks.js delete mode 100644 src/content/util/getChronologyRelations.js diff --git a/src/content/dependencies/generateAlbumChronologyLinks.js b/src/content/dependencies/generateAlbumChronologyLinks.js deleted file mode 100644 index 3dd7a18e1..000000000 --- a/src/content/dependencies/generateAlbumChronologyLinks.js +++ /dev/null @@ -1,53 +0,0 @@ -import {sortAlbumsTracksChronologically} from '#sort'; - -import getChronologyRelations from '../util/getChronologyRelations.js'; - -export default { - contentDependencies: [ - 'generateChronologyLinks', - 'linkAlbum', - 'linkArtist', - 'linkTrack', - ], - - relations: (relation, album) => ({ - chronologyLinks: - relation('generateChronologyLinks'), - - coverArtistChronologyContributions: - getChronologyRelations(album, { - contributions: album.coverArtistContribs ?? [], - - linkArtist: artist => relation('linkArtist', artist), - - linkThing: trackOrAlbum => - (trackOrAlbum.album - ? relation('linkTrack', trackOrAlbum) - : relation('linkAlbum', trackOrAlbum)), - - getThings(artist) { - const getDate = thing => thing.coverArtDate ?? thing.date; - - const things = - ([ - artist.albumCoverArtistContributions, - artist.trackCoverArtistContributions, - ]).flat() - .map(({thing}) => thing) - .filter(getDate); - - return sortAlbumsTracksChronologically(things, {getDate}); - }, - }), - }), - - generate: (relations) => - relations.chronologyLinks.slots({ - chronologyInfoSets: [ - { - headingString: 'misc.chronology.heading.coverArt', - contributions: relations.coverArtistChronologyContributions, - }, - ], - }), -} diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js index 3af312bb4..1bffe2d03 100644 --- a/src/content/dependencies/generateAlbumInfoPage.js +++ b/src/content/dependencies/generateAlbumInfoPage.js @@ -10,7 +10,6 @@ export default { 'generateAlbumSocialEmbed', 'generateAlbumStyleRules', 'generateAlbumTrackList', - 'generateAlbumChronologyLinks', 'generateCommentarySection', 'generateContentHeading', 'generatePageLayout', @@ -33,9 +32,6 @@ export default { albumNavAccent: relation('generateAlbumNavAccent', album, null), - chronologyLinks: - relation('generateAlbumChronologyLinks', album), - secondaryNav: relation('generateAlbumSecondaryNav', album), @@ -196,9 +192,6 @@ export default { }, ], - navContent: - relations.chronologyLinks, - banner: relations.banner ?? null, bannerPosition: 'top', diff --git a/src/content/dependencies/generateChronologyLinks.js b/src/content/dependencies/generateChronologyLinks.js deleted file mode 100644 index 7f24ded78..000000000 --- a/src/content/dependencies/generateChronologyLinks.js +++ /dev/null @@ -1,112 +0,0 @@ -import {accumulateSum, empty} from '#sugar'; - -export default { - extraDependencies: ['html', 'language'], - - slots: { - allowCollapsing: { - type: 'boolean', - default: true, - }, - - showOnly: { - type: 'boolean', - default: false, - }, - - chronologyInfoSets: { - validate: v => - v.strictArrayOf( - v.validateProperties({ - headingString: v.isString, - contributions: v.strictArrayOf(v.validateProperties({ - index: v.isCountingNumber, - only: v.isBoolean, - artistDirectory: v.isDirectory, - artistLink: v.isHTML, - previousLink: v.isHTML, - nextLink: v.isHTML, - })), - })), - } - }, - - generate(slots, {html, language}) { - if (empty(slots.chronologyInfoSets)) { - return html.blank(); - } - - let infoSets = slots.chronologyInfoSets; - - if (!slots.showOnly) { - infoSets = infoSets - .map(({contributions, ...entry}) => ({ - ...entry, - contributions: - contributions - .filter(({only}) => !only), - })) - .filter(({contributions}) => !empty(contributions)); - } - - const totalContributionCount = - accumulateSum( - infoSets, - ({contributions}) => contributions.length); - - if (totalContributionCount === 0) { - return html.blank(); - } - - if (slots.allowCollapsing && totalContributionCount > 8) { - return html.tag('div', {class: 'chronology'}, - language.$('misc.chronology.seeArtistPages')); - } - - return html.tags( - infoSets.map(({ - headingString, - contributions, - }) => - contributions.map(({ - index, - artistLink, - previousLink, - nextLink, - only, - }) => { - const heading = - html.tag('span', {class: 'heading'}, - language.$(headingString, { - index: - (only - ? language.formatString('misc.chronology.heading.onlyIndex') - : language.formatIndex(index)), - - artist: artistLink, - })); - - const navigation = - !only && - html.tag('span', {class: 'buttons'}, - language.formatUnitList([ - previousLink?.slots({ - tooltipStyle: 'browser', - color: false, - content: language.$('misc.nav.previous'), - }), - - nextLink?.slots({ - tooltipStyle: 'browser', - color: false, - content: language.$('misc.nav.next'), - }), - ].filter(Boolean))); - - return html.tag('div', {class: 'chronology'}, - (navigation - ? language.$('misc.chronology.withNavigation', {heading, navigation}) - : heading)); - }))); - }, -}; diff --git a/src/content/dependencies/generateChronologyLinksScopeSwitcher.js b/src/content/dependencies/generateChronologyLinksScopeSwitcher.js deleted file mode 100644 index 4a1b67a74..000000000 --- a/src/content/dependencies/generateChronologyLinksScopeSwitcher.js +++ /dev/null @@ -1,68 +0,0 @@ -import {stitchArrays} from '#sugar'; - -export default { - extraDependencies: ['html', 'language'], - - slots: { - scopes: { - validate: v => v.strictArrayOf(v.isStringNonEmpty), - }, - - contents: { - validate: v => v.strictArrayOf(v.isHTML), - }, - - open: { - type: 'boolean', - default: true, - }, - }, - - generate(slots, {html, language}) { - // TODO: Manual [html.onlyIfContent]-alike here is a bit unfortunate. - // We can't use a normal [html.onlyIfContent] because the summary counts - // as content - we'd need to encode that we want to exclude it from the - // content check (for the
element), somehow. - if (slots.contents.every(content => html.isBlank(content))) { - return html.blank(); - } - - const summary = - html.tag('summary', - {class: 'underline-white'}, - - html.tag('span', - language.encapsulate('trackPage.nav.chronology.scope', capsule => - language.$(capsule, 'title', { - scope: - slots.scopes.map((scope, index) => - html.tag('a', {class: 'switcher-link'}, - {href: '#'}, - - (index === 0 - ? {style: 'display: inline'} - : {style: 'display: none'}), - - language.$(capsule, scope))), - })))); - - const scopeContents = - stitchArrays({ - scope: slots.scopes, - content: slots.contents, - }).map(({scope, content}, index) => - html.tag('div', {class: 'scope-' + scope}, - (index === 0 - ? {style: 'display: block'} - : {style: 'display: none'}), - - content)); - - return ( - html.tag('details', {class: 'scoped-chronology-switcher'}, - slots.open && - {open: true}, - - [summary, scopeContents])); - }, -}; diff --git a/src/content/dependencies/generateTrackChronologyLinks.js b/src/content/dependencies/generateTrackChronologyLinks.js deleted file mode 100644 index f9ad62999..000000000 --- a/src/content/dependencies/generateTrackChronologyLinks.js +++ /dev/null @@ -1,177 +0,0 @@ -import {sortAlbumsTracksChronologically} from '#sort'; -import {accumulateSum, stitchArrays} from '#sugar'; - -import getChronologyRelations from '../util/getChronologyRelations.js'; - -export default { - contentDependencies: [ - 'generateChronologyLinks', - 'generateChronologyLinksScopeSwitcher', - 'linkAlbum', - 'linkArtist', - 'linkTrack', - ], - - relations(relation, track) { - function getScopedRelations(album) { - const albumFilter = - (album - ? track => track.album === album - : () => true); - - return { - chronologyLinks: - relation('generateChronologyLinks'), - - artistChronologyContributions: - getChronologyRelations(track, { - contributions: [ - ...track.artistContribs ?? [], - ...track.contributorContribs ?? [], - ], - - linkArtist: artist => relation('linkArtist', artist), - linkThing: track => relation('linkTrack', track), - - getThings(artist) { - const getDate = thing => thing.date; - - const things = - ([ - artist.trackArtistContributions, - artist.trackContributorContributions, - ]).flat() - .map(({thing}) => thing) - .filter(getDate) - .filter(albumFilter); - - return sortAlbumsTracksChronologically(things, {getDate}); - }, - }), - - coverArtistChronologyContributions: - getChronologyRelations(track, { - contributions: track.coverArtistContribs ?? [], - - linkArtist: artist => relation('linkArtist', artist), - - linkThing: trackOrAlbum => - (trackOrAlbum.album - ? relation('linkTrack', trackOrAlbum) - : relation('linkAlbum', trackOrAlbum)), - - getThings(artist) { - const getDate = thing => thing.coverArtDate ?? thing.date; - - // Album artwork isn't part of cover artist chronology scoped to - // even the same album - we use this list to show "nth track art". - const applicableContributions = - (album - ? artist.trackCoverArtistContributions - : ([ - artist.albumCoverArtistContributions, - artist.trackCoverArtistContributions, - ]).flat()); - - const things = - applicableContributions - .map(({thing}) => thing) - .filter(getDate) - .filter(albumFilter); - - return sortAlbumsTracksChronologically(things, {getDate}); - }, - }), - }; - } - - const relations = {}; - - relations.scopeSwitcher = - relation('generateChronologyLinksScopeSwitcher'); - - relations.wiki = - getScopedRelations(null); - - relations.album = - getScopedRelations(track.album); - - for (const setKey of [ - 'artistChronologyContributions', - 'coverArtistChronologyContributions', - ]) { - const wikiSet = relations.wiki[setKey]; - const albumSet = relations.album[setKey]; - - const wikiArtistDirectories = - wikiSet - .map(({artistDirectory}) => artistDirectory); - - albumSet.sort((a, b) => - (a.only === b.only && a.index === b.index - ? (wikiArtistDirectories.indexOf(a.artistDirectory) - - wikiArtistDirectories.indexOf(b.artistDirectory)) - : 0)); - } - - return relations; - }, - - generate(relations) { - function slotScopedRelations({content, artworkHeadingString}) { - return content.chronologyLinks.slots({ - showOnly: true, - allowCollapsing: false, - - chronologyInfoSets: [ - { - headingString: 'misc.chronology.heading.track', - contributions: content.artistChronologyContributions, - }, - { - headingString: `misc.chronology.heading.${artworkHeadingString}`, - contributions: content.coverArtistChronologyContributions, - }, - ], - }); - } - - const scopes = [ - 'wiki', - 'album', - ]; - - const contents = [ - relations.wiki, - relations.album, - ]; - - const artworkHeadingStrings = [ - 'coverArt', - 'trackArt', - ]; - - const totalContributionCount = - Math.max(... - contents.map(content => - accumulateSum([ - content.artistChronologyContributions, - content.coverArtistChronologyContributions, - ], contributions => contributions.length))); - - relations.scopeSwitcher.setSlots({ - scopes, - - open: - totalContributionCount <= 5, - - contents: - stitchArrays({ - content: contents, - artworkHeadingString: artworkHeadingStrings, - }).map(slotScopedRelations), - }); - - return relations.scopeSwitcher; - }, -}; diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 09cf55f65..64ed0cb45 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -10,7 +10,6 @@ export default { 'generateContributionList', 'generatePageLayout', 'generateTrackAdditionalNamesBox', - 'generateTrackChronologyLinks', 'generateTrackCoverArtwork', 'generateTrackInfoPageFeaturedByFlashesList', 'generateTrackInfoPageOtherReleasesList', @@ -49,9 +48,6 @@ export default { albumNavAccent: relation('generateAlbumNavAccent', track.album, track), - chronologyLinks: - relation('generateTrackChronologyLinks', track), - secondaryNav: relation('generateAlbumSecondaryNav', track.album), @@ -397,9 +393,6 @@ export default { showExtraLinks: false, }), - navContent: - relations.chronologyLinks, - secondaryNav: relations.secondaryNav .slot('mode', 'track'), diff --git a/src/content/util/getChronologyRelations.js b/src/content/util/getChronologyRelations.js deleted file mode 100644 index c601a990f..000000000 --- a/src/content/util/getChronologyRelations.js +++ /dev/null @@ -1,57 +0,0 @@ -export default function getChronologyRelations(thing, { - contributions, - linkArtist, - linkThing, - getThings, -}) { - // One call to getChronologyRelations is considered "lumping" together all - // contributions as carrying equivalent meaning (for example, "artist" - // contributions and "contributor" contributions are bunched together in - // one call to getChronologyRelations, while "cover artist" contributions - // are a separate call). getChronologyRelations prevents duplicates that - // carry the same meaning by only using the first instance of each artist - // in the contributions array passed to it. It's expected that the string - // identifying which kind of contribution ("track" or "cover art") is - // shared and applied to all contributions, as providing them together - // in one call to getChronologyRelations implies they carry the same - // meaning. - - const artistsSoFar = new Set(); - - contributions = contributions.filter(({artist}) => { - if (artistsSoFar.has(artist)) { - return false; - } else { - artistsSoFar.add(artist); - return true; - } - }); - - return contributions.map(({artist}) => { - const things = Array.from(new Set(getThings(artist))); - - // Don't show a line if this contribution isn't part of the artist's - // chronology at all (usually because this thing isn't dated). - const index = things.indexOf(thing); - if (index === -1) { - return; - } - - const previous = things[index - 1]; - const next = things[index + 1]; - - return { - index: index + 1, - artistDirectory: artist.directory, - only: !(previous || next), - - artistLink: linkArtist(artist), - previousLink: previous ? linkThing(previous) : null, - nextLink: next ? linkThing(next) : null, - }; - }).filter(Boolean) - .sort((a, b) => - (a.only === b.only ? b.index - a.index - : a.only ? +1 - : -1)) -} diff --git a/src/static/css/site.css b/src/static/css/site.css index b4c997881..80801c852 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -817,24 +817,6 @@ a:not([href]):hover { content: "\0020/\0020"; } -#header .chronology .heading, -#header .chronology .buttons { - white-space: nowrap; -} - -#header .scoped-chronology { - display: none; -} - -#header .scoped-chronology-switcher .switcher-link { - text-decoration: underline; - text-decoration-style: dotted; -} - -#header .scoped-chronology-switcher > div { - margin-left: 20px; -} - #secondary-nav { text-align: center; } diff --git a/src/static/js/client.js b/src/static/js/client.js index 38060999b..21c3911a2 100644 --- a/src/static/js/client.js +++ b/src/static/js/client.js @@ -3208,114 +3208,6 @@ clientSteps.getPageReferences.push(getAdditionalNamesBoxReferences); clientSteps.addInternalListeners.push(addAdditionalNamesBoxInternalListeners); clientSteps.addPageListeners.push(addAdditionalNamesBoxListeners); -// Scoped chronology links -------------------------------- - -const scopedChronologyLinksInfo = initInfo('scopedChronologyLinksInfo', { - switcher: null, - containers: null, - switcherLinks: null, - modes: null, - - session: { - selectedMode: 'wiki', - }, -}); - -function getScopedChronologyLinksReferences() { - const info = scopedChronologyLinksInfo; - - info.switcher = - document.querySelector('.scoped-chronology-switcher'); - - if (!info.switcher) { - return; - } - - info.containers = - Array.from(info.switcher.querySelectorAll(':scope > div')); - - info.switcherLinks = - Array.from(info.switcher.querySelectorAll('.switcher-link')); - - info.modes = - info.containers - .map(container => - Array.from(container.classList) - .find(className => className.startsWith('scope-')) - .slice('scope-'.length)); -} - -function addScopedChronologyLinksPageHandlers() { - const info = scopedChronologyLinksInfo; - const {session} = scopedChronologyLinksInfo; - - if (!info.switcher) { - return; - } - - for (const [index, { - container: currentContainer, - switcherLink: currentSwitcherLink, - }] of stitchArrays({ - container: info.containers, - switcherLink: info.switcherLinks, - }).entries()) { - const nextContainer = - atOffset(info.containers, index, +1, {wrap: true}); - - const nextSwitcherLink = - atOffset(info.switcherLinks, index, +1, {wrap: true}); - - const nextMode = - atOffset(info.modes, index, +1, {wrap: true}); - - currentSwitcherLink.addEventListener('click', domEvent => { - domEvent.preventDefault(); - - cssProp(currentContainer, 'display', 'none'); - cssProp(currentSwitcherLink, 'display', 'none'); - cssProp(nextContainer, 'display', 'block'); - cssProp(nextSwitcherLink, 'display', 'inline'); - - session.selectedMode = nextMode; - }); - } -} - -function mutateScopedChronologyLinksContent() { - const info = scopedChronologyLinksInfo; - - if (!info.switcher) { - return; - } - - const {selectedMode} = info.session; - - if (info.modes.includes(selectedMode)) { - const selectedIndex = info.modes.indexOf(selectedMode); - - for (const [index, { - container, - switcherLink, - }] of stitchArrays({ - container: info.containers, - switcherLink: info.switcherLinks, - }).entries()) { - if (index === selectedIndex) { - cssProp(container, 'display', 'block'); - cssProp(switcherLink, 'display', 'inline'); - } else { - cssProp(container, 'display', 'none'); - cssProp(switcherLink, 'display', 'none'); - } - } - } -} - -clientSteps.getPageReferences.push(getScopedChronologyLinksReferences); -clientSteps.mutatePageContent.push(mutateScopedChronologyLinksContent); -clientSteps.addPageListeners.push(addScopedChronologyLinksPageHandlers); - // Group contributions table ------------------------------ // TODO: Update to clientSteps style. From c1e2465e712580e9aeb4184305f62b6675a07176 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 18:30:01 -0300 Subject: [PATCH 28/29] content: linkContribution: manually check for blank tooltip This is unfortunately necessary to avoid the meaningless 'text-with-tooltip-interaction-cue' class (if there's no tooltip). --- src/content/dependencies/linkContribution.js | 38 ++++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index c43f31ca5..26f0b2d74 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -42,22 +42,30 @@ export default { language.encapsulate('misc.artistLink', workingCapsule => { const workingOptions = {}; + relations.tooltip.setSlots({ + showExternalLinks: slots.showExternalLinks, + showChronology: slots.showChronology, + chronologyKind: slots.chronologyKind, + }); + workingOptions.artist = - relations.textWithTooltip.slots({ - customInteractionCue: true, - - text: - relations.artistLink.slots({ - attributes: {class: 'text-with-tooltip-interaction-cue'}, - }), - - tooltip: - relations.tooltip.slots({ - showExternalLinks: slots.showExternalLinks, - showChronology: slots.showChronology, - chronologyKind: slots.chronologyKind, - }), - }); + (html.isBlank(relations.tooltip) + ? relations.artistLink + : relations.textWithTooltip.slots({ + customInteractionCue: true, + + text: + relations.artistLink.slots({ + attributes: {class: 'text-with-tooltip-interaction-cue'}, + }), + + tooltip: + relations.tooltip.slots({ + showExternalLinks: slots.showExternalLinks, + showChronology: slots.showChronology, + chronologyKind: slots.chronologyKind, + }), + })); if (slots.showContribution && data.contribution) { workingCapsule += '.withContribution'; From 2205ccb5b03814479233abb9e6c0cc6782912c59 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Jun 2024 18:37:08 -0300 Subject: [PATCH 29/29] test: update contribution tests (snapshot) --- .../generateAlbumReleaseInfo.js.test.cjs | 15 +- .../generateAlbumTrackList.js.test.cjs | 28 +-- .../generateTrackReleaseInfo.js.test.cjs | 8 +- .../snapshot/linkContribution.js.test.cjs | 188 +++++++++--------- test/snapshot/generateAlbumReleaseInfo.js | 12 +- test/snapshot/generateAlbumTrackList.js | 6 +- test/snapshot/generateTrackReleaseInfo.js | 4 +- 7 files changed, 131 insertions(+), 130 deletions(-) diff --git a/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs index 4a7f35c31..14cce64e8 100644 --- a/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs +++ b/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs @@ -7,16 +7,17 @@ 'use strict' exports[`test/snapshot/generateAlbumReleaseInfo.js > TAP > generateAlbumReleaseInfo (snapshot) > basic behavior 1`] = `

- By Toby Fox (music probably) and Tensei - - tenseimusic - Bandcamp (hot jams). + By Toby Fox (music probably) and Tensei + + tenseimusic + + Bandcamp (hot jams).
- Cover art by Hanni Brosh. + Cover art by Hanni Brosh.
- Wallpaper art by Hanni Brosh and Niklink (edits). + Wallpaper art by Hanni Brosh and Niklink (edits).
- Banner art by Hanni Brosh and Niklink (edits). + Banner art by Hanni Brosh and Niklink (edits).
Released 3/14/2011.
diff --git a/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs index 091f1b9f4..10ab17c46 100644 --- a/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs +++ b/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs @@ -10,7 +10,7 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList

  • (0:20) Track 1
  • [mocked: generateAlbumTrackListMissingDuration - slots: {}] Track 2
  • (0:40) Track 3
  • -
  • [mocked: generateAlbumTrackListMissingDuration - slots: {}] Track 4 by Apricot, Peach, and Cerise
  • +
  • [mocked: generateAlbumTrackListMissingDuration - slots: {}] Track 4 by Apricot, Peach, and Cerise
  • ` @@ -31,7 +31,7 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList Second section: -
    +
    ` @@ -52,17 +52,17 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList Second section: -
    +
    ` @@ -83,17 +83,17 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList Second section: -
    +
    ` @@ -114,17 +114,17 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList Second section: -
    +
    ` @@ -145,16 +145,16 @@ exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList Second section: -
    +
    ` diff --git a/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs b/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs index e35f93580..098fe145a 100644 --- a/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs +++ b/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs @@ -7,7 +7,7 @@ 'use strict' exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > basic behavior 1`] = `

    - By Toby Fox. + By Toby Fox.
    Released 11/29/2011.
    @@ -17,13 +17,13 @@ exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseI ` exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > cover artist contribs, non-unique 1`] = ` -

    By Toby Fox.

    +

    By Toby Fox.

    This wiki doesn't have any listening links for Suspicious Track.

    ` exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > cover artist contribs, unique 1`] = `

    - By Toby Fox. + By Toby Fox.
    Cover art by Alpaca (🔥).

    @@ -31,6 +31,6 @@ exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseI ` exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > reduced details 1`] = ` -

    By Toby Fox.

    +

    By Toby Fox.

    This wiki doesn't have any listening links for Suspicious Track.

    ` diff --git a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs index c1631d2b3..a9ac916eb 100644 --- a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs +++ b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs @@ -6,113 +6,113 @@ */ 'use strict' exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > loads of links 1`] = ` -Lorem Ipsum Lover - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other - - - loremipsum.io - - Other +Lorem Ipsum Lover + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other + + + loremipsum.io + + Other ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no accents 1`] = ` -Clark Powell -Grounder & Scratch -Toby Fox +Clark Powell +Grounder & Scratch +Toby Fox ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no preventWrapping 1`] = ` -Clark Powell - - plazmataz - - SoundCloud -Grounder & Scratch (Snooping) -Toby Fox - - tobyfox - - Bandcamp - - - toby.fox - - Other (Arrangement) +Clark Powell + + plazmataz + + SoundCloud +Grounder & Scratch (Snooping) +Toby Fox + + tobyfox + + Bandcamp + + + toby.fox + + Other (Arrangement) ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showContribution 1`] = ` -Clark Powell -Grounder & Scratch (Snooping) -Toby Fox (Arrangement) +Clark Powell +Grounder & Scratch (Snooping) +Toby Fox (Arrangement) ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showExternalLinks 1`] = ` -Clark Powell - - plazmataz - - SoundCloud -Grounder & Scratch -Toby Fox - - tobyfox - - Bandcamp - - - toby.fox - - Other +Clark Powell + + plazmataz + + SoundCloud +Grounder & Scratch +Toby Fox + + tobyfox + + Bandcamp + + + toby.fox + + Other ` exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > showContribution & showExternalLinks 1`] = ` -Clark Powell - - plazmataz - - SoundCloud -Grounder & Scratch (Snooping) -Toby Fox - - tobyfox - - Bandcamp - - - toby.fox - - Other (Arrangement) +Clark Powell + + plazmataz + + SoundCloud +Grounder & Scratch (Snooping) +Toby Fox + + tobyfox + + Bandcamp + + + toby.fox + + Other (Arrangement) ` diff --git a/test/snapshot/generateAlbumReleaseInfo.js b/test/snapshot/generateAlbumReleaseInfo.js index a109912f6..f41e502d7 100644 --- a/test/snapshot/generateAlbumReleaseInfo.js +++ b/test/snapshot/generateAlbumReleaseInfo.js @@ -8,22 +8,22 @@ testContentFunctions(t, 'generateAlbumReleaseInfo (snapshot)', async (t, evaluat name: 'generateAlbumReleaseInfo', args: [{ artistContribs: [ - {artist: {name: 'Toby Fox', directory: 'toby-fox', urls: null}, annotation: 'music probably'}, + {artist: {name: 'Toby Fox', directory: 'toby-fox', urls: []}, annotation: 'music probably'}, {artist: {name: 'Tensei', directory: 'tensei', urls: ['https://tenseimusic.bandcamp.com/']}, annotation: 'hot jams'}, ], coverArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: null}, annotation: null}, + {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, ], wallpaperArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: null}, annotation: null}, - {artist: {name: 'Niklink', directory: 'niklink', urls: null}, annotation: 'edits'}, + {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, + {artist: {name: 'Niklink', directory: 'niklink', urls: []}, annotation: 'edits'}, ], bannerArtistContribs: [ - {artist: {name: 'Hanni Brosh', directory: 'hb', urls: null}, annotation: null}, - {artist: {name: 'Niklink', directory: 'niklink', urls: null}, annotation: 'edits'}, + {artist: {name: 'Hanni Brosh', directory: 'hb', urls: []}, annotation: null}, + {artist: {name: 'Niklink', directory: 'niklink', urls: []}, annotation: 'edits'}, ], name: 'AlterniaBound', diff --git a/test/snapshot/generateAlbumTrackList.js b/test/snapshot/generateAlbumTrackList.js index 08b319025..a7c3f591d 100644 --- a/test/snapshot/generateAlbumTrackList.js +++ b/test/snapshot/generateAlbumTrackList.js @@ -10,13 +10,13 @@ testContentFunctions(t, 'generateAlbumTrackList (snapshot)', async (t, evaluate) }); const contribs1 = [ - {artist: {name: 'Apricot', directory: 'apricot', urls: null}}, + {artist: {name: 'Apricot', directory: 'apricot', urls: []}}, ]; const contribs2 = [ - {artist: {name: 'Apricot', directory: 'apricot', urls: null}}, + {artist: {name: 'Apricot', directory: 'apricot', urls: []}}, {artist: {name: 'Peach', directory: 'peach', urls: ['https://peach.bandcamp.com/']}}, - {artist: {name: 'Cerise', directory: 'cerise', urls: null}}, + {artist: {name: 'Cerise', directory: 'cerise', urls: []}}, ]; const color1 = '#fb07ff'; diff --git a/test/snapshot/generateTrackReleaseInfo.js b/test/snapshot/generateTrackReleaseInfo.js index 78f0fee73..931377c8c 100644 --- a/test/snapshot/generateTrackReleaseInfo.js +++ b/test/snapshot/generateTrackReleaseInfo.js @@ -4,8 +4,8 @@ import {testContentFunctions} from '#test-lib'; testContentFunctions(t, 'generateTrackReleaseInfo (snapshot)', async (t, evaluate) => { await evaluate.load(); - const artistContribs = [{artist: {name: 'Toby Fox', directory: 'toby-fox', urls: null}, annotation: null}]; - const coverArtistContribs = [{artist: {name: 'Alpaca', directory: 'alpaca', urls: null}, annotation: '🔥'}]; + const artistContribs = [{artist: {name: 'Toby Fox', directory: 'toby-fox', urls: []}, annotation: null}]; + const coverArtistContribs = [{artist: {name: 'Alpaca', directory: 'alpaca', urls: []}, annotation: '🔥'}]; evaluate.snapshot('basic behavior', { name: 'generateTrackReleaseInfo',