From cb13fb33a74d5f0ee988476cef937c5e6e0e520b Mon Sep 17 00:00:00 2001 From: til-schneider Date: Wed, 25 May 2016 17:01:52 +0200 Subject: [PATCH] Added TOC --- README.md | 1 + src/client/js/app-view.js | 34 ++++++++++++++++++-- src/server/layout/page.php | 2 ++ src/server/theme/slim/less/_layout.less | 42 ++++++++++++++++++++++--- src/server/theme/slim/less/_view.less | 25 +++++++++++++++ src/server/theme/slim/page-header.php | 1 + 6 files changed, 99 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c376347..a5afc14 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Build a release zip: - [Parsedown](https://github.com/erusev/parsedown/) - PHP markdown parser. - [prism](http://prismjs.com/) - JavaScript syntax highlighter. - [CodeMirror](https://codemirror.net/) - JavaScript in-browser code editor. +- [Tocbot](http://tscanlin.github.io/tocbot/) - JavaScript table of contents generator. - [Vanilla JS](http://vanilla-js.com/) - No jQuery. Instead standard DOM API in order to make things fast and slim. diff --git a/src/client/js/app-view.js b/src/client/js/app-view.js index 4777464..284a22e 100644 --- a/src/client/js/app-view.js +++ b/src/client/js/app-view.js @@ -1,4 +1,4 @@ -(function(document, slimwiki, Prism) { +(function(document, slimwiki, Prism, tocbot) { slimwiki.View = { updateSyntaxHighlighting: updateSyntaxHighlighting @@ -15,10 +15,40 @@ Prism.plugins.autoloader.languages_path = 'client/libs/prism/components/'; if (mode == 'view' || mode == 'edit') { + initToc(); updateSyntaxHighlighting(); } } + function initToc() { + var headingSelector = 'h1, h2, h3', + nextId = 1, + headingsOffset = 80, + headings; + + // tocbot needs ID attributes at the headings in order to function + headings = document.getElementById('content').querySelectorAll(headingSelector); + Array.prototype.forEach.call(headings, function (headingElem) { + headingElem.id = 'heading-' + (nextId++); + }); + + tocbot.init({ + // Where to render the table of contents. + tocSelector: '.toc-wrapper', + // Where to grab the headings to build the table of contents. + contentSelector: '#content', + // Which headings to grab inside of the contentSelector element. + headingSelector: headingSelector, + // Headings offset between the headings and the top of the document. + headingsOffset: headingsOffset, + // smooth-scroll options object, see docs at: + // https://github.com/cferdinandi/smooth-scroll + smoothScrollOptions: { + offset: headingsOffset + } + }); + } + function updateSyntaxHighlighting(parentElem) { if (! parentElem) { parentElem = document.getElementById('content'); @@ -30,4 +60,4 @@ }); } -})(document, slimwiki, Prism); +})(document, slimwiki, Prism, tocbot); diff --git a/src/server/layout/page.php b/src/server/layout/page.php index aeeefe6..a488854 100644 --- a/src/server/layout/page.php +++ b/src/server/layout/page.php @@ -32,6 +32,7 @@ include($themeDir . '/init-theme.php'); + @@ -179,6 +180,7 @@ if ($mode == 'edit') { + diff --git a/src/server/theme/slim/less/_layout.less b/src/server/theme/slim/less/_layout.less index 15efd29..366abbd 100644 --- a/src/server/theme/slim/less/_layout.less +++ b/src/server/theme/slim/less/_layout.less @@ -1,10 +1,20 @@ @contentMaxWidth: 800px; @contentMinMarginX: 30px; +@tocMinWidth: 200px; +@tocMarginLeft: @contentMinMarginX; -// We need a variable using brackets here, otherwise less won't calculate the width -// See: https://github.com/less/less.js/issues/1903 -@small-screen-breakpoint: (@contentMaxWidth + 2 * @contentMinMarginX); +// Screen sizes: +// - small: Content takes full width, TOC is hidden +// - medium: Content is left, TOC is right or hidden (if width < @screen-size-toc-hidden-max) +// - large: Content is centered, TOC is right +// +// NOTE: We need a variable using brackets here, otherwise less won't calculate the width +// See: https://github.com/less/less.js/issues/1903 +@screen-size-small-max: (@contentMaxWidth + 2 * @contentMinMarginX); +@screen-size-toc-hidden-max: (@screen-size-small-max + @tocMinWidth); +@screen-size-medium-min: (@screen-size-small-max + 1px); +@screen-size-medium-max: (@contentMaxWidth + 2 * @tocMinWidth); .main-column { width: @contentMaxWidth; @@ -15,7 +25,11 @@ margin: 0 @contentMinMarginX; } - @media (max-width: @small-screen-breakpoint) { + @media (min-width: @screen-size-medium-min) and (max-width: @screen-size-medium-max) { + width: @contentMaxWidth; + margin: 0 @contentMinMarginX; + } + @media (max-width: @screen-size-small-max) { width: auto; margin: 0 @contentMinMarginX; } @@ -32,6 +46,26 @@ } } +.toc-wrapper { + position: fixed; + left: 50%; + right: 10px; + top: 70px; + margin-left: @contentMaxWidth / 2 + @tocMarginLeft; + + .mode-edit & { + display: none; + } + + @media (min-width: @screen-size-medium-min) and (max-width: @screen-size-medium-max) { + left: @contentMinMarginX + @contentMaxWidth + @tocMarginLeft; + margin-left: 0; + } + @media (max-width: @screen-size-toc-hidden-max) { + display: none; + } +} + #content { padding: 30px 0 150px; } diff --git a/src/server/theme/slim/less/_view.less b/src/server/theme/slim/less/_view.less index 1403c2f..1e184c3 100644 --- a/src/server/theme/slim/less/_view.less +++ b/src/server/theme/slim/less/_view.less @@ -26,6 +26,31 @@ body { } } +.toc-wrapper { + ul { + list-style: none; + } + .toc-link { + display: block; + padding: 5px 0; + line-height: 1; + text-decoration: none; + cursor: pointer; + + &::before { + margin-top: -5px; + } + } + .is-active-link { + font-weight: normal; + color: @brand-primary; + + &::before { + background-color: @brand-primary; + } + } +} + footer { height: 80px; padding-top: 30px; diff --git a/src/server/theme/slim/page-header.php b/src/server/theme/slim/page-header.php index 8dae564..4695ac0 100644 --- a/src/server/theme/slim/page-header.php +++ b/src/server/theme/slim/page-header.php @@ -19,3 +19,4 @@ foreach ($data['breadcrumbs'] as $item) { $isFirst = false; } ?> +