Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: support customized sidebar item name from content #1825

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/helpers.md
Original file line number Diff line number Diff line change
@@ -127,6 +127,12 @@ Only when you set both the `routerMode: 'history'` and `externalLinkTarget: '_se
### Hello, world! :id=hello-world
```

## Customise the title for the sidebar items :sidebar="Customise title for sidebar"

```md
### How would I write a "hello, world" example? :sidebar="Hello, world?"
```

## Markdown in html tag

You need to insert a space between the html and markdown content.
8 changes: 3 additions & 5 deletions src/core/render/compiler.js
Original file line number Diff line number Diff line change
@@ -207,34 +207,32 @@ export class Compiler {
*/
origin.heading = renderer.heading = function (text, level) {
let { str, config } = getAndRemoveConfig(text);
const nextToc = { level, title: removeAtag(str) };
const nextToc = { level };

if (/<!-- {docsify-ignore} -->/g.test(str)) {
str = str.replace('<!-- {docsify-ignore} -->', '');
nextToc.title = removeAtag(str);
nextToc.ignoreSubHeading = true;
}

if (/{docsify-ignore}/g.test(str)) {
str = str.replace('{docsify-ignore}', '');
nextToc.title = removeAtag(str);
nextToc.ignoreSubHeading = true;
}

if (/<!-- {docsify-ignore-all} -->/g.test(str)) {
str = str.replace('<!-- {docsify-ignore-all} -->', '');
nextToc.title = removeAtag(str);
nextToc.ignoreAllSubs = true;
}

if (/{docsify-ignore-all}/g.test(str)) {
str = str.replace('{docsify-ignore-all}', '');
nextToc.title = removeAtag(str);
nextToc.ignoreAllSubs = true;
}

const slug = slugify(config.id || str);
const url = router.toURL(router.getCurrentPath(), { id: slug });
nextToc.title = removeAtag(str);
nextToc.text = config.sidebar || nextToc.title;
nextToc.slug = url;
_self.toc.push(nextToc);

8 changes: 3 additions & 5 deletions src/core/render/compiler/headline.js
Original file line number Diff line number Diff line change
@@ -4,34 +4,32 @@ import { slugify } from './slugify';
export const headingCompiler = ({ renderer, router, _self }) =>
(renderer.code = (text, level) => {
let { str, config } = getAndRemoveConfig(text);
const nextToc = { level, title: removeAtag(str) };
const nextToc = { level };

if (/<!-- {docsify-ignore} -->/g.test(str)) {
str = str.replace('<!-- {docsify-ignore} -->', '');
nextToc.title = removeAtag(str);
nextToc.ignoreSubHeading = true;
}

if (/{docsify-ignore}/g.test(str)) {
str = str.replace('{docsify-ignore}', '');
nextToc.title = removeAtag(str);
nextToc.ignoreSubHeading = true;
}

if (/<!-- {docsify-ignore-all} -->/g.test(str)) {
str = str.replace('<!-- {docsify-ignore-all} -->', '');
nextToc.title = removeAtag(str);
nextToc.ignoreAllSubs = true;
}

if (/{docsify-ignore-all}/g.test(str)) {
str = str.replace('{docsify-ignore-all}', '');
nextToc.title = removeAtag(str);
nextToc.ignoreAllSubs = true;
}

const slug = slugify(config.id || str);
const url = router.toURL(router.getCurrentPath(), { id: slug });
nextToc.title = removeAtag(str);
nextToc.text = config.sidebar || nextToc.title;
nextToc.slug = url;
_self.toc.push(nextToc);

2 changes: 1 addition & 1 deletion src/core/render/tpl.js
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
let innerHTML = '';
toc.forEach(node => {
const title = node.title.replace(/(<([^>]+)>)/g, '');
innerHTML += `<li><a class="section-link" href="${node.slug}" title="${title}">${node.title}</a></li>`;
innerHTML += `<li><a class="section-link" href="${node.slug}" title="${title}">${node.text}</a></li>`;
if (node.children) {
innerHTML += tree(node.children, tpl);
}
20 changes: 11 additions & 9 deletions src/core/render/utils.js
Original file line number Diff line number Diff line change
@@ -23,16 +23,18 @@ export function getAndRemoveConfig(str = '') {

if (str) {
str = str
.replace(/^('|")/, '')
.replace(/('|")$/, '')
.replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, (m, key, value) => {
if (key.indexOf(':') === -1) {
config[key] = (value && value.replace(/&quot;/g, '')) || true;
return '';
}
.replace(
/(?:^|\s):([\w-]+:?)=?([\w-%]+|&quot;((?!&quot;).)*&quot;|[“”][^“”]*[“”])?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-(
function (m, key, value) {
if (key.indexOf(':') === -1) {
config[key] = (value && value.replace(/&quot;|[“”]/g, '')) || true;
return '';
}

return m;
})
return m;
}
)
.replace(/^('|")|('|")$/g, '')
.trim();
}

24 changes: 23 additions & 1 deletion test/unit/render-util.test.js
Original file line number Diff line number Diff line change
@@ -58,6 +58,19 @@ describe('core/render/utils', () => {
str: `[filename](_media/example.md ":include")`,
});
});

test('parse config with quoted string arguments', () => {
const result = getAndRemoveConfig(
`[filename](_media/example.md ':include :foo=&quot;bar :baz test&quot;')`
);

expect(result).toMatchObject({
config: {
foo: 'bar :baz test',
},
str: `[filename](_media/example.md ':include')`,
});
});
});
});

@@ -68,22 +81,31 @@ describe('core/render/tpl', () => {
level: 2,
slug: '#/cover?id=basic-usage',
title: '<span style="color:red">Basic usage</span>',
text: '<span style="color:red">Basic usage</span>',
},
{
level: 2,
slug: '#/cover?id=custom-background',
title: 'Custom background',
text: 'Custom background',
},
{
level: 2,
slug: '#/cover?id=test',
title:
'<img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test',
text: '<img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test',
},
{
level: 2,
slug: '#/cover?id=different-title-and-label',
title: 'Long title string',
text: 'Short string',
},
]);

expect(result).toBe(
`<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li></ul>`
`<ul class="app-sub-sidebar"><li><a class="section-link" href="#/cover?id=basic-usage" title="Basic usage"><span style="color:red">Basic usage</span></a></li><li><a class="section-link" href="#/cover?id=custom-background" title="Custom background">Custom background</a></li><li><a class="section-link" href="#/cover?id=test" title="Test"><img src="/docs/_media/favicon.ico" data-origin="/_media/favicon.ico" alt="ico">Test</a></li><li><a class="section-link" href="#/cover?id=different-title-and-label" title="Long title string">Short string</a></li></ul>`
);
});
});