diff options
Diffstat (limited to 'src/data')
23 files changed, 306 insertions, 72 deletions
diff --git a/src/data/composite/data/excludeFromList.js b/src/data/composite/data/excludeFromList.js index d798dcdc..2a3e818e 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 4f818a79..356b1119 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 0a47c43c..c80bb350 100644 --- a/src/data/composite/data/index.js +++ b/src/data/composite/data/index.js @@ -3,16 +3,32 @@ // 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'; + +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) + +export {default as withSum} from './withSum.js'; diff --git a/src/data/composite/data/withFilteredList.js b/src/data/composite/data/withFilteredList.js index 82e56903..60fe66f4 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 edfa3403..31b1a742 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/withIndexInList.js b/src/data/composite/data/withIndexInList.js new file mode 100644 index 00000000..b1af2033 --- /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/withMappedList.js b/src/data/composite/data/withMappedList.js index e0a700b2..0bc63a92 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/withNearbyItemFromList.js b/src/data/composite/data/withNearbyItemFromList.js new file mode 100644 index 00000000..83a8cc21 --- /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}), + }), + }, + ], +}); diff --git a/src/data/composite/data/withPropertiesFromList.js b/src/data/composite/data/withPropertiesFromList.js index 08907bab..fb4134bc 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 a2c66d77..65ebf77b 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 dd810786..a7d21768 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 39a666dc..820d628a 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'; diff --git a/src/data/composite/things/contribution/index.js b/src/data/composite/things/contribution/index.js index 2c812644..9b22be2e 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 00000000..56704c8b --- /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', + }), + ], +}); diff --git a/src/data/composite/things/contribution/withContributionArtist.js b/src/data/composite/things/contribution/withContributionArtist.js index 9e588936..5a611c1a 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'], diff --git a/src/data/composite/wiki-data/withRecontextualizedContributionList.js b/src/data/composite/wiki-data/withRecontextualizedContributionList.js index 06c997b5..d2401eac 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 23b91691..b5d7255b 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 a0e6e52b..d9a6b417 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/album.js b/src/data/things/album.js index ae5226ba..a0021946 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/contribution.js b/src/data/things/contribution.js index 9d6a9711..79acf1e1 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, @@ -35,6 +36,11 @@ export class Contribution extends Thing { update: {validate: isStringNonEmpty}, }, + artistProperty: { + flags: {update: true, expose: true}, + update: {validate: isStringNonEmpty}, + }, + date: simpleDate(), artist: [ @@ -155,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) { diff --git a/src/data/things/flash.js b/src/data/things/flash.js index 7d37ed81..89e59fe7 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/language.js b/src/data/things/language.js index f20927a4..88f16ecb 100644 --- a/src/data/things/language.js +++ b/src/data/things/language.js @@ -208,9 +208,7 @@ export class Language extends Thing { args.at(-1) !== null; const key = - (hasOptions ? args.slice(0, -1) : args) - .filter(Boolean) - .join('.'); + this.#joinKeyParts(hasOptions ? args.slice(0, -1) : args); const options = (hasOptions @@ -843,6 +841,33 @@ export class Language extends Thing { return this.formatString('count.fileSize.bytes', {bytes}); } } + + // Utility function to quickly provide a useful string key + // (generally a prefix) to stuff nested beneath it. + encapsulate(...args) { + const fn = + (typeof args.at(-1) === 'function' + ? args.at(-1) + : null); + + const parts = + (fn + ? args.slice(0, -1) + : args); + + const capsule = + this.#joinKeyParts(parts); + + if (fn) { + return fn(capsule); + } else { + return capsule; + } + } + + #joinKeyParts(parts) { + return parts.filter(Boolean).join('.'); + } } const countHelper = (stringKey, optionName = stringKey) => diff --git a/src/data/things/track.js b/src/data/things/track.js index 28a784f5..4aaf364c 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({ |