From f6c95fb8b773ebeff021bd7a581e352e7ea5bddd Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Tue, 14 Jan 2025 08:25:46 +1300 Subject: [PATCH 1/7] Extend header nav to exclude frontCover pages --- .../brewRenderer/headerNav/headerNav.jsx | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index 68963129f..8278e2bd9 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -11,11 +11,18 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ const renderHeaderLinks = ()=>{ if(!pagesRef.current) return; + const excludedPages = { + '.frontCover' : 'Cover Page', + '.toc' : 'Contents' + }; + + const excluded = Object.keys(excludedPages).join(','); + const selector = [ - '.pages > .page', // All page elements, which by definition have IDs - '.page:not(:has(.toc)) > [id]', // All direct children of non-ToC .page with an ID (Legacy) - '.page:not(:has(.toc)) > .columnWrapper > [id]', // All direct children of non-ToC .page > .columnWrapper with an ID (V3) - '.page:not(:has(.toc)) h2', // All non-ToC H2 titles, like Monster frame titles + '.pages > .page', // All page elements, which by definition have IDs + `.page:not(:has(${excluded})) > [id]`, // All direct children of non-excluded .pages with an ID (Legacy) + `.page:not(:has(${excluded})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3) + `.page:not(:has(${excluded})) h2`, // All non-excluded H2 titles, like Monster frame titles ]; const elements = pagesRef.current.querySelectorAll(selector.join(',')); if(!elements) return; @@ -32,9 +39,13 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ elements.forEach((el)=>{ if(el.className.match(/\bpage\b/)) { let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number - if(el.querySelector('.toc')){ // If the page contains a table of contents, add "- Contents" to the display text - text += ' - Contents'; - }; + Object.keys(excludedPages).every((pageType)=>{ + if(el.querySelector(pageType)){ // If the page contains a table of contents, add "- Contents" to the display text + text += ` - ${excludedPages[pageType]}`; + return false; + }; + return true; + }); navList.push({ depth : 0, // Pages are always at the least indented level text : text, From 9f5a29099c4fd206f1e28d31dcab6891a84a0599 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 19 Feb 2025 13:24:07 +1300 Subject: [PATCH 2/7] Address requested changes --- .../brewRenderer/headerNav/headerNav.jsx | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index 8278e2bd9..d5332f011 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -11,18 +11,31 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ const renderHeaderLinks = ()=>{ if(!pagesRef.current) return; - const excludedPages = { - '.frontCover' : 'Cover Page', - '.toc' : 'Contents' + // Top Level Pages + // Pages that contain an element with a specified class will NOT have its content scanned for navigation headers + // e.g. cover pages, table of content pages + // Instead, a label will be added to the navigation at the page level + // --- + // The property name is class that will be used for detecting the page is a top level page + // The property value is a function that returns the text to be used + + const topLevelPages = { + '.frontCover' : (text)=>{ return text ? `Cover: ${text}` : 'Cover Page'; }, + '.insideCover' : (text)=>{ return text ? `Interior: ${text}` : 'Interior Cover Page'; }, + '.partCover' : (text)=>{ return text ? `Section: ${text}` : 'Section Cover Page'; }, + '.backCover' : (text)=>{ return text ? `Back: ${text}` : 'Rear Cover Page'; }, + '.toc' : ()=>{ return 'Table of Contents'; }, }; - const excluded = Object.keys(excludedPages).join(','); + const getTextContent = (el, pageType)=>{ return el.querySelector(pageType).textContent; }; + + const topLevelPageSelector = Object.keys(topLevelPages).join(','); const selector = [ - '.pages > .page', // All page elements, which by definition have IDs - `.page:not(:has(${excluded})) > [id]`, // All direct children of non-excluded .pages with an ID (Legacy) - `.page:not(:has(${excluded})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3) - `.page:not(:has(${excluded})) h2`, // All non-excluded H2 titles, like Monster frame titles + '.pages > .page', // All page elements, which by definition have IDs + `.page:not(:has(${topLevelPageSelector})) > [id]`, // All direct children of non-excluded .pages with an ID (Legacy) + `.page:not(:has(${topLevelPageSelector})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3) + `.page:not(:has(${topLevelPageSelector})) h2`, // All non-excluded H2 titles, like Monster frame titles ]; const elements = pagesRef.current.querySelectorAll(selector.join(',')); if(!elements) return; @@ -39,9 +52,9 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ elements.forEach((el)=>{ if(el.className.match(/\bpage\b/)) { let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number - Object.keys(excludedPages).every((pageType)=>{ - if(el.querySelector(pageType)){ // If the page contains a table of contents, add "- Contents" to the display text - text += ` - ${excludedPages[pageType]}`; + Object.keys(topLevelPages).every((pageType)=>{ + if(el.querySelector(pageType)){ // If a Top Level Page, add the text result to the navigation text + text += ` - ${topLevelPages[pageType](getTextContent(el, pageType))}`; return false; }; return true; From 87d76ea8f67318dcb9dfe92e968dbe1c689a7325 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 19 Feb 2025 13:46:35 +1300 Subject: [PATCH 3/7] Change topLevelPages functions for cover pages --- client/homebrew/brewRenderer/headerNav/headerNav.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index d5332f011..895b59589 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -20,10 +20,10 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ // The property value is a function that returns the text to be used const topLevelPages = { - '.frontCover' : (text)=>{ return text ? `Cover: ${text}` : 'Cover Page'; }, - '.insideCover' : (text)=>{ return text ? `Interior: ${text}` : 'Interior Cover Page'; }, - '.partCover' : (text)=>{ return text ? `Section: ${text}` : 'Section Cover Page'; }, - '.backCover' : (text)=>{ return text ? `Back: ${text}` : 'Rear Cover Page'; }, + '.frontCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Cover: ${text}` : 'Cover Page'; }, + '.insideCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Interior: ${text}` : 'Interior Cover Page'; }, + '.partCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Section: ${text}` : 'Section Cover Page'; }, + '.backCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Back: ${text}` : 'Rear Cover Page'; }, '.toc' : ()=>{ return 'Table of Contents'; }, }; @@ -54,7 +54,7 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number Object.keys(topLevelPages).every((pageType)=>{ if(el.querySelector(pageType)){ // If a Top Level Page, add the text result to the navigation text - text += ` - ${topLevelPages[pageType](getTextContent(el, pageType))}`; + text += ` - ${topLevelPages[pageType](el, pageType)}`; return false; }; return true; From ca0f18acd6878a11efad23190b6a49fd66ae7063 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 19 Feb 2025 15:22:08 +1300 Subject: [PATCH 4/7] Update getTextContent function to getHeaderContent --- client/homebrew/brewRenderer/headerNav/headerNav.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index 895b59589..3d2902da9 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -20,14 +20,14 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ // The property value is a function that returns the text to be used const topLevelPages = { - '.frontCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Cover: ${text}` : 'Cover Page'; }, - '.insideCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Interior: ${text}` : 'Interior Cover Page'; }, - '.partCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Section: ${text}` : 'Section Cover Page'; }, - '.backCover' : (el, pageType)=>{ const text = getTextContent(el, pageType); return text ? `Back: ${text}` : 'Rear Cover Page'; }, + '.frontCover' : (el, pageType)=>{ const text = getHeaderContent(el); return text ? `Cover: ${text}` : 'Cover Page'; }, + '.insideCover' : (el, pageType)=>{ const text = getHeaderContent(el); return text ? `Interior: ${text}` : 'Interior Cover Page'; }, + '.partCover' : (el, pageType)=>{ const text = getHeaderContent(el); return text ? `Section: ${text}` : 'Section Cover Page'; }, + '.backCover' : (el, pageType)=>{ const text = getHeaderContent(el); return text ? `Back: ${text}` : 'Rear Cover Page'; }, '.toc' : ()=>{ return 'Table of Contents'; }, }; - const getTextContent = (el, pageType)=>{ return el.querySelector(pageType).textContent; }; + const getHeaderContent = (el)=>{ return el.querySelector('h1')?.textContent; }; const topLevelPageSelector = Object.keys(topLevelPages).join(','); From f421ce1d93e1c3fcb048735ffefa1a5ae3dfcb4c Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 19 Feb 2025 16:10:27 +1300 Subject: [PATCH 5/7] Fix missing depth styling --- client/homebrew/brewRenderer/headerNav/headerNav.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.less b/client/homebrew/brewRenderer/headerNav/headerNav.less index 8b35041d9..a5769ef99 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.less +++ b/client/homebrew/brewRenderer/headerNav/headerNav.less @@ -35,11 +35,11 @@ font-weight: 900; } - @depths: 1,2,3,4,5,6,7; + @depths: 0,1,2,3,4,5,6,7; each(@depths, { &.depth-@{value} { - padding-left: ((@value - 1) * 0.5em); + padding-left: ((@value) * 0.5em); } }); } From b36376f9e824693c87bfab83408786c31bb7adec Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 19 Feb 2025 13:44:28 -0500 Subject: [PATCH 6/7] Linting --- .../brewRenderer/headerNav/headerNav.jsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index 3d2902da9..e1ef56fb2 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -3,7 +3,6 @@ require('./headerNav.less'); import * as React from 'react'; import * as _ from 'lodash'; - const MAX_TEXT_LENGTH = 40; const HeaderNav = React.forwardRef(({}, pagesRef)=>{ @@ -32,10 +31,10 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ const topLevelPageSelector = Object.keys(topLevelPages).join(','); const selector = [ - '.pages > .page', // All page elements, which by definition have IDs - `.page:not(:has(${topLevelPageSelector})) > [id]`, // All direct children of non-excluded .pages with an ID (Legacy) - `.page:not(:has(${topLevelPageSelector})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3) - `.page:not(:has(${topLevelPageSelector})) h2`, // All non-excluded H2 titles, like Monster frame titles + '.pages > .page', // All page elements, which by definition have IDs + `.page:not(:has(${topLevelPageSelector})) > [id]`, // All direct children of non-excluded .pages with an ID (Legacy) + `.page:not(:has(${topLevelPageSelector})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3) + `.page:not(:has(${topLevelPageSelector})) h2`, // All non-excluded H2 titles, like Monster frame titles ]; const elements = pagesRef.current.querySelectorAll(selector.join(',')); if(!elements) return; @@ -43,17 +42,17 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ // navList is a list of objects which have the following structure: // { - // depth : how deeply indented the item should be - // text : the text to display in the nav link - // link : the hyperlink to navigate to when clicked - // className : [optional] the class to apply to the nav link for styling + // depth : how deeply indented the item should be + // text : the text to display in the nav link + // link : the hyperlink to navigate to when clicked + // className : [optional] the class to apply to the nav link for styling // } elements.forEach((el)=>{ if(el.className.match(/\bpage\b/)) { - let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number + let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number Object.keys(topLevelPages).every((pageType)=>{ - if(el.querySelector(pageType)){ // If a Top Level Page, add the text result to the navigation text + if(el.querySelector(pageType)){ // If a Top Level Page, add the text result to the navigation text text += ` - ${topLevelPages[pageType](el, pageType)}`; return false; }; @@ -67,17 +66,17 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ }); return; } - if(el.localName.match(/^h[1-6]/)){ // Header elements H1 through H6 + if(el.localName.match(/^h[1-6]/)){ // Header elements H1 through H6 navList.push({ - depth : el.localName[1], // Depth is set by the header level - text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' + depth : el.localName[1], // Depth is set by the header level + text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' link : el.id }); return; } navList.push({ - depth : 7, // All unmatched elements with IDs are set to the maximum depth (7) - text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' + depth : 7, // All unmatched elements with IDs are set to the maximum depth (7) + text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' link : el.id }); }); From 917b6b314568f95f857c8c25c58d253ce7cfd91b Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 19 Feb 2025 15:52:52 -0500 Subject: [PATCH 7/7] Shrinking down some logic --- .../brewRenderer/headerNav/headerNav.jsx | 60 +++++++------------ 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index e1ef56fb2..cfc10bbd6 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -11,9 +11,8 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ if(!pagesRef.current) return; // Top Level Pages - // Pages that contain an element with a specified class will NOT have its content scanned for navigation headers - // e.g. cover pages, table of content pages - // Instead, a label will be added to the navigation at the page level + // Pages that contain an element with a specified class (e.g. cover pages, table of contents) + // will NOT have its content scanned for navigation headers, instead displaying a custom label // --- // The property name is class that will be used for detecting the page is a top level page // The property value is a function that returns the text to be used @@ -26,7 +25,7 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ '.toc' : ()=>{ return 'Table of Contents'; }, }; - const getHeaderContent = (el)=>{ return el.querySelector('h1')?.textContent; }; + const getHeaderContent = el => el.querySelector('h1')?.textContent; const topLevelPageSelector = Object.keys(topLevelPages).join(','); @@ -49,42 +48,30 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{ // } elements.forEach((el)=>{ - if(el.className.match(/\bpage\b/)) { - let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number - Object.keys(topLevelPages).every((pageType)=>{ - if(el.querySelector(pageType)){ // If a Top Level Page, add the text result to the navigation text - text += ` - ${topLevelPages[pageType](el, pageType)}`; - return false; - }; - return true; - }); - navList.push({ - depth : 0, // Pages are always at the least indented level - text : text, - link : el.id, - className : 'pageLink' - }); - return; - } - if(el.localName.match(/^h[1-6]/)){ // Header elements H1 through H6 - navList.push({ - depth : el.localName[1], // Depth is set by the header level - text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' - link : el.id - }); - return; - } - navList.push({ + const navEntry = { // Default structure of a navList entry depth : 7, // All unmatched elements with IDs are set to the maximum depth (7) text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' link : el.id - }); - }); - - return _.map(navList, (navItem, index)=>{ - return ; + } + if(el.classList.contains('page')) { + let text = `Page ${el.id.slice(1)}`; // Get the page # by trimming off the 'p' from the ID + const pageType = Object.keys(topLevelPages).find(pageType => el.querySelector(pageType)); + if (pageType) + text += ` - ${topLevelPages[pageType](el, pageType)}` // If a Top Level Page, add extra label + + navEntry.depth = 0; // Pages are always at the least indented level + navEntry.text = text; + navEntry.className = 'pageLink'; + } + else if(el.localName.match(/^h[1-6]/)){ // Header elements H1 through H6 + navEntry.depth = el.localName[1]; // Depth is set by the header level + } + navList.push(navEntry); }); + return _.map(navList, (navItem, index)=> + + ); }; return ; -} -); +}); const HeaderNavItem = ({ link, text, depth, className })=>{