mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 11:19:51 +00:00
FEATURE: Add anchor links to headings (#12379)
This commit is contained in:
parent
e48d055232
commit
2ad9b3f432
@ -664,13 +664,13 @@ eviltrout</p>
|
|||||||
|
|
||||||
assert.cooked(
|
assert.cooked(
|
||||||
"># #category-hashtag\n",
|
"># #category-hashtag\n",
|
||||||
'<blockquote>\n<h1><span class="hashtag">#category-hashtag</span></h1>\n</blockquote>',
|
'<blockquote>\n<h1><a name="category-hashtag" class="anchor" href="#category-hashtag"></a><span class="hashtag">#category-hashtag</span></h1>\n</blockquote>',
|
||||||
"it handles category hashtags in simple quotes"
|
"it handles category hashtags in simple quotes"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.cooked(
|
assert.cooked(
|
||||||
"# #category-hashtag",
|
"# #category-hashtag",
|
||||||
'<h1><span class="hashtag">#category-hashtag</span></h1>',
|
'<h1><a name="category-hashtag" class="anchor" href="#category-hashtag"></a><span class="hashtag">#category-hashtag</span></h1>',
|
||||||
"it works within ATX-style headers"
|
"it works within ATX-style headers"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -696,7 +696,7 @@ eviltrout</p>
|
|||||||
test("Heading", function (assert) {
|
test("Heading", function (assert) {
|
||||||
assert.cooked(
|
assert.cooked(
|
||||||
"**Bold**\n----------",
|
"**Bold**\n----------",
|
||||||
"<h2><strong>Bold</strong></h2>",
|
'<h2><a name="bold" class="anchor" href="#bold"></a><strong>Bold</strong></h2>',
|
||||||
"It will bold the heading"
|
"It will bold the heading"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -939,7 +939,7 @@ eviltrout</p>
|
|||||||
|
|
||||||
assert.cooked(
|
assert.cooked(
|
||||||
"## a\nb\n```\nc\n```",
|
"## a\nb\n```\nc\n```",
|
||||||
'<h2>a</h2>\n<p>b</p>\n<pre><code class="lang-auto">c\n</code></pre>',
|
'<h2><a name="a" class="anchor" href="#a"></a>a</h2>\n<p>b</p>\n<pre><code class="lang-auto">c\n</code></pre>',
|
||||||
"it handles headings with code blocks after them."
|
"it handles headings with code blocks after them."
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -130,6 +130,7 @@ export default class AllowLister {
|
|||||||
// Only add to `default` when you always want your allowlist to occur. In other words,
|
// Only add to `default` when you always want your allowlist to occur. In other words,
|
||||||
// don't change this for a plugin or a feature that can be disabled
|
// don't change this for a plugin or a feature that can be disabled
|
||||||
export const DEFAULT_LIST = [
|
export const DEFAULT_LIST = [
|
||||||
|
"a.anchor",
|
||||||
"a.attachment",
|
"a.attachment",
|
||||||
"a.hashtag",
|
"a.hashtag",
|
||||||
"a.mention",
|
"a.mention",
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
export function setup(helper) {
|
||||||
|
helper.registerPlugin((md) => {
|
||||||
|
md.core.ruler.push("anchor", (state) => {
|
||||||
|
for (let idx = 0; idx < state.tokens.length; idx++) {
|
||||||
|
if (state.tokens[idx].type !== "heading_open") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkOpen = new state.Token("link_open", "a", 1);
|
||||||
|
const linkClose = new state.Token("link_close", "a", -1);
|
||||||
|
|
||||||
|
const slug = state.tokens[idx + 1].content
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, "-")
|
||||||
|
.replace(/[^\w\-]+/g, "")
|
||||||
|
.replace(/\-\-+/g, "-")
|
||||||
|
.replace(/^-+/, "")
|
||||||
|
.replace(/-+$/, "");
|
||||||
|
|
||||||
|
linkOpen.attrSet("name", slug);
|
||||||
|
linkOpen.attrSet("class", "anchor");
|
||||||
|
linkOpen.attrSet("href", "#" + slug);
|
||||||
|
|
||||||
|
state.tokens[idx + 1].children.unshift(linkClose);
|
||||||
|
state.tokens[idx + 1].children.unshift(linkOpen);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -94,6 +94,20 @@ $quote-share-maxwidth: 150px;
|
|||||||
h6 {
|
h6 {
|
||||||
margin: 30px 0 10px;
|
margin: 30px 0 10px;
|
||||||
line-height: $line-height-medium;
|
line-height: $line-height-medium;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
a.anchor {
|
||||||
|
&:before {
|
||||||
|
content: svg-uri(
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 512 512"><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"></path></svg>'
|
||||||
|
);
|
||||||
|
float: left;
|
||||||
|
margin-left: -20px;
|
||||||
|
padding-right: 4px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -189,8 +189,8 @@ describe PrettyText do
|
|||||||
</div>
|
</div>
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
expect(cooked).to include("<h1>Pre-heading</h1>")
|
expect(cooked).to include("<h1>\n<a name=\"pre-heading\" class=\"anchor\" href=\"#pre-heading\"></a>Pre-heading</h1>")
|
||||||
expect(cooked).to include("<h1>Post-heading</h1>")
|
expect(cooked).to include("<h1>\n<a name=\"post-heading\" class=\"anchor\" href=\"#post-heading\"></a>Post-heading</h1>")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not break when there are headings before/after a poll without a title" do
|
it "does not break when there are headings before/after a poll without a title" do
|
||||||
@ -211,7 +211,7 @@ describe PrettyText do
|
|||||||
<div class="poll" data-poll-status="open" data-poll-name="poll">
|
<div class="poll" data-poll-status="open" data-poll-name="poll">
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
expect(cooked).to include("<h1>Pre-heading</h1>")
|
expect(cooked).to include("<h1>\n<a name=\"pre-heading\" class=\"anchor\" href=\"#pre-heading\"></a>Pre-heading</h1>")
|
||||||
expect(cooked).to include("<h1>Post-heading</h1>")
|
expect(cooked).to include("<h1>\n<a name=\"post-heading\" class=\"anchor\" href=\"#post-heading\"></a>Post-heading</h1>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1903,4 +1903,17 @@ HTML
|
|||||||
expect(cooked).to eq(html.strip)
|
expect(cooked).to eq(html.strip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "adds anchor links to headings" do
|
||||||
|
cooked = PrettyText.cook('# Hello world')
|
||||||
|
|
||||||
|
html = <<~HTML
|
||||||
|
<h1>
|
||||||
|
<a name="hello-world" class="anchor" href="#hello-world"></a>
|
||||||
|
Hello world
|
||||||
|
</h1>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
expect(cooked).to match_html(html)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,6 +33,7 @@ describe "CommonMark" do
|
|||||||
cooked.strip!
|
cooked.strip!
|
||||||
cooked.gsub!(" class=\"lang-auto\"", '')
|
cooked.gsub!(" class=\"lang-auto\"", '')
|
||||||
cooked.gsub!(/<span class="hashtag">(.*)<\/span>/, "\\1")
|
cooked.gsub!(/<span class="hashtag">(.*)<\/span>/, "\\1")
|
||||||
|
cooked.gsub!(/<a name="(.*)" class="anchor" href="#\1*"><\/a>/, "")
|
||||||
# we don't care about this
|
# we don't care about this
|
||||||
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
||||||
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user