Skip to content

Commit c9f6a68

Browse files
committed
Add links to embedded / source notebooks
1 parent 401debc commit c9f6a68

File tree

3 files changed

+166
-74
lines changed

3 files changed

+166
-74
lines changed

src/format/html/format-html-bootstrap.ts

Lines changed: 154 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
kLinkCitations,
1919
kQuartoTemplateParams,
2020
kRelatedFormatsTitle,
21+
kRelatedNotebooksTitle,
2122
kSectionDivs,
22-
kTitle,
2323
kTocDepth,
2424
kTocLocation,
2525
} from "../../config/constants.ts";
@@ -75,6 +75,7 @@ import {
7575
isPdfOutput,
7676
isPresentationOutput,
7777
} from "../../config/format.ts";
78+
import * as ld from "../../core/lodash.ts";
7879
import { basename } from "path/mod.ts";
7980

8081
export function formatPageLayout(format: Format) {
@@ -233,6 +234,9 @@ function bootstrapHtmlPostprocessor(
233234
renderedFormats: RenderedFormat[];
234235
},
235236
): Promise<HtmlPostProcessResult> => {
237+
// Resources used in this post processor
238+
const resources: string[] = [];
239+
236240
// use display-7 style for title
237241
const title = doc.querySelector("header > .title");
238242
if (title) {
@@ -314,78 +318,10 @@ function bootstrapHtmlPostprocessor(
314318

315319
// Inject links to other formats if there is another
316320
// format that of this file that has been rendered
317-
if (options.renderedFormats.length > 1) {
318-
let dlLinkTarget = doc.querySelector(`nav[role="doc-toc"]`);
319-
if (dlLinkTarget === null) {
320-
dlLinkTarget = doc.querySelector("#quarto-margin-sidebar");
321-
}
322-
323-
if (dlLinkTarget) {
324-
const bsIcon = (format: Format) => {
325-
if (isDocxOutput(format.pandoc)) {
326-
return "file-word";
327-
} else if (isPdfOutput(format.pandoc)) {
328-
return "file-pdf";
329-
} else if (isIpynbOutput(format.pandoc)) {
330-
return "journal-arrow-down";
331-
} else if (isMarkdownOutput(format.pandoc)) {
332-
return "file-code";
333-
} else if (isPresentationOutput(format.pandoc)) {
334-
return "file-slides";
335-
} else {
336-
return "file";
337-
}
338-
};
339-
340-
const dlName = (format: Format, path: string) => {
341-
if (isIpynbOutput(format.pandoc)) {
342-
return basename(path);
343-
} else {
344-
return undefined;
345-
}
346-
};
347-
348-
const containerEl = doc.createElement("div");
349-
containerEl.classList.add("quarto-alternate-formats");
350-
351-
const heading = doc.createElement("h2");
352-
if (format.language[kRelatedFormatsTitle]) {
353-
heading.innerText = format.language[kRelatedFormatsTitle];
354-
}
355-
containerEl.appendChild(heading);
356-
357-
const formatList = doc.createElement("ul");
358-
359-
for (const renderedFormat of options.renderedFormats) {
360-
if (!isHtmlOutput(renderedFormat.format.pandoc, true)) {
361-
const li = doc.createElement("li");
362-
363-
const link = doc.createElement("a");
364-
link.setAttribute("href", renderedFormat.path);
365-
const dlAttrValue = dlName(
366-
renderedFormat.format,
367-
renderedFormat.path,
368-
);
369-
if (dlAttrValue) {
370-
link.setAttribute("download", dlAttrValue);
371-
}
372-
373-
const icon = doc.createElement("i");
374-
icon.classList.add("bi");
375-
icon.classList.add(`bi-${bsIcon(renderedFormat.format)}`);
376-
link.appendChild(icon);
377-
link.appendChild(
378-
doc.createTextNode(`${renderedFormat.format.pandoc.to}`),
379-
);
321+
processAlternateFormatLinks(options, doc, format, resources);
380322

381-
li.appendChild(link);
382-
formatList.appendChild(li);
383-
}
384-
}
385-
containerEl.appendChild(formatList);
386-
dlLinkTarget.appendChild(containerEl);
387-
}
388-
}
323+
// Look for included / embedded notebooks and include those
324+
processNotebookEmbeds(doc, format, resources);
389325

390326
// default treatment for computational tables
391327
const addTableClasses = (table: Element, computational = false) => {
@@ -447,7 +383,6 @@ function bootstrapHtmlPostprocessor(
447383
}
448384

449385
// Process the title elements of this document
450-
const resources: string[] = [];
451386
const titleResourceFiles = processDocumentTitle(
452387
input,
453388
format,
@@ -476,6 +411,152 @@ function bootstrapHtmlPostprocessor(
476411
};
477412
}
478413

414+
// Provides a download name for a format/path
415+
const fileDownloadAttr = (format: Format, path: string) => {
416+
if (isIpynbOutput(format.pandoc)) {
417+
return basename(path);
418+
} else {
419+
return undefined;
420+
}
421+
};
422+
423+
// Provides an icon for a format
424+
const fileBsIconName = (format: Format) => {
425+
if (isDocxOutput(format.pandoc)) {
426+
return "file-word";
427+
} else if (isPdfOutput(format.pandoc)) {
428+
return "file-pdf";
429+
} else if (isIpynbOutput(format.pandoc)) {
430+
return "journal-arrow-down";
431+
} else if (isMarkdownOutput(format.pandoc)) {
432+
return "file-code";
433+
} else if (isPresentationOutput(format.pandoc)) {
434+
return "file-slides";
435+
} else {
436+
return "file";
437+
}
438+
};
439+
440+
function processAlternateFormatLinks(
441+
options: {
442+
inputMetadata: Metadata;
443+
inputTraits: PandocInputTraits;
444+
renderedFormats: RenderedFormat[];
445+
},
446+
doc: Document,
447+
format: Format,
448+
resources: string[],
449+
) {
450+
if (options.renderedFormats.length > 1) {
451+
let dlLinkTarget = doc.querySelector(`nav[role="doc-toc"]`);
452+
if (dlLinkTarget === null) {
453+
dlLinkTarget = doc.querySelector("#quarto-margin-sidebar");
454+
}
455+
456+
if (dlLinkTarget) {
457+
const containerEl = doc.createElement("div");
458+
containerEl.classList.add("quarto-alternate-formats");
459+
460+
const heading = doc.createElement("h2");
461+
if (format.language[kRelatedFormatsTitle]) {
462+
heading.innerText = format.language[kRelatedFormatsTitle];
463+
}
464+
containerEl.appendChild(heading);
465+
466+
const formatList = doc.createElement("ul");
467+
468+
for (const renderedFormat of options.renderedFormats) {
469+
if (!isHtmlOutput(renderedFormat.format.pandoc, true)) {
470+
const li = doc.createElement("li");
471+
472+
const link = doc.createElement("a");
473+
link.setAttribute("href", renderedFormat.path);
474+
const dlAttrValue = fileDownloadAttr(
475+
renderedFormat.format,
476+
renderedFormat.path,
477+
);
478+
if (dlAttrValue) {
479+
link.setAttribute("download", dlAttrValue);
480+
}
481+
482+
const icon = doc.createElement("i");
483+
icon.classList.add("bi");
484+
icon.classList.add(`bi-${fileBsIconName(renderedFormat.format)}`);
485+
link.appendChild(icon);
486+
link.appendChild(
487+
doc.createTextNode(`${renderedFormat.format.pandoc.to}`),
488+
);
489+
490+
li.appendChild(link);
491+
formatList.appendChild(li);
492+
493+
resources.push(renderedFormat.path);
494+
}
495+
}
496+
containerEl.appendChild(formatList);
497+
dlLinkTarget.appendChild(containerEl);
498+
}
499+
}
500+
}
501+
502+
function processNotebookEmbeds(
503+
doc: Document,
504+
format: Format,
505+
resources: string[],
506+
) {
507+
const notebookDivNodes = doc.querySelectorAll("[data-notebook]");
508+
if (notebookDivNodes.length > 0) {
509+
const nbPaths: string[] = [];
510+
notebookDivNodes.forEach((nbDivNode) => {
511+
const nbDivEl = nbDivNode as Element;
512+
const nbPath = nbDivEl.getAttribute("data-notebook");
513+
if (nbPath) {
514+
nbPaths.push(nbPath);
515+
}
516+
});
517+
518+
const containerEl = doc.createElement("div");
519+
containerEl.classList.add("quarto-alternate-notebooks");
520+
521+
const heading = doc.createElement("h2");
522+
if (format.language[kRelatedNotebooksTitle]) {
523+
heading.innerText = format.language[kRelatedNotebooksTitle];
524+
}
525+
containerEl.appendChild(heading);
526+
527+
const formatList = doc.createElement("ul");
528+
containerEl.appendChild(formatList);
529+
ld.uniq(nbPaths).forEach((nbPath) => {
530+
const li = doc.createElement("li");
531+
532+
const link = doc.createElement("a");
533+
link.setAttribute("href", nbPath);
534+
link.setAttribute("download", basename(nbPath));
535+
536+
const icon = doc.createElement("i");
537+
icon.classList.add("bi");
538+
icon.classList.add(`bi-journal-arrow-down`);
539+
link.appendChild(icon);
540+
link.appendChild(
541+
doc.createTextNode(`${basename(nbPath)}`),
542+
);
543+
544+
li.appendChild(link);
545+
formatList.appendChild(li);
546+
547+
resources.push(nbPath);
548+
});
549+
let dlLinkTarget = doc.querySelector(`nav[role="doc-toc"]`);
550+
if (dlLinkTarget === null) {
551+
dlLinkTarget = doc.querySelector("#quarto-margin-sidebar");
552+
}
553+
554+
if (dlLinkTarget) {
555+
dlLinkTarget.appendChild(containerEl);
556+
}
557+
}
558+
}
559+
479560
function bootstrapHtmlFinalizer(format: Format, flags: PandocFlags) {
480561
return (doc: Document): Promise<void> => {
481562
const { citesInMargin, refsInMargin } = processColumnElements(

src/resources/formats/html/bootstrap/_bootstrap-rules.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,10 +977,12 @@ td code:not(.sourceCode) {
977977
}
978978
}
979979

980+
.quarto-alternate-notebooks i.bi,
980981
.quarto-alternate-formats i.bi {
981982
margin-right: 0.4em;
982983
}
983984

985+
.sidebar .quarto-alternate-notebooks h2,
984986
.sidebar .quarto-alternate-formats h2,
985987
.sidebar nav[role="doc-toc"] > h2 {
986988
font-size: $toc-font-size;
@@ -993,21 +995,29 @@ td code:not(.sourceCode) {
993995
padding-top: 0px;
994996
}
995997

998+
.sidebar .quarto-alternate-notebooks h2,
999+
.sidebar .quarto-alternate-formats h2 {
1000+
margin-top: 1rem;
1001+
}
1002+
9961003
.sidebar nav[role="doc-toc"] > ul a {
9971004
border-left: 1px solid $toc-inactive-border;
9981005
padding-left: 0.6rem;
9991006
}
10001007

1008+
.sidebar .quarto-alternate-notebooks h2 > ul a,
10011009
.sidebar .quarto-alternate-formats h2 > ul a {
10021010
border-left: none;
10031011
padding-left: 0.6rem;
10041012
}
10051013

1014+
.sidebar .quarto-alternate-notebooks ul a:empty,
10061015
.sidebar .quarto-alternate-formats ul a:empty,
10071016
.sidebar nav[role="doc-toc"] > ul a:empty {
10081017
display: none;
10091018
}
10101019

1020+
.sidebar .quarto-alternate-notebooks ul,
10111021
.sidebar .quarto-alternate-formats ul,
10121022
.sidebar nav[role="doc-toc"] ul {
10131023
padding-left: 0;
@@ -1016,6 +1026,7 @@ td code:not(.sourceCode) {
10161026
font-weight: 300;
10171027
}
10181028

1029+
.sidebar .quarto-alternate-notebooks ul li a,
10191030
.sidebar .quarto-alternate-formats ul li a,
10201031
.sidebar nav[role="doc-toc"] > ul li a {
10211032
line-height: 1.1rem;

src/resources/language/_language.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ toc-title-document: "Table of contents"
22
toc-title-website: "On this page"
33

44
related-formats-title: "Alternate Formats"
5-
related-notebooks-title: "Notebooks"
5+
related-notebooks-title: "Source Notebooks"
66

77
section-title-abstract: "Abstract"
88
section-title-appendices: "Appendices"

0 commit comments

Comments
 (0)