« 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.js278
1 files changed, 180 insertions, 98 deletions
diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js
index e138a981..070c7c82 100644
--- a/src/content/dependencies/generatePageLayout.js
+++ b/src/content/dependencies/generatePageLayout.js
@@ -1,10 +1,11 @@
 import {openAggregate} from '#aggregate';
-import {empty} from '#sugar';
+import {atOffset, empty, repeat} from '#sugar';
 
 export default {
   contentDependencies: [
     'generateColorStyleRules',
     'generateFooterLocalizationLinks',
+    'generateImageOverlay',
     'generatePageSidebar',
     'generateSearchSidebarBox',
     'generateStickyHeadingContainer',
@@ -16,25 +17,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 = {};
@@ -61,6 +61,9 @@ export default {
     relations.colorStyleRules =
       relation('generateColorStyleRules');
 
+    relations.imageOverlay =
+      relation('generateImageOverlay');
+
     return relations;
   },
 
@@ -71,8 +74,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 +93,7 @@ export default {
       mutable: false,
     },
 
-    cover: {
+    artworkColumnContent: {
       type: 'html',
       mutable: false,
     },
@@ -228,6 +236,7 @@ export default {
     html,
     language,
     pagePath,
+    pagePathStringFromRoot,
     to,
   }) {
     const colors = getColors(slots.color ?? data.wikiColor);
@@ -241,6 +250,29 @@ 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 firstItemInArtworkColumn =
+      html.smooth(slots.artworkColumnContent)
+        .content[0];
+
+    const primaryCover =
+      (firstItemInArtworkColumn &&
+       html.resolve(firstItemInArtworkColumn, {normalize: 'tag'})
+         .attributes.has('class', 'cover-artwork')
+        ? firstItemInArtworkColumn
+        : null);
+
     const titleContentsHTML =
       (html.isBlank(slots.title)
         ? null
@@ -255,12 +287,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 +321,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 +374,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 +408,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 +484,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,51 +569,31 @@ export default {
               {id: 'additional-files', string: 'additionalFiles'},
               {id: 'commentary', string: 'commentary'},
               {id: 'artist-commentary', string: 'artistCommentary'},
+              {id: 'credit-sources', string: 'creditSources'},
             ])),
         ]);
 
-    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'}, [
-          html.tag('div', {id: 'image-overlay-action-content-without-size'},
-            language.$('releaseInfo.viewOriginalFile', {
-              link: html.tag('a', {class: 'image-overlay-view-original'},
-                language.$('releaseInfo.viewOriginalFile.link')),
-            })),
+    const styleRulesCSS =
+      html.resolve(slots.styleRules, {normalize: 'string'});
 
-          html.tag('div', {id: 'image-overlay-action-content-with-size'}, [
-            language.$('releaseInfo.viewOriginalFile.withSize', {
-              link:
-                html.tag('a', {class: 'image-overlay-view-original'},
-                  language.$('releaseInfo.viewOriginalFile.link')),
-
-              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'}),
-                      })),
-
-                    html.tag('span', {id: 'image-overlay-file-size-megabytes'},
-                      language.$('count.fileSize.megabytes', {
-                        megabytes:
-                          html.tag('span', {class: 'image-overlay-file-size-count'}),
-                      })),
-                  ]),
-            }),
+    const fallbackBackgroundStyleRule =
+      (styleRulesCSS.match(/body::before[^}]*background-image:/)
+        ? ''
+        : `body::before {\n` +
+          `    background-image: url("${to('media.path', 'bg.jpg')}");\n` +
+          `}`);
 
-            html.tag('span', {id: 'image-overlay-file-size-warning'},
-              language.$('releaseInfo.viewOriginalFile.sizeWarning')),
-          ]),
-        ]),
-      ]));
+    const numWallpaperParts =
+      html.resolve(slots.styleRules, {normalize: 'string'})
+        .match(/\.wallpaper-part:nth-child/g)
+        ?.length ?? 0;
+
+    const wallpaperPartsHTML =
+      html.tag('div', {class: 'wallpaper-parts'},
+        {[html.onlyIfContent]: true},
+
+        repeat(numWallpaperParts, () =>
+          html.tag('div', {class: 'wallpaper-part'})));
 
     const layoutHTML = [
       navHTML,
@@ -587,14 +639,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', {
@@ -626,13 +694,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', {
@@ -640,7 +710,6 @@ export default {
                   hreflang: lang,
                   href,
                 }))),
-
             */
 
             hasSocialEmbed &&
@@ -648,6 +717,12 @@ 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'),
@@ -656,6 +731,8 @@ export default {
             html.tag('style', [
               relations.colorStyleRules
                 .slot('color', slots.color ?? data.wikiColor),
+
+              fallbackBackgroundStyleRule,
               slots.styleRules,
             ]),
 
@@ -671,12 +748,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'},
@@ -684,13 +763,16 @@ export default {
                 showingSidebarRight &&
                   {class: 'showing-sidebar-right'},
 
+                sidebarsInContentColumn &&
+                  {class: 'sidebars-in-content-column'},
+
                 [
                   skippersHTML,
                   layoutHTML,
                 ]),
 
               // infoCardHTML,
-              imageOverlayHTML,
+              relations.imageOverlay,
             ]),
         ])
     ]).toString();