diff options
Diffstat (limited to 'src/data/things/homepage-layout')
7 files changed, 354 insertions, 0 deletions
diff --git a/src/data/things/homepage-layout/HomepageLayout.js b/src/data/things/homepage-layout/HomepageLayout.js new file mode 100644 index 00000000..e144bf80 --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayout.js @@ -0,0 +1,128 @@ +const HOMEPAGE_LAYOUT_DATA_FILE = 'homepage.yaml'; + +import {V} from '#composite'; +import Thing from '#thing'; +import {empty} from '#sugar'; +import {isStringNonEmpty, validateArrayItems} from '#validators'; + +import {exposeConstant} from '#composite/control-flow'; +import {contentString, thingList} from '#composite/wiki-properties'; + +export class HomepageLayout extends Thing { + static [Thing.friendlyName] = `Homepage Layout`; + static [Thing.wikiData] = 'homepageLayout'; + static [Thing.oneInstancePerWiki] = true; + + static [Thing.getPropertyDescriptors] = ({HomepageLayoutSection}) => ({ + // Update & expose + + sidebarContent: contentString(), + + navbarLinks: { + flags: {update: true, expose: true}, + update: {validate: validateArrayItems(isStringNonEmpty)}, + expose: {transform: value => value ?? []}, + }, + + sections: thingList(V(HomepageLayoutSection)), + + // Expose only + + isHomepageLayout: exposeConstant(V(true)), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Homepage': {ignore: true}, + + 'Sidebar Content': {property: 'sidebarContent'}, + 'Navbar Links': {property: 'navbarLinks'}, + }, + }; + + static [Thing.getYamlLoadingSpec] = ({ + documentModes: {allInOne}, + thingConstructors: { + HomepageLayout, + HomepageLayoutActionsRow, + HomepageLayoutAlbumCarouselRow, + HomepageLayoutAlbumGridRow, + HomepageLayoutRow, + HomepageLayoutSection, + }, + }) => ({ + title: `Process homepage layout file`, + file: HOMEPAGE_LAYOUT_DATA_FILE, + + documentMode: allInOne, + documentThing: document => { + if (document['Homepage']) { + return HomepageLayout; + } + + if (document['Section']) { + return HomepageLayoutSection; + } + + if (document['Row']) { + switch (document['Row']) { + case 'actions': + return HomepageLayoutActionsRow; + case 'album carousel': + return HomepageLayoutAlbumCarouselRow; + case 'album grid': + return HomepageLayoutAlbumGridRow; + default: + throw new TypeError(`Unrecognized row type ${document['Row']}`); + } + } + + return null; + }, + + connect(results) { + if (!empty(results) && !(results[0] instanceof HomepageLayout)) { + throw new Error(`Expected 'Homepage' document at top of homepage layout file`); + } + + const homepageLayout = results[0]; + const sections = []; + + let currentSection = null; + let currentSectionRows = []; + + const closeCurrentSection = () => { + if (currentSection) { + for (const row of currentSectionRows) { + row.section = currentSection; + } + + currentSection.rows = currentSectionRows; + sections.push(currentSection); + + currentSection = null; + currentSectionRows = []; + } + }; + + for (const entry of results.slice(1)) { + if (entry instanceof HomepageLayout) { + throw new Error(`Expected only one 'Homepage' document in total`); + } else if (entry instanceof HomepageLayoutSection) { + closeCurrentSection(); + currentSection = entry; + } else if (entry instanceof HomepageLayoutRow) { + if (currentSection) { + currentSectionRows.push(entry); + } else { + throw new Error(`Expected a 'Section' document to add following rows into`); + } + } + } + + closeCurrentSection(); + + homepageLayout.sections = sections; + }, + }); +} diff --git a/src/data/things/homepage-layout/HomepageLayoutActionsRow.js b/src/data/things/homepage-layout/HomepageLayoutActionsRow.js new file mode 100644 index 00000000..b6d19793 --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayoutActionsRow.js @@ -0,0 +1,31 @@ +import {V} from '#composite'; +import Thing from '#thing'; +import {validateArrayItems, isString} from '#validators'; + +import {exposeConstant} from '#composite/control-flow'; + +import {HomepageLayoutRow} from './HomepageLayoutRow.js'; + +export class HomepageLayoutActionsRow extends HomepageLayoutRow { + static [Thing.friendlyName] = `Homepage Actions Row`; + + static [Thing.getPropertyDescriptors] = () => ({ + // Update & expose + + actionLinks: { + flags: {update: true, expose: true}, + update: {validate: validateArrayItems(isString)}, + }, + + // Expose only + + isHomepageLayoutActionsRow: exposeConstant(V(true)), + type: exposeConstant(V('actions')), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Actions': {property: 'actionLinks'}, + }, + }; +} diff --git a/src/data/things/homepage-layout/HomepageLayoutAlbumCarouselRow.js b/src/data/things/homepage-layout/HomepageLayoutAlbumCarouselRow.js new file mode 100644 index 00000000..41cfd0af --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayoutAlbumCarouselRow.js @@ -0,0 +1,31 @@ +import {input, V} from '#composite'; +import Thing from '#thing'; + +import {exposeConstant} from '#composite/control-flow'; +import {referenceList, soupyFind} from '#composite/wiki-properties'; + +import {HomepageLayoutRow} from './HomepageLayoutRow.js'; + +export class HomepageLayoutAlbumCarouselRow extends HomepageLayoutRow { + static [Thing.friendlyName] = `Homepage Album Carousel Row`; + + static [Thing.getPropertyDescriptors] = (opts, {Album} = opts) => ({ + // Update & expose + + albums: referenceList({ + class: input.value(Album), + find: soupyFind.input('album'), + }), + + // Expose only + + isHomepageLayoutAlbumCarouselRow: exposeConstant(V(true)), + type: exposeConstant(V('album carousel')), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Albums': {property: 'albums'}, + }, + }; +} diff --git a/src/data/things/homepage-layout/HomepageLayoutAlbumGridRow.js b/src/data/things/homepage-layout/HomepageLayoutAlbumGridRow.js new file mode 100644 index 00000000..fafeb1ed --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayoutAlbumGridRow.js @@ -0,0 +1,68 @@ +import {input, V} from '#composite'; +import Thing from '#thing'; + +import {anyOf, is, isCountingNumber, validateReference} from '#validators'; + +import {exposeConstant, exposeDependency} from '#composite/control-flow'; +import {withResolvedReference} from '#composite/wiki-data'; +import {referenceList, soupyFind} from '#composite/wiki-properties'; + +import {HomepageLayoutRow} from './HomepageLayoutRow.js'; + +export class HomepageLayoutAlbumGridRow extends HomepageLayoutRow { + static [Thing.friendlyName] = `Homepage Album Grid Row`; + + static [Thing.getPropertyDescriptors] = (opts, {Album, Group} = opts) => ({ + // Update & expose + + sourceGroup: [ + { + flags: {expose: true, update: true, compose: true}, + + update: { + validate: + anyOf( + is('new-releases', 'new-additions'), + validateReference(Group[Thing.referenceType])), + }, + + expose: { + transform: (value, continuation) => + (value === 'new-releases' || value === 'new-additions' + ? value + : continuation(value)), + }, + }, + + withResolvedReference({ + ref: input.updateValue(), + find: soupyFind.input('group'), + }), + + exposeDependency('#resolvedReference'), + ], + + sourceAlbums: referenceList({ + class: input.value(Album), + find: soupyFind.input('album'), + }), + + countAlbumsFromGroup: { + flags: {update: true, expose: true}, + update: {validate: isCountingNumber}, + }, + + // Expose only + + isHomepageLayoutAlbumGridRow: exposeConstant(V(true)), + type: exposeConstant(V('album grid')), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Group': {property: 'sourceGroup'}, + 'Count': {property: 'countAlbumsFromGroup'}, + 'Albums': {property: 'sourceAlbums'}, + }, + }; +} diff --git a/src/data/things/homepage-layout/HomepageLayoutRow.js b/src/data/things/homepage-layout/HomepageLayoutRow.js new file mode 100644 index 00000000..5b0899e9 --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayoutRow.js @@ -0,0 +1,60 @@ +import {inspect} from 'node:util'; + +import {colors} from '#cli'; +import {V} from '#composite'; +import Thing from '#thing'; + +import {exposeConstant} from '#composite/control-flow'; +import {soupyFind, thing} from '#composite/wiki-properties'; + +export class HomepageLayoutRow extends Thing { + static [Thing.friendlyName] = `Homepage Row`; + + static [Thing.getPropertyDescriptors] = ({HomepageLayoutSection}) => ({ + // Update & expose + + section: thing(V(HomepageLayoutSection)), + + // Update only + + find: soupyFind(), + + // Expose only + + isHomepageLayoutRow: exposeConstant(V(true)), + + type: { + flags: {expose: true}, + + expose: { + compute() { + throw new Error(`'type' property validator must be overridden`); + }, + }, + }, + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Row': {ignore: true}, + }, + }; + + [inspect.custom](depth) { + const parts = []; + + parts.push(Thing.prototype[inspect.custom].apply(this)); + + if (depth >= 0 && this.section) { + const sectionName = this.section.name; + const index = this.section.rows.indexOf(this); + const rowNum = + (index === -1 + ? 'indeterminate position' + : `#${index + 1}`); + parts.push(` (${colors.yellow(rowNum)} in ${colors.green(sectionName)})`); + } + + return parts.join(''); + } +} diff --git a/src/data/things/homepage-layout/HomepageLayoutSection.js b/src/data/things/homepage-layout/HomepageLayoutSection.js new file mode 100644 index 00000000..1593ba6e --- /dev/null +++ b/src/data/things/homepage-layout/HomepageLayoutSection.js @@ -0,0 +1,30 @@ +import {V} from '#composite'; +import Thing from '#thing'; + +import {exposeConstant} from '#composite/control-flow'; +import {color, name, thingList} from '#composite/wiki-properties'; + +export class HomepageLayoutSection extends Thing { + static [Thing.friendlyName] = `Homepage Section`; + + static [Thing.getPropertyDescriptors] = ({HomepageLayoutRow}) => ({ + // Update & expose + + name: name(V(`Unnamed Homepage Section`)), + + color: color(), + + rows: thingList(V(HomepageLayoutRow)), + + // Expose only + + isHomepageLayoutSection: exposeConstant(V(true)), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Section': {property: 'name'}, + 'Color': {property: 'color'}, + }, + }; +} diff --git a/src/data/things/homepage-layout/index.js b/src/data/things/homepage-layout/index.js new file mode 100644 index 00000000..d003e39a --- /dev/null +++ b/src/data/things/homepage-layout/index.js @@ -0,0 +1,6 @@ +export * from './HomepageLayout.js'; +export * from './HomepageLayoutSection.js'; +export * from './HomepageLayoutRow.js'; +export * from './HomepageLayoutActionsRow.js'; +export * from './HomepageLayoutAlbumCarouselRow.js'; +export * from './HomepageLayoutAlbumGridRow.js'; |