Skip to content

Commit 8e34693

Browse files
footnote: list at the end of a page (#1497)
\
1 parent 0af05f5 commit 8e34693

18 files changed

Lines changed: 487 additions & 52 deletions

znai-docs/znai/flow/footnotes.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,24 @@ To add a reference to the footnote use `[^my-id]` which will result in [^my-id]
1818
Constructor()
1919
```
2020
21-
Note: numeric footnotes are treated as text footnotes, and they will be assigned the auto incremented
21+
Note: numeric footnotes are treated as text footnotes, and they will be assigned the auto incremented
2222
number in order of appearance.
2323
2424
# Preview
2525
2626
Hover mouse over a footnote reference see its content in a tooltip.
27+
28+
# Footnotes List
29+
30+
A footnotes list is automatically appended at the end of the page when footnotes are present.
31+
Clicking the number in the list navigates back to the reference [^another-note].
32+
33+
[^another-note]: This is another footnote to demonstrate the list with multiple entries.
34+
35+
To hide the footnotes list, set `hideFootnoteList` to `true` in `meta.json`:
36+
37+
```json {title: "meta.json"}
38+
{
39+
"hideFootnoteList": true
40+
}
41+
```

znai-docs/znai/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"category": "Documentation",
55
"description": "Build functional, maintainable, beautiful User Guides with markdown and Znai plugins. Instant pages navigation. Local search. Multiple integrations to work with Python, Java, C++, OpenAPI, etc. Transform \"getting started\" sections into slideshow for your workshops. Manage multiple documentations with self-deployed znai hub.",
66
"hidePresentationTrigger": false,
7+
"hideFootnoteList": false,
78
"viewOn": {
89
"link": "https://github.com/testingisdocumenting/znai/blob/master/znai-docs/znai",
910
"title": "View Markdown"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Add: Footnote list at the end of the page in addition to footnote on hover

znai-reactjs/src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
--znai-footnote-preview-background-color: hsl(212, 95%, 94%);
132132
--znai-footnote-preview-label-background-color: hsl(212, 95%, 90%);
133133
--znai-footnote-preview-border-color: hsl(212, 95%, 77%);
134+
--znai-footnotes-list-border-color: hsl(0, 0%, 92%);
134135

135136
--znai-search-box-text-color: #777;
136137
--znai-search-box-background-color: #eee;

znai-reactjs/src/doc-elements/DefaultElementsLibrary.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import { ApiLinkedTextBlock } from "./api/ApiLinkedTextBlock";
8888
import { OpenApiMethodAndUrl } from "./open-api/OpenApiMethodAndUrl";
8989
import { Card } from "./card/Card";
9090
import { FootnoteReference } from "./footnote/FootnoteReference";
91+
import { FootnoteBackLinks } from "./footnote/FootnotesList";
9192
import { EmbeddedHtml } from "./html/EmbeddedHtml";
9293
import { Asciinema } from "./asciinema/Asciinema";
9394
import { ReadMore } from "./read-more/ReadMore.js";
@@ -180,6 +181,7 @@ library.AnnotatedImageWithOrderedList = AnnotatedImageWithOrderedList
180181
library.Card = Card;
181182

182183
library.FootnoteReference = FootnoteReference;
184+
library.FootnoteBackLinks = FootnoteBackLinks;
183185

184186
library.Tabs = Tabs
185187
presentationElementHandlers.Tabs = presentationTabsHandler

znai-reactjs/src/doc-elements/Documentation.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ import PresentationRegistry from "./presentation/PresentationRegistry";
3535

3636
import AllPagesAtOnce from "./AllPagesAtOnce";
3737

38-
import { mergeDocMeta } from "../structure/docMeta";
38+
import { mergeDocMeta, isFootnoteListHidden } from "../structure/docMeta";
3939

4040
import { pageContentProcessor } from "./pageContentProcessor.js";
41+
import { FootnotesList } from "./footnote/FootnotesList";
4142

4243
import { DocumentationModes } from "./DocumentationModes";
4344
import { pageTypesRegistry } from "./page/PageTypesRegistry";
@@ -203,6 +204,11 @@ export class Documentation extends React.Component {
203204
/>
204205
);
205206

207+
const footnotes = isFootnoteListHidden() ? [] : pageContentProcessor.extractFootnotes(page.content);
208+
const renderedFootnotesList = footnotes.length > 0 ? (
209+
<FootnotesList footnotes={footnotes} elementsLibrary={elementsLibrary} />
210+
) : null;
211+
206212
const NextPrevNavigation = pageTypesRegistry.nextPrevNavigationComponent(page.tocItem);
207213
const renderedNextPrevNavigation = (
208214
<NextPrevNavigation
@@ -255,6 +261,7 @@ export class Documentation extends React.Component {
255261
nextPageTocItem={this.nextPageTocItem}
256262
searchPopup={searchPopup}
257263
renderedPage={renderedPage}
264+
renderedFootnotesList={renderedFootnotesList}
258265
renderedNextPrevNavigation={renderedNextPrevNavigation}
259266
renderedFooter={renderedFooter}
260267
onHeaderClick={this.onHeaderClick}

znai-reactjs/src/doc-elements/footnote/Footnote.demo.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import React from "react";
1818

1919
import { elementsLibrary } from "../DefaultElementsLibrary";
2020
import { FootnoteReference } from "./FootnoteReference";
21+
import { FootnotesList } from "./FootnotesList";
2122
import { Registry } from "react-component-viewer";
2223
import { TooltipRenderer } from "../../components/Tooltip";
2324
import { FootnotePreview } from "./FootnotePreview";
@@ -54,6 +55,16 @@ export function footnoteDemo(registry: Registry) {
5455
</div>
5556
);
5657
});
58+
59+
registry.add("footnotes list", () => (
60+
<FootnotesList
61+
elementsLibrary={elementsLibrary}
62+
footnotes={[
63+
{ label: "1", content: footnoteContent(), refCount: 1 },
64+
{ label: "2", content: longerFootnoteContent(), refCount: 3 },
65+
]}
66+
/>
67+
));
5768
}
5869

5970
function footnoteContent() {

znai-reactjs/src/doc-elements/footnote/FootnoteReference.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@
2727
padding-left: 2px;
2828
padding-right: 2px;
2929
}
30+
31+
a:has(> .znai-footnote-reference) {
32+
text-decoration: none;
33+
color: inherit;
34+
}

znai-reactjs/src/doc-elements/footnote/FootnoteReference.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,40 @@ import React from "react";
1818
import { DocElementContent, ElementsLibraryMap } from "../default-elements/DocElement";
1919
import { Tooltip } from "../../components/Tooltip";
2020
import { FootnotePreview } from "./FootnotePreview";
21+
import { isFootnoteListHidden } from "../../structure/docMeta";
22+
import { footnoteRefAnchorId, footnoteEntryAnchorId } from "./footnoteAnchors";
2123

2224
import "./FootnoteReference.css";
2325

2426
interface Props {
2527
label: string;
2628
content: DocElementContent;
29+
occurrence: number;
2730
elementsLibrary: ElementsLibraryMap;
2831
}
2932

30-
export function FootnoteReference({ label, content, elementsLibrary }: Props) {
33+
export function FootnoteReference({ label, content, occurrence, elementsLibrary }: Props) {
3134
const tooltipContent = <FootnotePreview label={label} content={content} elementsLibrary={elementsLibrary} />;
3235

3336
const referenceClassName =
3437
"znai-footnote-reference " + (label.length === 1 ? "single-char-label" : "multi-char-label");
3538

39+
const hasFootnoteList = !isFootnoteListHidden();
40+
const anchorId = hasFootnoteList ? footnoteRefAnchorId(label, occurrence) : undefined;
41+
42+
const sup = (
43+
<sup id={anchorId} className={referenceClassName}>
44+
{label}
45+
</sup>
46+
);
47+
3648
return (
3749
<Tooltip
3850
content={tooltipContent}
3951
placement="parent-content-block"
4052
contentClassName="znai-footnote-preview-container"
4153
>
42-
<sup className={referenceClassName}>{label}</sup>
54+
{hasFootnoteList ? <a href={"#" + footnoteEntryAnchorId(label)}>{sup}</a> : sup}
4355
</Tooltip>
4456
);
4557
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2025 znai maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
.znai-footnotes-list {
18+
--znai-regular-text-size: var(--znai-smaller-text-size);
19+
--znai-code-text-size: var(--znai-small-meta-text-size);
20+
21+
border-top: 1px solid var(--znai-footnotes-list-border-color);
22+
font-size: var(--znai-regular-text-size);
23+
24+
margin: 56px calc(-1 * var(--znai-single-side-horizontal-min-spacing)) -56px;
25+
}
26+
27+
.znai-footnotes-list-content {
28+
padding: 64px 0 32px 32px;
29+
}
30+
31+
.znai-footnotes-list-entry {
32+
display: flex;
33+
align-items: first baseline;
34+
gap: 12px;
35+
margin-bottom: 4px;
36+
}
37+
38+
.znai-footnotes-list-entry:last-child {
39+
margin-bottom: 0;
40+
}
41+
42+
.znai-footnotes-list-entry-label {
43+
flex-shrink: 0;
44+
min-width: 20px;
45+
text-align: right;
46+
color: var(--znai-snippets-title-color);
47+
}
48+
49+
.znai-footnotes-list-entry-content {
50+
flex: 1;
51+
min-width: 0;
52+
}
53+
54+
.znai-footnote-back-links {
55+
margin-left: 4px;
56+
white-space: nowrap;
57+
}
58+
59+
.znai-footnote-back-link {
60+
text-decoration: none;
61+
color: var(--znai-snippets-title-color);
62+
margin-left: 2px;
63+
}
64+
65+
.znai-footnote-back-link:hover {
66+
color: var(--znai-brand-primary-color);
67+
}

0 commit comments

Comments
 (0)