diff --git a/gatsby-config.js b/gatsby-config.js
index 82b9edd..ee55a7a 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -31,7 +31,7 @@ module.exports = {
{
resolve: `gatsby-transformer-remark`,
options: {
- plugins: [`gatsby-remark-auto-headers`],
+ plugins: [`gatsby-remark-auto-headers-improved`],
},
},
{
diff --git a/package-lock.json b/package-lock.json
index 40682b9..e98d8ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10015,34 +10015,6 @@
}
}
},
- "gatsby-remark-auto-headers": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/gatsby-remark-auto-headers/-/gatsby-remark-auto-headers-1.0.3.tgz",
- "integrity": "sha512-bLcZD53sXU3jklGlbtT4MEN+a8w+WocSxe+sqRFkoHWOJeB1ClyqM0IH7wHUV0M/BFyQ54i+ycwjrUa17op1bg==",
- "requires": {
- "@babel/runtime": "^7.0.0",
- "github-slugger": "^1.2.0",
- "unist-util-visit": "^1.4.0"
- },
- "dependencies": {
- "unist-util-visit": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz",
- "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==",
- "requires": {
- "unist-util-visit-parents": "^2.0.0"
- }
- },
- "unist-util-visit-parents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz",
- "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==",
- "requires": {
- "unist-util-is": "^3.0.0"
- }
- }
- }
- },
"gatsby-source-filesystem": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-2.3.5.tgz",
diff --git a/package.json b/package.json
index b0c5093..7b44390 100644
--- a/package.json
+++ b/package.json
@@ -40,10 +40,10 @@
"gatsby-plugin-react-svg": "^3.0.0",
"gatsby-plugin-sharp": "^2.6.6",
"gatsby-plugin-styled-components": "^3.3.1",
- "gatsby-remark-auto-headers": "^1.0.3",
"gatsby-source-filesystem": "^2.3.5",
"gatsby-transformer-remark": "^2.8.10",
"gatsby-transformer-sharp": "^2.5.2",
+ "github-slugger": "^1.3.0",
"lodash": "^4.17.15",
"polished": "^3.6.3",
"prismjs": "^1.20.0",
diff --git a/plugins/gatsby-remark-auto-headers-improved/gatsby-browser.js b/plugins/gatsby-remark-auto-headers-improved/gatsby-browser.js
new file mode 100644
index 0000000..50e9872
--- /dev/null
+++ b/plugins/gatsby-remark-auto-headers-improved/gatsby-browser.js
@@ -0,0 +1,43 @@
+var attr = "id"
+const getTargetOffset = (hash) => {
+ const id = window.decodeURI(hash.replace(`#`, ``))
+ if (id !== ``) {
+ const element = document.querySelector("[" + attr + "=" + id + "]")
+ if (element) {
+ return getElementTop(element)
+ }
+ }
+ return null
+}
+
+function getElementTop(el) {
+ let actualTop = el.offsetTop
+
+ let current = el.offsetParent
+
+ while (current !== null) {
+ actualTop += current.offsetTop
+
+ current = current.offsetParent
+ }
+
+ return actualTop
+}
+
+exports.onInitialClientRender = (_, pluginOptions) => {
+ if (pluginOptions.offsetY) {
+ attr = pluginOptions.attr
+ }
+
+ requestAnimationFrame(() => {
+ const offset = getTargetOffset(window.location.hash)
+ if (offset !== null) {
+ window.scrollTo(0, offset)
+ }
+ })
+}
+
+exports.shouldUpdateScroll = ({ routerProps: { location } }) => {
+ const offset = getTargetOffset(location.hash)
+ return offset !== null ? [0, offset] : true
+}
diff --git a/plugins/gatsby-remark-auto-headers-improved/gatsby-ssr.js b/plugins/gatsby-remark-auto-headers-improved/gatsby-ssr.js
new file mode 100644
index 0000000..5404349
--- /dev/null
+++ b/plugins/gatsby-remark-auto-headers-improved/gatsby-ssr.js
@@ -0,0 +1,85 @@
+const React = require("react")
+
+const pluginDefaults = {
+ className: `anchor`,
+ icon: true,
+ offsetY: 0,
+ attr: "id",
+}
+
+exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => {
+ const { className, icon, attr } = Object.assign(pluginDefaults, pluginOptions)
+ const styles = `
+ .${className} {
+ float: left;
+ padding-right: 4px;
+ margin-left: -20px;
+ }
+ h1 .${className} svg,
+ h2 .${className} svg,
+ h3 .${className} svg,
+ h4 .${className} svg,
+ h5 .${className} svg,
+ h6 .${className} svg {
+ visibility: hidden;
+ }
+ h1:hover .${className} svg,
+ h2:hover .${className} svg,
+ h3:hover .${className} svg,
+ h4:hover .${className} svg,
+ h5:hover .${className} svg,
+ h6:hover .${className} svg,
+ h1 .${className}:focus svg,
+ h2 .${className}:focus svg,
+ h3 .${className}:focus svg,
+ h4 .${className}:focus svg,
+ h5 .${className}:focus svg,
+ h6 .${className}:focus svg {
+ visibility: visible;
+ }
+ `
+
+ const script = `
+ function getElementTop(el) {
+ var actualTop = el.offsetTop
+
+ var current = el.offsetParent
+
+ while (current !== null) {
+ actualTop += current.offsetTop
+
+ current = current.offsetParent
+ }
+
+ return actualTop
+ }
+
+ document.addEventListener("DOMContentLoaded", function(event) {
+ var hash = window.decodeURI(location.hash.replace('#', ''))
+ if (hash !== '') {
+ var element = document.querySelector('[${attr}=' + hash + ']')
+ if (element) {
+ var offset = getElementTop(element)
+ // Wait for the browser to finish rendering before scrolling.
+ setTimeout((function() {
+ window.scrollTo(0, offset)
+ }), 0)
+ }
+ }
+ })
+ `
+
+ const style = icon ? (
+
+ ) : undefined
+
+ return setHeadComponents([
+ style,
+ ,
+ ])
+}
diff --git a/plugins/gatsby-remark-auto-headers-improved/index.js b/plugins/gatsby-remark-auto-headers-improved/index.js
new file mode 100644
index 0000000..e911ea6
--- /dev/null
+++ b/plugins/gatsby-remark-auto-headers-improved/index.js
@@ -0,0 +1,69 @@
+const visit = require(`unist-util-visit`)
+const slugs = require(`github-slugger`)()
+
+function toString(node) {
+ return (
+ valueOf(node) ||
+ (node.children && node.children.map(toString).join("")) ||
+ ""
+ )
+}
+function valueOf(node) {
+ return (
+ (node && node.value ? node.value : node.alt ? node.alt : node.title) || ""
+ )
+}
+
+function patch(context, key, value) {
+ if (!context[key]) {
+ context[key] = value
+ }
+ return context[key]
+}
+
+function stripLeadingNumber(value) {
+ return value.replace(/^(\d*\-)(.*)/, "$2")
+}
+
+const svgIcon = ``
+
+module.exports = (
+ { markdownAST },
+ { icon = svgIcon, attr = "id", className = "anchor", maintainCase = false }
+) => {
+ slugs.reset()
+ visit(markdownAST, `heading`, (node) => {
+ const id = slugs.slug(toString(node), maintainCase)
+ const strippedId = stripLeadingNumber(id)
+ const data = patch(node, `data`, {})
+
+ patch(data, attr, strippedId)
+ patch(data, `htmlAttributes`, {})
+ patch(data, `hProperties`, {})
+ patch(data.htmlAttributes, attr, strippedId)
+ patch(data.hProperties, attr, strippedId)
+
+ if (icon !== false) {
+ node.children.unshift({
+ type: `link`,
+ url: `#${strippedId}`,
+ title: null,
+ data: {
+ hProperties: {
+ "aria-hidden": true,
+ class: className,
+ },
+ hChildren: [
+ {
+ type: `raw`,
+ // The Octicon link icon is the default. But users can set their own icon via the "icon" option.
+ value: icon,
+ },
+ ],
+ },
+ })
+ }
+ })
+
+ return markdownAST
+}
diff --git a/plugins/gatsby-remark-auto-headers-improved/package-lock.json b/plugins/gatsby-remark-auto-headers-improved/package-lock.json
new file mode 100644
index 0000000..6c7d0bd
--- /dev/null
+++ b/plugins/gatsby-remark-auto-headers-improved/package-lock.json
@@ -0,0 +1,50 @@
+{
+ "name": "gatsby-remark-auto-headers-improved",
+ "version": "0.1.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/unist": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
+ "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ=="
+ },
+ "emoji-regex": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz",
+ "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4="
+ },
+ "github-slugger": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz",
+ "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==",
+ "requires": {
+ "emoji-regex": ">=6.0.0 <=6.1.1"
+ }
+ },
+ "unist-util-is": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
+ "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ=="
+ },
+ "unist-util-visit": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz",
+ "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==",
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0",
+ "unist-util-visit-parents": "^3.0.0"
+ }
+ },
+ "unist-util-visit-parents": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz",
+ "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==",
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^4.0.0"
+ }
+ }
+ }
+}
diff --git a/plugins/gatsby-remark-auto-headers-improved/package.json b/plugins/gatsby-remark-auto-headers-improved/package.json
new file mode 100644
index 0000000..9a0eb06
--- /dev/null
+++ b/plugins/gatsby-remark-auto-headers-improved/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "gatsby-remark-auto-headers-improved",
+ "version": "0.1.0",
+ "description": "Gatsby remark plugin to add headers to markdown, improved from gatsby-remark-auto-headers",
+ "main": "index.js",
+ "scripts": {},
+ "keywords": [],
+ "author": "Jean-Philippe Sirois ",
+ "license": "MIT",
+ "dependencies": {
+ "github-slugger": "^1.3.0",
+ "unist-util-visit": "^2.0.2"
+ }
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index a61d301..5db36f4 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,4 +1,4 @@
-import kebabCase from "lodash/kebabCase"
+import GithubSlugger from "github-slugger"
import chain from "ramda/es/chain"
import partition from "ramda/es/partition"
import pipe from "ramda/es/pipe"
@@ -7,6 +7,8 @@ import sort from "ramda/es/sort"
import { IFile, IFileOrFolder, IFolder, ITocItem } from "../types"
import { MarkdownRemark } from "../../generated/graphql"
+const slugger = new GithubSlugger()
+
function specificWordsToUpper(str: string): string {
const wordsToUpper = ["pdo", "c"]
const toUpper = (word: string) =>
@@ -144,12 +146,18 @@ export function buildToc(headings: MarkdownRemark["headings"]): ITocItem[] {
return []
}
+ const strip = (value: string) => value.replace(/^(\d*\-)(.*)/, "$2")
+
+ slugger.reset()
+
return headings
.filter((h) => h?.depth === 2)
.map((h) => {
+ const slug = slugger.slug(h!.value!)
+
return {
depth: h!.depth!,
- link: `#${kebabCase(h!.value!)}`,
+ link: `#${strip(slug)}`,
title: h!.value!,
}
})