mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-05 23:12:39 +00:00
Merge branch 'master' into marked-subsuper
This commit is contained in:
@@ -3,7 +3,6 @@ require('./headerNav.less');
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
const MAX_TEXT_LENGTH = 40;
|
const MAX_TEXT_LENGTH = 40;
|
||||||
|
|
||||||
const HeaderNav = React.forwardRef(({}, pagesRef)=>{
|
const HeaderNav = React.forwardRef(({}, pagesRef)=>{
|
||||||
@@ -11,11 +10,30 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{
|
|||||||
const renderHeaderLinks = ()=>{
|
const renderHeaderLinks = ()=>{
|
||||||
if(!pagesRef.current) return;
|
if(!pagesRef.current) return;
|
||||||
|
|
||||||
|
// Top Level Pages
|
||||||
|
// 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
|
||||||
|
|
||||||
|
const topLevelPages = {
|
||||||
|
'.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 getHeaderContent = el => el.querySelector('h1')?.textContent;
|
||||||
|
|
||||||
|
const topLevelPageSelector = Object.keys(topLevelPages).join(',');
|
||||||
|
|
||||||
const selector = [
|
const selector = [
|
||||||
'.pages > .page', // All page elements, which by definition have IDs
|
'.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(${topLevelPageSelector})) > [id]`, // All direct children of non-excluded .pages 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(${topLevelPageSelector})) > .columnWrapper > [id]`, // All direct children of non-excluded .page > .columnWrapper with an ID (V3)
|
||||||
'.page:not(:has(.toc)) h2', // All non-ToC H2 titles, like Monster frame titles
|
`.page:not(:has(${topLevelPageSelector})) h2`, // All non-excluded H2 titles, like Monster frame titles
|
||||||
];
|
];
|
||||||
const elements = pagesRef.current.querySelectorAll(selector.join(','));
|
const elements = pagesRef.current.querySelectorAll(selector.join(','));
|
||||||
if(!elements) return;
|
if(!elements) return;
|
||||||
@@ -23,45 +41,37 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{
|
|||||||
|
|
||||||
// navList is a list of objects which have the following structure:
|
// navList is a list of objects which have the following structure:
|
||||||
// {
|
// {
|
||||||
// depth : how deeply indented the item should be
|
// depth : how deeply indented the item should be
|
||||||
// text : the text to display in the nav link
|
// text : the text to display in the nav link
|
||||||
// link : the hyperlink to navigate to when clicked
|
// link : the hyperlink to navigate to when clicked
|
||||||
// className : [optional] the class to apply to the nav link for styling
|
// className : [optional] the class to apply to the nav link for styling
|
||||||
// }
|
// }
|
||||||
|
|
||||||
elements.forEach((el)=>{
|
elements.forEach((el)=>{
|
||||||
if(el.className.match(/\bpage\b/)) {
|
const navEntry = { // Default structure of a navList entry
|
||||||
let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number
|
depth : 7, // All unmatched elements with IDs are set to the maximum depth (7)
|
||||||
if(el.querySelector('.toc')){ // If the page contains a table of contents, add "- Contents" to the display text
|
text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto'
|
||||||
text += ' - Contents';
|
|
||||||
};
|
|
||||||
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({
|
|
||||||
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
|
link : el.id
|
||||||
});
|
}
|
||||||
});
|
if(el.classList.contains('page')) {
|
||||||
|
let text = `Page ${el.id.slice(1)}`; // Get the page # by trimming off the 'p' from the ID
|
||||||
return _.map(navList, (navItem, index)=>{
|
const pageType = Object.keys(topLevelPages).find(pageType => el.querySelector(pageType));
|
||||||
return <HeaderNavItem {...navItem} key={index} />;
|
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)=>
|
||||||
|
<HeaderNavItem {...navItem} key={index} />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <nav className='headerNav'>
|
return <nav className='headerNav'>
|
||||||
@@ -69,8 +79,7 @@ const HeaderNav = React.forwardRef(({}, pagesRef)=>{
|
|||||||
{renderHeaderLinks()}
|
{renderHeaderLinks()}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>;
|
</nav>;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const HeaderNavItem = ({ link, text, depth, className })=>{
|
const HeaderNavItem = ({ link, text, depth, className })=>{
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,11 @@
|
|||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
@depths: 1,2,3,4,5,6,7;
|
@depths: 0,1,2,3,4,5,6,7;
|
||||||
|
|
||||||
each(@depths, {
|
each(@depths, {
|
||||||
&.depth-@{value} {
|
&.depth-@{value} {
|
||||||
padding-left: ((@value - 1) * 0.5em);
|
padding-left: ((@value) * 0.5em);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user