diff --git a/package-lock.json b/package-lock.json
index a624e63..18d54f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,12 +14,12 @@
"@fortawesome/react-fontawesome": "^0.1.14",
"@graphql-tools/merge": "^8.2.1",
"@graphql-tools/schema": "^8.2.0",
+ "@graphql-tools/utils": "^8.6.5",
"@next/bundle-analyzer": "^12.0.3",
"@reach/listbox": "^0.16.2",
"@reach/menu-button": "^0.16.2",
"common-tags": "^1.8.0",
"framer-motion": "^6.2.1",
- "github-slugger": "^1.4.0",
"graphql": "^15.6.1",
"graphql-tag": "^2.12.6",
"knex": "^0.95.11",
@@ -474,9 +474,9 @@
}
},
"node_modules/@graphql-tools/utils": {
- "version": "8.5.3",
- "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.5.3.tgz",
- "integrity": "sha512-HDNGWFVa8QQkoQB0H1lftvaO1X5xUaUDk1zr1qDe0xN1NL0E/CrQdJ5UKLqOvH4hkqVUPxQsyOoAZFkaH6rLHg==",
+ "version": "8.6.5",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.5.tgz",
+ "integrity": "sha512-mjOtaWiS2WIqRz/cq5gaeM3sVrllcu2xbtHROw1su1v3xWa3D3dKgn8Lrl7+tvWs5WUVySsBss/VZ3WdoPkCrA==",
"dependencies": {
"tslib": "~2.3.0"
},
@@ -2332,11 +2332,6 @@
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz",
"integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA=="
},
- "node_modules/github-slugger": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz",
- "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ=="
- },
"node_modules/glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
@@ -5116,9 +5111,9 @@
}
},
"@graphql-tools/utils": {
- "version": "8.5.3",
- "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.5.3.tgz",
- "integrity": "sha512-HDNGWFVa8QQkoQB0H1lftvaO1X5xUaUDk1zr1qDe0xN1NL0E/CrQdJ5UKLqOvH4hkqVUPxQsyOoAZFkaH6rLHg==",
+ "version": "8.6.5",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.5.tgz",
+ "integrity": "sha512-mjOtaWiS2WIqRz/cq5gaeM3sVrllcu2xbtHROw1su1v3xWa3D3dKgn8Lrl7+tvWs5WUVySsBss/VZ3WdoPkCrA==",
"requires": {
"tslib": "~2.3.0"
}
@@ -6469,11 +6464,6 @@
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz",
"integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA=="
},
- "github-slugger": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz",
- "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ=="
- },
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
diff --git a/package.json b/package.json
index 0cbbf96..228389f 100644
--- a/package.json
+++ b/package.json
@@ -15,12 +15,12 @@
"@fortawesome/react-fontawesome": "^0.1.14",
"@graphql-tools/merge": "^8.2.1",
"@graphql-tools/schema": "^8.2.0",
+ "@graphql-tools/utils": "^8.6.5",
"@next/bundle-analyzer": "^12.0.3",
"@reach/listbox": "^0.16.2",
"@reach/menu-button": "^0.16.2",
"common-tags": "^1.8.0",
"framer-motion": "^6.2.1",
- "github-slugger": "^1.4.0",
"graphql": "^15.6.1",
"graphql-tag": "^2.12.6",
"knex": "^0.95.11",
diff --git a/src/components/footer/Footer.js b/src/components/footer/Footer.js
index 33b837d..e033957 100644
--- a/src/components/footer/Footer.js
+++ b/src/components/footer/Footer.js
@@ -1,3 +1,4 @@
+import Link from "next/link";
import { Column, Row, Solid } from "components/box";
import { Container } from "components/container";
import { Text } from "components/text";
@@ -6,6 +7,7 @@ import { faDiscord, faGithub, faReddit, faTwitter } from "@fortawesome/free-bran
import { Button } from "components/button";
import styled from "styled-components";
import theme from "theme";
+import { forwardRef } from "react";
const StyledFooter = styled(Solid)`
margin-top: auto;
@@ -46,12 +48,16 @@ export function Footer() {
Transparency
-
- Donate
-
-
- FAQ
-
+
+
+ Donate
+
+
+
+
+ FAQ
+
+
@@ -91,18 +97,18 @@ export function Footer() {
);
}
-function FooterLink({ children, ...props }) {
+const FooterLink = forwardRef(function FooterLink({ children, ...props }, ref) {
return (
-
+
{children}
);
-}
+});
-function FooterTextLink({ children, ...props }) {
+const FooterTextLink = forwardRef(function FooterTextLink({ children, ...props }, ref) {
return (
-
+
{children}
);
-}
+});
diff --git a/src/pages/page/[...pageSlug].js b/src/pages/page/[...pageSlug].js
index 6d80a88..13d075c 100644
--- a/src/pages/page/[...pageSlug].js
+++ b/src/pages/page/[...pageSlug].js
@@ -7,7 +7,6 @@ import { Text } from "components/text";
import { createElement, Fragment, useEffect, useState } from "react";
import rehypeParse from "rehype-parse";
import styled from "styled-components";
-import { slug } from "github-slugger";
import { motion } from "framer-motion";
import theme from "theme";
import { SEO } from "components/seo";
@@ -99,6 +98,7 @@ const StyledMarkdown = styled.div`
}
& pre {
+ margin-bottom: 16px;
overflow-x: auto;
}
@@ -177,7 +177,7 @@ export default function DocumentPage({ page }) {
}
function TableOfContents({ headings }) {
- const [currentHeading, setCurrentHeading] = useState();
+ const [currentSlug, setCurrentSlug] = useState();
useEffect(() => {
function onScroll() {
@@ -193,7 +193,7 @@ function TableOfContents({ headings }) {
break;
}
}
- setCurrentHeading(currentHeading?.id);
+ setCurrentSlug(currentHeading?.id);
}
window.addEventListener("scroll", onScroll);
@@ -203,12 +203,12 @@ function TableOfContents({ headings }) {
return (
- {headings.map(({ text }) => (
+ {headings.map(({ text, slug }) => (
- {slug(text) === currentHeading && (
+ {slug === currentSlug && (
)}
- {text}
+ {text}
))}
diff --git a/src/utils/markdownToHtml.js b/src/utils/markdownToHtml.js
index bf88d1a..6e5d95e 100644
--- a/src/utils/markdownToHtml.js
+++ b/src/utils/markdownToHtml.js
@@ -6,16 +6,32 @@ import "prismjs/components/prism-json";
export default function markdownToHtml(markdown) {
const headings = [];
- const html = marked.parse(markdown, {
+ const slugger = new marked.Slugger();
+
+ const html = marked(markdown, {
highlight(code, lang) {
if (lang) {
- return Prism.highlight(code, Prism.languages[lang], lang);
+ if (lang in Prism.languages) {
+ return Prism.highlight(code, Prism.languages[lang], lang);
+ }
+ return `[!] Unknown syntax highlighting language: ${lang}\n${code}`;
}
return code;
},
walkTokens(token) {
- if (token.type === "heading" && token.depth === 2) {
- headings.push({ text: token.text, level: token.depth });
+ if (token.type === "heading") {
+ // To remove links and other markdown entities from the text we join only the text parts together.
+ const text = joinTextDeep(token.tokens);
+ // Slugger keeps track of seen slugs, so we have to generate a slug for every heading.
+ // This way links will work in the table of contents later.
+ const slug = slugger.slug(text);
+ if (token.depth === 2) {
+ headings.push({
+ text: text,
+ level: token.depth,
+ slug
+ });
+ }
}
}
});
@@ -25,3 +41,7 @@ export default function markdownToHtml(markdown) {
headings
};
}
+
+function joinTextDeep(tokens) {
+ return tokens.map((token) => token.tokens ? joinTextDeep(token.tokens) : token.text).join("");
+}