Skip to content

Commit

Permalink
Merge pull request #294 from ndw/rework-pagetoc
Browse files Browse the repository at this point in the history
Rework pagetoc
  • Loading branch information
ndw authored Apr 5, 2023
2 parents 0d39d7c + 4732288 commit 91bf179
Show file tree
Hide file tree
Showing 155 changed files with 475 additions and 399 deletions.
4 changes: 2 additions & 2 deletions properties.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
ext {
xslTNGtitle = 'DocBook xslTNG'
xslTNGbaseName = 'docbook-xslTNG'
xslTNGversion = '2.0.15'
guideVersion = '2.0.15'
xslTNGversion = '2.0.16'
guideVersion = '2.0.16'
guidePrerelease = true

docbookVersion = '5.2CR5'
Expand Down
7 changes: 1 addition & 6 deletions src/guide/resources/css/guide-online.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ header img {
}

main {
max-width: 40rem;
max-width: 50rem;
margin-left: auto;
margin-right: auto;
padding-left: 1rem;
Expand Down Expand Up @@ -159,11 +159,6 @@ nav.pagetoc a:visited {

html.js .pagebody .tocwrapper {
font-size: 80%;
top: 2rem;
}

html.js .pagebody .pagetoc .ctrl {
top: 2rem;
}

.infofooter {
Expand Down
18 changes: 18 additions & 0 deletions src/guide/xml/changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@
xmlns:xlink="http://www.w3.org/1999/xlink">
<?db revhistory-style="list"?>

<revision xml:id="r216">
<revnumber>2.0.16</revnumber>
<date>2023-04-05</date>
<revdescription>
<para>This is still a pre-release; see <link linkend="r215">2.0.15</link>.</para>
<itemizedlist>
<listitem>
<para>Completely reworked the way the <link
linkend="onpage-toc">on-page ToC</link> is implemented. The previous
implementation used a CSS “sticky” property and flex to manage the
ToC. That was nice and all, but it was impossible to scroll the ToC.
There’s a lot more JavaScript in the new implementation, but since
there was already JavaScript in the implementation, I think that’s justified.</para>
</listitem>
</itemizedlist>
</revdescription>
</revision>

<revision xml:id="r215">
<revnumber>2.0.15</revnumber>
<date>2023-04-04</date>
Expand Down
4 changes: 1 addition & 3 deletions src/main/scss/media-screen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ html {
body {
height: 100%;
min-height: 100%;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
display: grid;
Expand Down Expand Up @@ -67,7 +65,7 @@ main {
}
}

@media screen and (min-width: 60rem) {
@media screen and (min-width: 70rem) {
main {
min-width: 50rem;
}
Expand Down
25 changes: 9 additions & 16 deletions src/main/scss/pagetoc.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
/* This provides CSS support for the in-page ToC */

html.js {
.pagebody {
display: flex;
grid-template-columns: 3fr 1fr;
grid-gap: 1rem;
}

.pagetoc {
padding-top: 3rem;
position: fixed;
top: 3rem;
right: 1em;
width: 18rem;
order: 2;
margin-right: 1rem;
font-size: 95%;

height: calc(100% - 5em);
overflow: hidden;
}

.pagetoc .toggle {
Expand All @@ -24,21 +21,17 @@ html.js {
}

.tocwrapper {
position: sticky;
top: 0;
border-left: 1px solid #aaaaaa;
padding-left: 0.25rem;
line-height: 1.5;
}

.pagetoc .ctrl {
position: sticky;
top: 0;
right: 0;
position: fixed;
right: 1em;
z-index: 2;
opacity: 1;
height: 0;
text-align: right;
}

.tocwrapper {
Expand Down Expand Up @@ -94,7 +87,7 @@ html.js {
}
}

@media screen and (max-width: 80rem) {
@media screen and (max-width: 82rem) {
.pagetoc {
display: none;
}
Expand Down
142 changes: 111 additions & 31 deletions src/main/web/js/pagetoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
const pagetoc = document.querySelector("nav.pagetoc");
const tocwrap = document.querySelector("nav.pagetoc div.tocwrapper");

const onerem = parseFloat(getComputedStyle(html).fontSize);
const mainMinWidthStyle = getComputedStyle(main).minWidth;
const mainMaxWidthStyle = getComputedStyle(main).maxWidth;
const mainMinWidth = parseInt(mainMinWidthStyle.substring(0, mainMinWidthStyle.length - 2));
const mainMaxWidth = parseInt(mainMaxWidthStyle.substring(0, mainMaxWidthStyle.length - 2));

const pagetocWidthStyle = getComputedStyle(pagetoc).width;
const pagetocMinWidth = parseInt(pagetocWidthStyle.substring(0, pagetocWidthStyle.length - 2));

let uncentered = true;
let dynamic = true;
html.querySelectorAll("head script").forEach(script => {
const data = script.getAttribute("data-dynamic-pagetoc");
Expand All @@ -31,30 +41,33 @@
let cbcount = 0;
let hidden_section = false;
let nothing_to_reveal = HIDDEN;
let idcount = 0;

const randomId = function() {
idcount++;
return `__random_${idcount}`;
};

const findsections = function(parent) {
const sections = [];
const ancestors = [];
let firstparent = null;
parent.querySelectorAll("section").forEach(sect => {
parent.querySelectorAll(":scope > article,:scope > section").forEach(sect => {
if (!sect.hasAttribute("id")) {
sect.setAttribute("id", randomId());
}
const id = sect.getAttribute("id");
const header = sect.querySelector("header");
const title = header && header.querySelector("h1,h2,h3,h4,h5,h6");
const skip = sect.classList.contains("nopagetoc");
if (id && title && !skip) {
toclength++;

// Don't find nested sections...
if (!firstparent) {
firstparent = sect.parentNode;
}

if (sect.parentNode == firstparent) {
sections.push({
"elem": sect,
"id": id,
"title": title.innerHTML
});
}
if (title && !skip) {
toclength++;
sections.push({
"elem": sect,
"id": id,
"title": title.innerHTML
});
}
});

Expand Down Expand Up @@ -100,17 +113,6 @@
});
tocwrap.parentNode.insertBefore(ctrl, tocwrap);
}
const header = main.querySelector("header");
const title = header && header.querySelector("h1,h2,h3,h4,h5,h6");
if (title) {
const div = document.createElement("div");
div.setAttribute("class", "li depth0 active");
const anchor = document.createElement("a");
anchor.setAttribute("href", "#");
anchor.innerHTML = title.innerHTML;
div.appendChild(anchor);
tocwrap.appendChild(div);
}
}

sections.forEach(section => {
Expand Down Expand Up @@ -147,6 +149,70 @@
});
};

const scrollHandler = function(event) {
if (pagetoc.scrollHeight <= pagetoc.clientHeight) {
// No scrolling is necessary
return;
}

let lastActiveIndex = 0;
let lastActiveDiv = null;
pagetoc.querySelectorAll("div.li").forEach((div, index) => {
if (div.classList.contains("active")) {
lastActiveIndex = index;
lastActiveDiv = div;
}
});

if (lastActiveDiv == null) {
// IntersectionObserver hasn't fired yet after a reload.
return;
}

let offset = pagetoc.clientHeight / 4;
if (lastActiveDiv.offsetTop > offset) {
pagetoc.scrollTo(0, lastActiveDiv.offsetTop - offset);
} else {
pagetoc.scrollTo(0, 0);
}
};

const centerMain = function() {
// If the pagetoc is not displayed, just leave everything alone
if (getComputedStyle(pagetoc).display == "none") {
return;
}

// Some padding
const pad = 4*onerem;

// Compute available width and new main width
let availableWidth = html.clientWidth - (pagetocMinWidth + pad);
let newWidth = Math.min(mainMaxWidth, availableWidth);

// Let the tocwidth grow if there's already more than enough room for main
let tocwidth = html.clientWidth - (newWidth + pad);
tocwidth = Math.max(tocwidth, pagetocMinWidth);
tocwidth = Math.min(tocwidth, (pagetocMinWidth * 1.5));
pagetoc.style.width = `${tocwidth}px`;

// Recompute the available width and main width (in case we changed the pagetoc width)
availableWidth = html.clientWidth - pagetoc.clientWidth;
newWidth = Math.min(mainMaxWidth, availableWidth - (2*pad));

let paddingLeft = Math.trunc((availableWidth - newWidth) / 2);

const nwStr = `${newWidth}px`;
main.style.width = nwStr;
main.style.minWidth = nwStr;
main.style.marginLeft = "0";
main.style.paddingLeft = `${paddingLeft}px`;
};

const resizeHandler = function(event) {
centerMain();
};

if (main && pagetoc) {
if (!window.DocBook) {
window.DocBook = {};
Expand Down Expand Up @@ -175,9 +241,9 @@
}

sections = findsections(main);
if (toclength > 1) {
maketoc(sections, 0);
maketoc(sections, 0);

if (toclength > 1) {
if (dynamic) {
const observer = new IntersectionObserver((sections) => {
sections.forEach((section) => {
Expand All @@ -201,17 +267,31 @@

hidden_section = hidden_section || (addRemove === "remove");
cbcount++;
if (cbcount == toclength && !hidden_section) {
nothingToReveal();

if (cbcount == toclength) {
// We've made a complete pass. If there's nothing hidden,
// then there's nothing to reveal...
if (!hidden_section) {
nothingToReveal();
} else {
// Otherwise, the first time through, center main
if (uncentered) {
centerMain();
uncentered = false;
}
}
}
});
});

// Observe all the sections of the article
main.querySelectorAll('section').forEach((section) => {
main.querySelectorAll('article,section').forEach((section) => {
observer.observe(section);
});
}

window.addEventListener("scroll", scrollHandler);
window.addEventListener("resize", resizeHandler);
} else {
pagetoc.style.display = "none";
}
Expand Down
12 changes: 2 additions & 10 deletions src/main/xslt/modules/components.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
version="3.0">

<xsl:template match="db:preface|db:chapter|db:appendix|db:article
|db:topic">
|db:topic|db:acknowledgements|db:dedication|db:colophon">
<xsl:variable name="gi" select="if (parent::*)
then 'div'
then 'section'
else 'article'"/>
<xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
<xsl:apply-templates select="." mode="m:attributes"/>
Expand All @@ -23,13 +23,5 @@
</xsl:element>
</xsl:template>

<xsl:template match="db:acknowledgements|db:dedication|db:colophon">
<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<xsl:apply-templates select="." mode="m:generate-titlepage"/>
<xsl:apply-templates/>
</div>
</xsl:template>

</xsl:stylesheet>

2 changes: 1 addition & 1 deletion src/main/xslt/modules/refentry.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<xsl:template match="db:refentry">
<xsl:variable name="gi" select="if (parent::*)
then 'div'
then 'section'
else 'article'"/>

<xsl:element name="{$gi}" namespace="http://www.w3.org/1999/xhtml">
Expand Down
4 changes: 2 additions & 2 deletions src/test/resources/expected/article.001.html
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@
erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam
consequat. Curabitur augue lorem, dapibus quis, laoreet et,
pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu,
feugiat in, orci. In hac habitasse platea dictumst.</p><div id="article_ack1" class="acknowledgements component"><header><h2>Acknowledgements</h2></header>
feugiat in, orci. In hac habitasse platea dictumst.</p><section id="article_ack1" class="acknowledgements component"><header><h2>Acknowledgements</h2></header>
<p>I'd like to thank all the tests that came before me.</p>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Expand Down Expand Up @@ -462,4 +462,4 @@
consequat. Curabitur augue lorem, dapibus quis, laoreet et,
pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu,
feugiat in, orci. In hac habitasse platea dictumst.</p>
</div></article></main><nav class="bottom"></nav></body></html>
</section></article></main><nav class="bottom"></nav></body></html>
Loading

0 comments on commit 91bf179

Please sign in to comment.