Skip to content

Commit 98c1d22

Browse files
authored
Merge pull request #1636 from smikitky/remark-autolink-custom-id
Support Custom-ID Markdown Syntax
2 parents 8eaa701 + 16515ce commit 98c1d22

File tree

7 files changed

+190
-13
lines changed

7 files changed

+190
-13
lines changed

gatsby-config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ module.exports = {
5555
maxWidth: 840,
5656
},
5757
},
58-
'gatsby-remark-autolink-headers',
58+
'gatsby-remark-header-custom-ids',
5959
{
6060
resolve: 'gatsby-remark-code-repls',
6161
options: {

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"gatsby-plugin-react-helmet": "^3.0.0",
3030
"gatsby-plugin-sharp": "^2.0.0",
3131
"gatsby-plugin-twitter": "^2.0.0",
32-
"gatsby-remark-autolink-headers": "^2.0.12",
3332
"gatsby-remark-code-repls": "^2.0.0",
3433
"gatsby-remark-copy-linked-files": "^2.0.0",
3534
"gatsby-remark-embed-snippet": "^3.0.0",
@@ -40,8 +39,10 @@
4039
"gatsby-source-filesystem": "^2.0.0",
4140
"gatsby-transformer-remark": "^2.0.0",
4241
"gatsby-transformer-sharp": "^2.0.0",
42+
"github-slugger": "^1.2.1",
4343
"glamor": "^2.20.40",
4444
"hex2rgba": "^0.0.1",
45+
"mdast-util-to-string": "^1.0.5",
4546
"normalize.css": "^8.0.0",
4647
"prettier": "^1.7.4",
4748
"prismjs": "^1.15.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
let offsetY = 0;
2+
3+
const getTargetOffset = hash => {
4+
const id = window.decodeURI(hash.replace(`#`, ``));
5+
if (id !== ``) {
6+
const element = document.getElementById(id);
7+
if (element) {
8+
return element.offsetTop - offsetY;
9+
}
10+
}
11+
return null;
12+
};
13+
14+
exports.onInitialClientRender = (_, pluginOptions) => {
15+
if (pluginOptions.offsetY) {
16+
offsetY = pluginOptions.offsetY;
17+
}
18+
19+
requestAnimationFrame(() => {
20+
const offset = getTargetOffset(window.location.hash);
21+
if (offset !== null) {
22+
window.scrollTo(0, offset);
23+
}
24+
});
25+
};
26+
27+
exports.shouldUpdateScroll = ({routerProps: {location}}) => {
28+
const offset = getTargetOffset(location.hash);
29+
return offset !== null ? [0, offset] : true;
30+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const React = require(`react`);
2+
3+
const pluginDefaults = {
4+
className: `anchor`,
5+
icon: true,
6+
offsetY: 0,
7+
};
8+
9+
exports.onRenderBody = ({setHeadComponents}, pluginOptions) => {
10+
const {className, icon, offsetY} = Object.assign(
11+
pluginDefaults,
12+
pluginOptions,
13+
);
14+
15+
const styles = `
16+
.${className} {
17+
float: left;
18+
padding-right: 4px;
19+
margin-left: -20px;
20+
}
21+
h1 .${className} svg,
22+
h2 .${className} svg,
23+
h3 .${className} svg,
24+
h4 .${className} svg,
25+
h5 .${className} svg,
26+
h6 .${className} svg {
27+
visibility: hidden;
28+
}
29+
h1:hover .${className} svg,
30+
h2:hover .${className} svg,
31+
h3:hover .${className} svg,
32+
h4:hover .${className} svg,
33+
h5:hover .${className} svg,
34+
h6:hover .${className} svg,
35+
h1 .${className}:focus svg,
36+
h2 .${className}:focus svg,
37+
h3 .${className}:focus svg,
38+
h4 .${className}:focus svg,
39+
h5 .${className}:focus svg,
40+
h6 .${className}:focus svg {
41+
visibility: visible;
42+
}
43+
`;
44+
45+
const script = `
46+
document.addEventListener("DOMContentLoaded", function(event) {
47+
var hash = window.decodeURI(location.hash.replace('#', ''))
48+
if (hash !== '') {
49+
var element = document.getElementById(hash)
50+
if (element) {
51+
var offset = element.offsetTop
52+
// Wait for the browser to finish rendering before scrolling.
53+
setTimeout((function() {
54+
window.scrollTo(0, offset - ${offsetY})
55+
}), 0)
56+
}
57+
}
58+
})
59+
`;
60+
61+
const style = icon ? (
62+
<style key="gatsby-remark-header-custom-ids-style" type="text/css">
63+
{styles}
64+
</style>
65+
) : (
66+
undefined
67+
);
68+
69+
return setHeadComponents([
70+
style,
71+
<script
72+
key="gatsby-remark-header-custom-ids-script"
73+
dangerouslySetInnerHTML={{__html: script}}
74+
/>,
75+
]);
76+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*!
2+
* Based on 'gatsby-remark-autolink-headers'
3+
* Original Author: Kyle Mathews <[email protected]>
4+
* Copyright (c) 2015 Gatsbyjs
5+
*/
6+
7+
const toString = require('mdast-util-to-string');
8+
const visit = require('unist-util-visit');
9+
const slugs = require('github-slugger')();
10+
11+
function patch(context, key, value) {
12+
if (!context[key]) {
13+
context[key] = value;
14+
}
15+
16+
return context[key];
17+
}
18+
19+
const svgIcon = `<svg aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>`;
20+
21+
module.exports = (
22+
{markdownAST},
23+
{icon = svgIcon, className = `anchor`, maintainCase = false},
24+
) => {
25+
slugs.reset();
26+
27+
visit(markdownAST, 'heading', node => {
28+
// Support custom-id syntax.
29+
const rawHeader = toString(node);
30+
const match = /^.+(\s*\{#([a-z0-9\-_]+?)\}\s*)$/.exec(rawHeader);
31+
const id = match ? match[2] : slugs.slug(rawHeader, maintainCase);
32+
if (match) {
33+
// Remove the custom ID part from the text node.
34+
const lastNode = node.children[node.children.length - 1];
35+
lastNode.value = lastNode.value.replace(match[1], '');
36+
}
37+
38+
const data = patch(node, 'data', {});
39+
40+
patch(data, 'id', id);
41+
patch(data, 'htmlAttributes', {});
42+
patch(data, 'hProperties', {});
43+
patch(data.htmlAttributes, 'id', id);
44+
patch(data.hProperties, 'id', id);
45+
46+
if (icon !== false) {
47+
node.children.unshift({
48+
type: 'link',
49+
url: `#${id}`,
50+
title: null,
51+
data: {
52+
hProperties: {
53+
'aria-hidden': true,
54+
class: className,
55+
},
56+
hChildren: [
57+
{
58+
type: 'raw',
59+
// The Octicon link icon is the default. But users can set their own icon via the "icon" option.
60+
value: icon,
61+
},
62+
],
63+
},
64+
});
65+
}
66+
});
67+
68+
return markdownAST;
69+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "gatsby-remark-header-custom-ids",
3+
"version": "0.0.1"
4+
}

yarn.lock

+8-11
Original file line numberDiff line numberDiff line change
@@ -5463,16 +5463,6 @@ gatsby-react-router-scroll@^2.0.0:
54635463
scroll-behavior "^0.9.9"
54645464
warning "^3.0.0"
54655465

5466-
gatsby-remark-autolink-headers@^2.0.12:
5467-
version "2.0.12"
5468-
resolved "https://registry.yarnpkg.com/gatsby-remark-autolink-headers/-/gatsby-remark-autolink-headers-2.0.12.tgz#96830bd7496ace7d1b7e07444aa09ecbe2ac59d2"
5469-
integrity sha512-TI7dpdHOUohIWCW7i8ta4zZAdWG5C2ZmrHMC9flWGWQ62oq6Q2eW+I1I/v//UZ+vJ1dRaSX2lmsVFmt87rLVHQ==
5470-
dependencies:
5471-
"@babel/runtime" "^7.0.0"
5472-
github-slugger "^1.1.1"
5473-
mdast-util-to-string "^1.0.2"
5474-
unist-util-visit "^1.3.0"
5475-
54765466
gatsby-remark-code-repls@^2.0.0:
54775467
version "2.0.0"
54785468
resolved "https://registry.yarnpkg.com/gatsby-remark-code-repls/-/gatsby-remark-code-repls-2.0.0.tgz#b3290987046f1d325e6e93dbe9295f3cbb3df34a"
@@ -5814,6 +5804,13 @@ github-slugger@^1.1.1:
58145804
dependencies:
58155805
emoji-regex ">=6.0.0 <=6.1.1"
58165806

5807+
github-slugger@^1.2.1:
5808+
version "1.2.1"
5809+
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508"
5810+
integrity sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ==
5811+
dependencies:
5812+
emoji-regex ">=6.0.0 <=6.1.1"
5813+
58175814
glamor@^2.20.40:
58185815
version "2.20.40"
58195816
resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.40.tgz#f606660357b7cf18dface731ad1a2cfa93817f05"
@@ -8184,7 +8181,7 @@ mdast-util-to-nlcst@^3.2.0:
81848181
unist-util-position "^3.0.0"
81858182
vfile-location "^2.0.0"
81868183

8187-
mdast-util-to-string@^1.0.2:
8184+
mdast-util-to-string@^1.0.2, mdast-util-to-string@^1.0.5:
81888185
version "1.0.5"
81898186
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.0.5.tgz#3552b05428af22ceda34f156afe62ec8e6d731ca"
81908187
integrity sha512-2qLt/DEOo5F6nc2VFScQiHPzQ0XXcabquRJxKMhKte8nt42o08HUxNDPk7tt0YPxnWjAT11I1SYi0X0iPnfI5A==

0 commit comments

Comments
 (0)