Skip to content

Commit df2a49a

Browse files
committed
improved sidebar nav
1 parent c11e84c commit df2a49a

File tree

4 files changed

+77
-37
lines changed

4 files changed

+77
-37
lines changed

src/partials/page.hbs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,26 @@
1616
<aside id="page-sidebar" class="sidebar pe-lg-2 offcanvas-lg offcanvas-start" tabindex="-1"
1717
aria-labelledby="sidebar-offcanvas-label">
1818
<div class="offcanvas-header">
19-
<h5 class="offcanvas-title" id="sidebar-offcanvas-label">Pages</h5>
19+
<h5 class="offcanvas-title" id="sidebar-offcanvas-label">{{sidebar}}</h5>
2020
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#page-sidebar"
2121
aria-label="Close"></button>
2222
</div>
2323
<hr class="my-0 d-lg-none">
2424
<div class="sidebar-content offcanvas-body">
25-
{{{sidebar}}}
25+
{{#block 'sidebar'}}
26+
<p class="text-danger">
27+
Please declare sidebar partial before <code>\{{#>page}}</code> partial usage.
28+
Example: <br>
29+
</p>
30+
<!-- @formatter:off -->
31+
<pre><code>\{{#partial 'sidebar'}}
32+
Sidebar Content
33+
\{{/partial}}
34+
\{{#>page sidebar='Sidebar'}}
35+
Page Content
36+
\{{/page}}</code></pre>
37+
<!-- @formatter:on -->
38+
{{/block}}
2639
</div>
2740
</aside>
2841
{{/if}}
@@ -31,7 +44,7 @@
3144
<div class="page-sidebar-toggle d-lg-none">
3245
<button class="btn btn-sm btn-outline-secondary mb-2" type="button" data-bs-toggle="offcanvas"
3346
data-bs-target="#page-sidebar">
34-
<i class="bi bi-list"></i> Pages
47+
<i class="bi bi-list"></i> {{sidebar}}
3548
</button>
3649
</div>
3750
{{/if}}

src/views/develop/guides/_sidebar.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# All Guides
2+
13
* :bi-book:Getting Started
24
* [Introduction](/develop/guides)
35
* [Environment Setup](/develop/guides/env-setup)

webpack.config.mjs

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import FaviconsBundlerPlugin from "html-bundler-webpack-plugin/plugins/favicons-
55
import ImageMinimizerPlugin from "image-minimizer-webpack-plugin";
66
import {PurgeCSSPlugin} from "purgecss-webpack-plugin";
77
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
8-
import {remark} from "remark";
9-
import remarkRehype from "remark-rehype";
10-
import rehypeStringify from "rehype-stringify";
11-
import remarkBootstrapIcon from "./webpack/remark/bootstrap-icon.js";
12-
import {rehypeLinkActive} from "./webpack/rehype/link-active.js";
8+
import markdownToPage from "./webpack/markdown-to-page.js";
139

1410
const babelLoader = {
1511
loader: 'babel-loader',
@@ -19,39 +15,13 @@ const babelLoader = {
1915
}
2016
};
2117

22-
/**
23-
* @param relative {string}
24-
* @return {string}
25-
*/
26-
function getPagePath(relative) {
27-
let segs = relative.split(path.sep);
28-
if (segs[0] === 'src' && segs[1] === 'views') {
29-
segs = segs.slice(2);
30-
}
31-
if (segs[0] === 'index') {
32-
return '/';
33-
}
34-
const parsed = path.parse(segs.join(path.posix.sep));
35-
return `/${path.posix.format(parsed.name === 'index' ? {root: parsed.dir} : {dir: parsed.dir, name: parsed.name})}`;
36-
}
37-
3818
/** @type {HtmlBundlerPlugin.LoaderOptions} */
3919
const HtmlBundlerMarkdownOptions = {
40-
beforePreprocessor({content, meta}, {resourcePath, data, context, rootContext, fs}) {
41-
if (!resourcePath.endsWith('.md')) {
20+
beforePreprocessor({content, meta}, loaderContext) {
21+
if (!loaderContext.resourcePath.endsWith('.md')) {
4222
return undefined;
4323
}
44-
const sidebarEnt = fs.readdirSync(path.dirname(resourcePath))
45-
.find(ent => ent.startsWith('_sidebar.'));
46-
const processor = remark()
47-
.use(remarkBootstrapIcon)
48-
.use(remarkRehype, {allowDangerousHtml: true})
49-
.use(rehypeLinkActive, {active: getPagePath(path.relative(rootContext, resourcePath))})
50-
.use(rehypeStringify, {allowDangerousCharacters: true, allowDangerousHtml: true});
51-
const sidebar = sidebarEnt && processor
52-
.processSync(fs.readFileSync(path.join(context, sidebarEnt)));
53-
Object.assign(data, meta, sidebar && {sidebar});
54-
return `{{#> page }}${content}{{/page}}`;
24+
return markdownToPage(content, meta, loaderContext);
5525
}
5626
};
5727

webpack/markdown-to-page.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import path from "path";
2+
3+
import {remark} from "remark";
4+
5+
import remarkRehype from "remark-rehype";
6+
import remarkBootstrapIcon from "./remark/bootstrap-icon.js";
7+
8+
import {rehypeLinkActive} from "./rehype/link-active.js";
9+
import rehypeStringify from "rehype-stringify";
10+
11+
import {visit} from "unist-util-visit";
12+
import {toString} from "hast-util-to-string";
13+
14+
/**
15+
* @param relative {string}
16+
* @return {string}
17+
*/
18+
function getPagePath(relative) {
19+
let segs = relative.split(path.sep);
20+
if (segs[0] === 'src' && segs[1] === 'views') {
21+
segs = segs.slice(2);
22+
}
23+
if (segs[0] === 'index') {
24+
return '/';
25+
}
26+
const parsed = path.parse(segs.join(path.posix.sep));
27+
return `/${path.posix.format(parsed.name === 'index' ? {root: parsed.dir} : {dir: parsed.dir, name: parsed.name})}`;
28+
}
29+
30+
/**
31+
* @param content {string}
32+
* @param meta {Object}
33+
* @param loaderContext {import('webpack').LoaderContext<Object> & { data: { [key: string]: any } | string }}
34+
*/
35+
export default function (content, meta, loaderContext) {
36+
const {resourcePath, data, context, rootContext, fs} = loaderContext;
37+
const sidebarEnt = fs.readdirSync(path.dirname(resourcePath))
38+
.find(ent => ent.startsWith('_sidebar.'));
39+
const processor = remark()
40+
.use(remarkBootstrapIcon)
41+
.use(remarkRehype, {allowDangerousHtml: true})
42+
.use(rehypeLinkActive, {active: getPagePath(path.relative(rootContext, resourcePath))})
43+
.use(() => (tree, vfile) => {
44+
visit(tree, e => e.tagName === 'h1', (h1, index, parent) => {
45+
vfile.data.title = toString(h1);
46+
parent.children[index] = {type: 'comment', value: vfile.data.title};
47+
});
48+
})
49+
.use(rehypeStringify, {allowDangerousCharacters: true, allowDangerousHtml: true});
50+
/** @type {VFile|undefined} */
51+
const sidebar = sidebarEnt ? processor
52+
.processSync(fs.readFileSync(path.join(context, sidebarEnt))) : undefined;
53+
Object.assign(data, meta, sidebar && {sidebar: sidebar.data.title || 'Pages'});
54+
return `{{#partial 'sidebar'}}${sidebar}{{/partial}}{{#> page }}${content}{{/page}}`;
55+
}

0 commit comments

Comments
 (0)