diff --git a/lib/config/default.js b/lib/config/default.js index 95ee1940fe..08a4413d05 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -188,5 +188,6 @@ module.exports = { // 2nd appearance: "31-good-morning-my-friend---do-you-have-5-1" // 3rd appearance: "31-good-morning-my-friend---do-you-have-5-2" linkifyHeaderStyle: 'keep-case', - autoVersionCheck: true + autoVersionCheck: true, + defaultTocDepth: 3 } diff --git a/lib/config/environment.js b/lib/config/environment.js index 0867aecf54..cbf0f4c13f 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -147,5 +147,6 @@ module.exports = { openID: toBooleanConfig(process.env.CMD_OPENID), defaultUseHardbreak: toBooleanConfig(process.env.CMD_DEFAULT_USE_HARD_BREAK), linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE, - autoVersionCheck: toBooleanConfig(process.env.CMD_AUTO_VERSION_CHECK) + autoVersionCheck: toBooleanConfig(process.env.CMD_AUTO_VERSION_CHECK), + defaultTocDepth: toIntegerConfig(process.env.CMD_DEFAULT_TOC_DEPTH) } diff --git a/lib/status/index.js b/lib/status/index.js index 2f5366d62c..2e344efa2f 100644 --- a/lib/status/index.js +++ b/lib/status/index.js @@ -41,7 +41,8 @@ exports.getConfig = (req, res) => { allowedUploadMimeTypes: config.allowedUploadMimeTypes, defaultUseHardbreak: config.defaultUseHardbreak, linkifyHeaderStyle: config.linkifyHeaderStyle, - useCDN: config.useCDN + useCDN: config.useCDN, + defaultTocDepth: config.defaultTocDepth } res.set({ 'Cache-Control': 'private', // only cache by client diff --git a/public/css/extra.css b/public/css/extra.css index 2b23ad2413..f46eb3e6dc 100644 --- a/public/css/extra.css +++ b/public/css/extra.css @@ -253,6 +253,32 @@ padding-right: 40px; } +.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a { + padding-top: 1px; + padding-bottom: 1px; + padding-left: 50px; + font-size: 12px; + font-weight: 400; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a { + padding-right: 50px; +} + +.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a { + padding-top: 1px; + padding-bottom: 1px; + padding-left: 60px; + font-size: 12px; + font-weight: 400; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a { + padding-right: 60px; +} + + + .ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover { padding-left: 29px; } @@ -269,6 +295,22 @@ padding-right: 39px; } +.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a:hover { + padding-left: 49px; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a:hover { + padding-right: 49px; +} + +.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a:hover { + padding-left: 59px; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a:hover { + padding-right: 59px; +} + .ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a { padding-left: 28px; font-weight: 500; @@ -287,6 +329,24 @@ padding-right: 38px; } +.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>a { + padding-left: 48px; + font-weight: 500; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.active>.nav>.nav>.active>a { + padding-right: 48px; +} + +.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active>a { + padding-left: 58px; + font-weight: 500; +} + +.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.active>.nav>.nav>.active>.nav>.active>a { + padding-right: 58px; +} + /* support japanese font */ .markdown-body[lang^="ja"] { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", Osaka, Meiryo, "メイリオ", "MS Gothic", "MS ゴシック", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; diff --git a/public/docs/features.md b/public/docs/features.md index c3f5fac931..8902bee7ca 100644 --- a/public/docs/features.md +++ b/public/docs/features.md @@ -82,7 +82,8 @@ View ## Table of Contents: You can look at the bottom right section of the view area, there is a _ToC_ button . Pressing that button will show you a current _Table of Contents_, and will highlight which section you're at. -ToCs support up to **three header levels**. +ToCs support up to **five header levels**, the **default** is **set to three**. The maxLevel can be set for each note by using +[YAML Metadata](./yaml-metadata) ## Permalink Every header will automatically add a permalink on the right side. @@ -133,12 +134,19 @@ You can provide advanced note information to set the browser behavior (visit abo - GA: set to use Google Analytics - disqus: set to use Disqus - slideOptions: setup slide mode options +- toc: set options of the Table of Contents. ## ToC: -Use the syntax `[TOC]` to embed table of content into your note. +Use the syntax `[TOC]` to embed table of content into your note. By default, three header levels are displayed. This can also be specified by using [YAML Metadata](./yaml-metadata). [TOC] +You can also specify the number of header levels by specifying the `maxLevel` like this: `[TOC maxLevel=1]` + +[TOC maxLevel=1] + + + ## Emoji You can type any emoji like this :smile: :smiley: :cry: :wink: > See full emoji list [here](http://www.emoji-cheat-sheet.com/). diff --git a/public/docs/yaml-metadata.md b/public/docs/yaml-metadata.md index 839616a8f9..e064da19b1 100644 --- a/public/docs/yaml-metadata.md +++ b/public/docs/yaml-metadata.md @@ -128,6 +128,22 @@ This option allows you to enable Disqus with your shortname. disqus: codimd ``` +toc +--- + +This option allows you to set options regarding the table of contents (toc). Currently, its only option is to set the maxDepth. + +**Notice: always use two spaces as indention in YAML metadata!** + + +> **maxDepth:** +> default: not set (whioch will show everything until level 3 (h1 -- h3)) +> max: 5 (as defined by md-toc.js) + + +**Example** + + type --- This option allows you to switch the document view to the slide preview, to simplify live editing of presentations. diff --git a/public/js/extra.js b/public/js/extra.js index dfc206b0aa..8384739ad8 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -811,8 +811,12 @@ export function generateToc (id) { const target = $(`#${id}`) target.html('') /* eslint-disable no-unused-vars */ + + var tocOptions = md.meta.toc || {} + var maxLevel = (typeof tocOptions.maxLevel === 'number' && tocOptions.maxLevel > 0) ? tocOptions.maxLevel : window.defaultTocDepth + var toc = new window.Toc('doc', { - level: 3, + level: maxLevel, top: -1, class: 'toc', ulClass: 'nav', @@ -1010,11 +1014,20 @@ export function renderTOC (view) { const target = $(`#${id}`) target.html('') /* eslint-disable no-unused-vars */ + + const specificDepth = parseInt(toc.data('toc-depth')) + + var tocOptions = md.meta.toc || {} + var yamlMaxDepth = (typeof tocOptions.maxLevel === 'number' && tocOptions.maxLevel > 0) ? tocOptions.maxLevel : window.defaultTocDepth + + var maxLevel = specificDepth || yamlMaxDepth + const TOC = new window.Toc('doc', { - level: 3, + level: maxLevel, top: -1, class: 'toc', targetId: id, + data: { tocDepth: specificDepth }, process: getHeaderContent }) /* eslint-enable no-unused-vars */ @@ -1260,9 +1273,12 @@ const gistPlugin = new Plugin( // TOC const tocPlugin = new Plugin( // regexp to match - /^\[TOC\]$/i, + /^\[TOC(|\s*maxLevel=\d+?)\]$/i, - (match, utils) => '
' + (match, utils) => { + const tocDepth = match[1].split(/[?&=]+/)[1] + return `
` + } ) // slideshare const slidesharePlugin = new Plugin( diff --git a/public/js/lib/common/constant.ejs b/public/js/lib/common/constant.ejs index 57438912a7..3cb960ff2f 100644 --- a/public/js/lib/common/constant.ejs +++ b/public/js/lib/common/constant.ejs @@ -13,3 +13,5 @@ window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>' window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>' window.USE_CDN = <%- useCDN %> + +window.defaultTocDepth = <%- defaultTocDepth %> diff --git a/public/vendor/md-toc.js b/public/vendor/md-toc.js index 59e75aed3a..0c7b511641 100644 --- a/public/vendor/md-toc.js +++ b/public/vendor/md-toc.js @@ -2,6 +2,8 @@ /** * md-toc.js v1.0.2 * https://github.com/yijian166/md-toc.js + * + * Adapted to accept data attributes */ (function (window) { @@ -15,6 +17,7 @@ this.tocTop = parseInt(options.top) || 0 this.elChilds = this.el.children this.process = options['process'] + this.data = options.data || {} if (!this.elChilds.length) return this._init() } @@ -123,6 +126,9 @@ this.toc = document.createElement('div') this.toc.innerHTML = this.tocContent this.toc.setAttribute('class', this.tocClass) + if (this.data.tocDepth) { + this.toc.dataset.tocDepth = this.data.tocDepth + } if (!this.options.targetId) { this.el.appendChild(this.toc) } else {