« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content/dependencies/generatePageLayout.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/content/dependencies/generatePageLayout.js')
-rw-r--r--src/content/dependencies/generatePageLayout.js328
1 files changed, 219 insertions, 109 deletions
diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js
index 7e9e49a0..0326f415 100644
--- a/src/content/dependencies/generatePageLayout.js
+++ b/src/content/dependencies/generatePageLayout.js
@@ -1,13 +1,16 @@
 import {openAggregate} from '#aggregate';
-import {empty} from '#sugar';
+import {atOffset, empty, repeat} from '#sugar';
 
 export default {
   contentDependencies: [
-    'generateColorStyleRules',
+    'generateColorStyleTag',
     'generateFooterLocalizationLinks',
+    'generateImageOverlay',
     'generatePageSidebar',
     'generateSearchSidebarBox',
+    'generateStaticURLStyleTag',
     'generateStickyHeadingContainer',
+    'generateWikiWallpaperStyleTag',
     'transformContent',
   ],
 
@@ -16,25 +19,24 @@ export default {
     'html',
     'language',
     'pagePath',
+    'pagePathStringFromRoot',
     'to',
     'wikiData',
   ],
 
-  sprawl({wikiInfo}) {
-    return {
-      enableSearch: wikiInfo.enableSearch,
-      footerContent: wikiInfo.footerContent,
-      wikiColor: wikiInfo.color,
-      wikiName: wikiInfo.nameShort,
-    };
-  },
+  sprawl: ({wikiInfo}) => ({
+    enableSearch: wikiInfo.enableSearch,
+    footerContent: wikiInfo.footerContent,
+    wikiColor: wikiInfo.color,
+    wikiName: wikiInfo.nameShort,
+    canonicalBase: wikiInfo.canonicalBase,
+  }),
 
-  data({wikiColor, wikiName}) {
-    return {
-      wikiColor,
-      wikiName,
-    };
-  },
+  data: (sprawl) => ({
+    wikiColor: sprawl.wikiColor,
+    wikiName: sprawl.wikiName,
+    canonicalBase: sprawl.canonicalBase,
+  }),
 
   relations(relation, sprawl) {
     const relations = {};
@@ -58,8 +60,17 @@ export default {
         relation('transformContent', sprawl.footerContent);
     }
 
-    relations.colorStyleRules =
-      relation('generateColorStyleRules');
+    relations.colorStyleTag =
+      relation('generateColorStyleTag');
+
+    relations.staticURLStyleTag =
+      relation('generateStaticURLStyleTag');
+
+    relations.wikiWallpaperStyleTag =
+      relation('generateWikiWallpaperStyleTag');
+
+    relations.imageOverlay =
+      relation('generateImageOverlay');
 
     return relations;
   },
@@ -71,8 +82,13 @@ export default {
     },
 
     showWikiNameInTitle: {
-      type: 'boolean',
-      default: true,
+      validate: v => v.is(true, false, 'auto'),
+      default: 'auto',
+    },
+
+    subtitle: {
+      type: 'html',
+      mutable: false,
     },
 
     showSearch: {
@@ -85,7 +101,7 @@ export default {
       mutable: false,
     },
 
-    cover: {
+    artworkColumnContent: {
       type: 'html',
       mutable: false,
     },
@@ -99,9 +115,9 @@ export default {
 
     color: {validate: v => v.isColor},
 
-    styleRules: {
-      validate: v => v.sparseArrayOf(v.isHTML),
-      default: [],
+    styleTags: {
+      type: 'html',
+      mutable: false,
     },
 
     mainClasses: {
@@ -228,6 +244,7 @@ export default {
     html,
     language,
     pagePath,
+    pagePathStringFromRoot,
     to,
   }) {
     const colors = getColors(slots.color ?? data.wikiColor);
@@ -241,6 +258,41 @@ export default {
     const mainContentHTML = html.tags([slots.mainContent]).toString();
     const hasID = id => mainContentHTML.includes(`id="${id}"`);
 
+    const oEmbedJSONHref =
+      (hasSocialEmbed && data.canonicalBase
+        ? data.canonicalBase +
+          pagePathStringFromRoot +
+          'oembed.json'
+        : null);
+
+    const canonicalHref =
+      (data.canonicalBase
+        ? data.canonicalBase + pagePathStringFromRoot
+        : null);
+
+    const primaryCover = (() => {
+      const apparentFirst = tag => html.smooth(tag).content[0];
+
+      const maybeTemplate =
+        apparentFirst(slots.artworkColumnContent);
+
+      if (!maybeTemplate) return null;
+
+      const maybeTemplateContent =
+        html.resolve(maybeTemplate, {normalize: 'tag'});
+
+      const maybeCoverArtwork =
+        apparentFirst(maybeTemplateContent);
+
+      if (!maybeCoverArtwork) return null;
+
+      if (maybeCoverArtwork.attributes.has('class', 'cover-artwork')) {
+        return maybeTemplate;
+      } else {
+        return null;
+      }
+    })();
+
     const titleContentsHTML =
       (html.isBlank(slots.title)
         ? null
@@ -255,12 +307,26 @@ export default {
       (html.isBlank(slots.title)
         ? null
      : slots.headingMode === 'sticky'
-        ? relations.stickyHeadingContainer.slots({
-            title: titleContentsHTML,
-            cover: slots.cover,
-          })
+        ? [
+            relations.stickyHeadingContainer.slots({
+              title: titleContentsHTML,
+              cover: primaryCover,
+            }),
+
+            relations.stickyHeadingContainer.clone().slots({
+              rootAttributes: {inert: true},
+            }),
+          ]
         : html.tag('h1', titleContentsHTML));
 
+    // TODO: There could be neat interactions with the sticky heading here,
+    // but for now subtitle is totally separate.
+    const subtitleHTML =
+      (html.isBlank(slots.subtitle)
+        ? null
+        : html.tag('h2', {class: 'page-subtitle'},
+            language.sanitize(slots.subtitle)));
+
     let footerContent = slots.footerContent;
 
     if (html.isBlank(footerContent) && relations.defaultFooterContent) {
@@ -275,12 +341,19 @@ export default {
       html.tag('main', {id: 'content'},
         {class: slots.mainClasses},
 
+        !html.isBlank(subtitleHTML) &&
+          {class: 'has-subtitle'},
+
         [
           titleHTML,
 
-          html.tag('div', {id: 'cover-art-container'},
+          html.tag('div', {id: 'artwork-column'},
             {[html.onlyIfContent]: true},
-            slots.cover),
+            {class: 'isolate-tooltip-z-indexing'},
+
+            slots.artworkColumnContent),
+
+          subtitleHTML,
 
           slots.additionalNames,
 
@@ -321,34 +394,32 @@ export default {
 
             slots.navLinks
               ?.filter(Boolean)
-              ?.map((cur, i) => {
+              ?.map((cur, i, entries) => {
                 let content;
 
                 if (cur.html) {
                   content = cur.html;
                 } else {
+                  const attributes = html.attributes();
                   let title;
-                  let href;
 
                   switch (cur.auto) {
                     case 'home':
                       title = data.wikiName;
-                      href = to('localized.home');
+                      attributes.set('href', to('localized.home'));
                       break;
                     case 'current':
                       title = slots.title;
-                      href = '';
+                      attributes.set('href', '');
                       break;
                     case null:
                     case undefined:
                       title = cur.title;
-                      href = to(...cur.path);
+                      attributes.set('href', to(...cur.path));
                       break;
                   }
 
-                  content = html.tag('a',
-                    {href},
-                    title);
+                  content = html.tag('a', attributes, title);
                 }
 
                 const showAsCurrent =
@@ -357,31 +428,51 @@ export default {
                   (slots.navLinkStyle === 'hierarchical' &&
                     i === slots.navLinks.length - 1);
 
-                return (
+                const navLink =
                   html.tag('span', {class: 'nav-link'},
                     showAsCurrent &&
                       {class: 'current'},
 
                     [
                       html.tag('span', {class: 'nav-link-content'},
-                        // Use inline-block styling on the content span,
-                        // rather than wrapping the whole nav-link in a proper
-                        // blockwrap, so that if the content spans multiple
-                        // lines, it'll kick the accent down beneath it.
-                        i > 0 &&
-                          {class: 'blockwrap'},
-
                         content),
 
                       html.tag('span', {class: 'nav-link-accent'},
+                        {[html.noEdgeWhitespace]: true},
                         {[html.onlyIfContent]: true},
-                        cur.accent),
-                    ]));
+
+                        language.$('misc.navAccent', {
+                          [language.onlyIfOptions]: ['links'],
+                          links: cur.accent,
+                        })),
+                    ]);
+
+                if (slots.navLinkStyle === 'index') {
+                  return navLink;
+                }
+
+                const prev =
+                  atOffset(entries, i, -1);
+
+                if (
+                  prev &&
+                  prev.releaseRestToWrapTogether !== true &&
+                  (prev.releaseRestToWrapTogether === false ||
+                   prev.auto === 'home')
+                ) {
+                  return navLink;
+                } else {
+                  return html.metatag('blockwrap', navLink);
+                }
               })),
 
           html.tag('div', {class: 'nav-bottom-row'},
             {[html.onlyIfContent]: true},
-            slots.navBottomRowContent),
+
+            language.$('misc.navAccent', {
+              [language.onlyIfOptions]: ['links'],
+              links: slots.navBottomRowContent,
+            })),
 
           html.tag('div', {class: 'nav-content'},
             {[html.onlyIfContent]: true},
@@ -413,14 +504,15 @@ export default {
 
     let showingSidebarLeft;
     let showingSidebarRight;
+    let sidebarsInContentColumn = false;
 
     const leftSidebar = getSidebar('leftSidebar', 'sidebar-left', willShowSearch);
     const rightSidebar = getSidebar('rightSidebar', 'sidebar-right', false);
 
     if (willShowSearch) {
       if (html.isBlank(leftSidebar)) {
-        leftSidebar.setSlot('initiallyHidden', true);
-        showingSidebarLeft = false;
+        sidebarsInContentColumn = true;
+        showingSidebarLeft = true;
       }
 
       leftSidebar.setSlot(
@@ -497,53 +589,40 @@ export default {
               {id: 'additional-files', string: 'additionalFiles'},
               {id: 'commentary', string: 'commentary'},
               {id: 'artist-commentary', string: 'artistCommentary'},
+              {id: 'crediting-sources', string: 'creditingSources'},
+              {id: 'referencing-sources', string: 'referencingSources'},
             ])),
         ]);
 
-    const imageOverlayHTML = html.tag('div', {id: 'image-overlay-container'},
-      html.tag('div', {id: 'image-overlay-content-container'}, [
-        html.tag('a', {id: 'image-overlay-image-container'}, [
-          html.tag('img', {id: 'image-overlay-image'}),
-          html.tag('img', {id: 'image-overlay-image-thumb'}),
-        ]),
-
-        html.tag('div', {id: 'image-overlay-action-container'},
-          language.encapsulate('releaseInfo.viewOriginalFile', capsule => [
-            html.tag('div', {id: 'image-overlay-action-content-without-size'},
-              language.$(capsule, {
-                link: html.tag('a', {class: 'image-overlay-view-original'},
-                  language.$(capsule, 'link')),
-              })),
+    const slottedStyleTags =
+      html.smush(slots.styleTags);
 
-            html.tag('div', {id: 'image-overlay-action-content-with-size'}, [
-              language.$(capsule, 'withSize', {
-                link:
-                  html.tag('a', {class: 'image-overlay-view-original'},
-                    language.$(capsule, 'link')),
+    const slottedWallpaperStyleTag =
+      slottedStyleTags.content
+        .find(tag => tag.attributes.has('class', 'wallpaper-style'));
 
-                size:
-                  html.tag('span',
-                    {[html.joinChildren]: ''},
-                    [
-                      html.tag('span', {id: 'image-overlay-file-size-kilobytes'},
-                        language.$('count.fileSize.kilobytes', {
-                          kilobytes:
-                            html.tag('span', {class: 'image-overlay-file-size-count'}),
-                        })),
+    const fallbackWallpaperStyleTag =
+      (slottedWallpaperStyleTag
+        ? html.blank()
+        : relations.wikiWallpaperStyleTag);
 
-                      html.tag('span', {id: 'image-overlay-file-size-megabytes'},
-                        language.$('count.fileSize.megabytes', {
-                          megabytes:
-                            html.tag('span', {class: 'image-overlay-file-size-count'}),
-                        })),
-                    ]),
-              }),
+    const usingWallpaperStyleTag =
+      (slottedWallpaperStyleTag
+        ? slottedWallpaperStyleTag
+        : html.resolve(fallbackWallpaperStyleTag, {normalize: 'tag'}));
 
-              html.tag('span', {id: 'image-overlay-file-size-warning'},
-                language.$(capsule, 'sizeWarning')),
-            ]),
-          ])),
-      ]));
+    const numWallpaperParts =
+      (usingWallpaperStyleTag &&
+       usingWallpaperStyleTag.attributes.has('data-wallpaper-mode', 'parts')
+        ? parseInt(usingWallpaperStyleTag.attributes.get('data-num-wallpaper-parts'))
+        : 0);
+
+    const wallpaperPartsHTML =
+      html.tag('div', {class: 'wallpaper-parts'},
+        {[html.onlyIfContent]: true},
+
+        repeat(numWallpaperParts, () =>
+          html.tag('div', {class: 'wallpaper-part'})));
 
     const layoutHTML = [
       navHTML,
@@ -589,14 +668,30 @@ export default {
 
           html.tag('head', [
             html.tag('title',
-              (slots.showWikiNameInTitle
-                ? language.formatString('misc.pageTitle.withWikiName', {
-                    title: slots.title,
-                    wikiName: data.wikiName,
-                  })
-                : language.formatString('misc.pageTitle', {
-                    title: slots.title,
-                  }))),
+              language.encapsulate('misc.pageTitle', workingCapsule => {
+                const workingOptions = {};
+
+                workingOptions.title = slots.title;
+
+                if (!html.isBlank(slots.subtitle)) {
+                  workingCapsule += '.withSubtitle';
+                  workingOptions.subtitle = slots.subtitle;
+                }
+
+                const showWikiName =
+                  (slots.showWikiNameInTitle === true
+                    ? true
+                 : slots.showWikiNameInTitle === 'auto'
+                    ? html.isBlank(slots.subtitle)
+                    : false);
+
+                if (showWikiName) {
+                  workingCapsule += '.withWikiName';
+                  workingOptions.wikiName = data.wikiName;
+                }
+
+                return language.$(workingCapsule, workingOptions);
+              })),
 
             html.tag('meta', {charset: 'utf-8'}),
             html.tag('meta', {
@@ -628,13 +723,15 @@ export default {
               Object.entries(meta)
                 .filter(([key, value]) => value)
                 .map(([key, value]) => html.tag('meta', {[key]: value}))),
+            */
 
-            canonical &&
+            canonicalHref &&
               html.tag('link', {
                 rel: 'canonical',
-                href: canonical,
+                href: canonicalHref,
               }),
 
+            /*
             ...(
               localizedCanonical
                 .map(({lang, href}) => html.tag('link', {
@@ -642,7 +739,6 @@ export default {
                   hreflang: lang,
                   href,
                 }))),
-
             */
 
             hasSocialEmbed &&
@@ -650,16 +746,25 @@ export default {
                 .clone()
                 .slot('mode', 'html'),
 
+            oEmbedJSONHref &&
+              html.tag('link', {
+                type: 'application/json+oembed',
+                href: oEmbedJSONHref,
+              }),
+
             html.tag('link', {
               rel: 'stylesheet',
               href: to('staticCSS.path', 'site.css'),
             }),
 
-            html.tag('style', [
-              relations.colorStyleRules
-                .slot('color', slots.color ?? data.wikiColor),
-              slots.styleRules,
-            ]),
+            relations.colorStyleTag
+              .slot('color', slots.color ?? data.wikiColor),
+
+            relations.staticURLStyleTag,
+
+            fallbackWallpaperStyleTag,
+
+            slottedStyleTags,
 
             html.tag('script', {
               src: to('staticLib.path', 'chroma-js/chroma.min.js'),
@@ -673,12 +778,14 @@ export default {
             html.tag('script', {
               blocking: 'render',
               type: 'module',
-              src: to('staticJS.path', 'client.js'),
+              src: to('staticJS.path', 'client/index.js'),
             }),
           ]),
 
           html.tag('body',
             [
+              wallpaperPartsHTML,
+
               html.tag('div', {id: 'page-container'},
                 showingSidebarLeft &&
                   {class: 'showing-sidebar-left'},
@@ -686,13 +793,16 @@ export default {
                 showingSidebarRight &&
                   {class: 'showing-sidebar-right'},
 
+                sidebarsInContentColumn &&
+                  {class: 'sidebars-in-content-column'},
+
                 [
                   skippersHTML,
                   layoutHTML,
                 ]),
 
               // infoCardHTML,
-              imageOverlayHTML,
+              relations.imageOverlay,
             ]),
         ])
     ]).toString();