From ef53a07f497e12f800a4422df31c4ea501079c1e Mon Sep 17 00:00:00 2001 From: "qingwei.li" Date: Tue, 10 Jan 2017 23:16:55 +0800 Subject: [PATCH] Add new markdown grammar & improve sidebar performance. --- CHANGELOG.md | 5 +++ docs/README.md | 5 ++- src/event.js | 60 ++++++++++++++++++++++-------------- src/render.js | 25 +++++++++------ src/themes/basic/_layout.css | 34 ++++++++++++++++++++ src/themes/buble.css | 12 -------- src/themes/vue.css | 12 -------- 7 files changed, 95 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 268b9c9a..fb68fdec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.6.0 +### Features +- Improve sidebar performance. The active item is automatically scrolled in the visible view. +- New markdown grammar: `! `. e.g. `! content` will be rendered as `

content

` + ## 1.5.2 ### Bug fixes - Fixed number at the beginning of the slug diff --git a/docs/README.md b/docs/README.md index a670fec0..71c8ac3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -351,11 +351,14 @@ Scroll to the top on changing hash. ```html + + ``` ```js window.$docsify = { - auto2top: true + auto2top: true, + // auto2top: 50 } ``` diff --git a/src/event.js b/src/event.js index 802ddc39..0bfe5146 100644 --- a/src/event.js +++ b/src/event.js @@ -7,10 +7,12 @@ import { isMobile } from './util' export function scrollActiveSidebar () { if (isMobile()) return + let hoveredOverSidebar = false const anchors = document.querySelectorAll('.anchor') + const sidebar = document.querySelector('aside.sidebar') const nav = {} - const lis = document.querySelectorAll('.sidebar li') - let active = null + const lis = sidebar.querySelectorAll('li') + let active = sidebar.querySelector('li.active') for (let i = 0, len = lis.length; i < len; i += 1) { const li = lis[i] @@ -25,27 +27,34 @@ export function scrollActiveSidebar () { } function highlight () { + const top = document.body.scrollTop + let last + for (let i = 0, len = anchors.length; i < len; i += 1) { const node = anchors[i] - const bcr = node.getBoundingClientRect() - if (bcr.top < 10 && bcr.bottom > 10) { - const li = nav[node.getAttribute('data-id')] - - if (!li || li === active) return - if (active) active.setAttribute('class', '') - - li.setAttribute('class', 'active') - active = li - - return + if (node.offsetTop > top) { + if (!last) last = node + break + } else { + last = node } } + if (!last) return + const li = nav[last.getAttribute('data-id')] + + if (!li || li === active) return + if (active) active.classList.remove('active') + + li.classList.add('active') + active = li + !hoveredOverSidebar && active.scrollIntoView() } window.removeEventListener('scroll', highlight) window.addEventListener('scroll', highlight) - highlight() + sidebar.addEventListener('mouseover', () => { hoveredOverSidebar = true }) + sidebar.addEventListener('mouseleave', () => { hoveredOverSidebar = false }) } export function scrollIntoView () { @@ -53,7 +62,9 @@ export function scrollIntoView () { if (!id || !id.length) return const section = document.querySelector(decodeURIComponent(id[0])) - if (section) section.scrollIntoView() + if (section) setTimeout(() => section.scrollIntoView(), 0) + + return section } /** @@ -71,13 +82,13 @@ export function activeLink (dom, activeParent) { .forEach(node => { if (host.indexOf(node.href) === 0 && !target) { activeParent - ? node.parentNode.setAttribute('class', 'active') - : node.setAttribute('class', 'active') + ? node.parentNode.classList.add('active') + : node.classList.add('active') target = node } else { activeParent - ? node.parentNode.removeAttribute('class') - : node.removeAttribute('class') + ? node.parentNode.classList.remove('active') + : node.classList.remove('active') } }) @@ -95,13 +106,16 @@ export function bindToggle (dom) { dom.addEventListener('click', () => body.classList.toggle('close')) if (isMobile()) { - document.querySelector('aside') - .addEventListener('click', _ => body.classList.toggle('close')) + const sidebar = document.querySelector('aside.sidebar') + document.body.addEventListener('click', e => { + if (e.target !== dom && !dom.contains(e.target)) body.classList.toggle('close') + if (sidebar.contains(e.target)) setTimeout(() => activeLink(sidebar, true), 0) + }) } } -export function scroll2Top () { - document.body.scrollTop = 0 +export function scroll2Top (offset = 0) { + document.body.scrollTop = offset === true ? 0 : Number(offset) } export function sticky () { diff --git a/src/render.js b/src/render.js index 63d1937f..c3678215 100644 --- a/src/render.js +++ b/src/render.js @@ -1,13 +1,14 @@ import marked from 'marked' import Prism from 'prismjs' import * as tpl from './tpl' -import { activeLink, scrollActiveSidebar, bindToggle, scroll2Top, sticky } from './event' +import * as event from './event' import { genTree, getRoute, isMobile, slugify, merge } from './util' let OPTIONS = {} let markdown = marked let toc = [] const CACHE = {} +const TIP_RE = /^!\s/ const renderTo = function (dom, content) { dom = typeof dom === 'object' ? dom : document.querySelector(dom) @@ -55,6 +56,10 @@ export function init (options) { return `${text}` } + renderer.paragraph = function (text) { + const isTip = TIP_RE.test(text) + return isTip ? `

${text.replace(TIP_RE, '')}

` : `

${text}

` + } if (typeof OPTIONS.markdown === 'function') { markdown.setOptions({ renderer }) @@ -78,10 +83,10 @@ export function renderApp (dom, replace) { document.body.insertBefore(nav, document.body.children[0]) // bind toggle - bindToggle('button.sidebar-toggle') + event.bindToggle('button.sidebar-toggle') // bind sticky effect if (OPTIONS.coverpage) { - !isMobile() && window.addEventListener('scroll', sticky) + !isMobile() && window.addEventListener('scroll', event.sticky) } else { document.body.classList.add('sticky') } @@ -96,9 +101,9 @@ export function renderArticle (content) { if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') { const vm = new Vue({ el: 'main' }) // eslint-disable-line - vm.$nextTick(_ => scrollActiveSidebar()) + vm.$nextTick(_ => event.scrollActiveSidebar()) } - if (OPTIONS.auto2top) scroll2Top() + if (OPTIONS.auto2top) setTimeout(() => event.scroll2Top(OPTIONS.auto2top), 0) } /** @@ -109,7 +114,7 @@ export function renderNavbar (content) { CACHE.navbar = content if (content) renderTo('nav', markdown(content)) - activeLink('nav') + event.activeLink('nav') } /** @@ -127,11 +132,11 @@ export function renderSidebar (content) { } renderTo('aside.sidebar', html) - const target = activeLink('aside.sidebar', true) - if (content) renderSubSidebar(target) + const target = event.activeLink('aside.sidebar', true) + if (target) renderSubSidebar(target) toc = [] - scrollActiveSidebar() + event.scrollActiveSidebar() } export function renderSubSidebar (target) { @@ -171,7 +176,7 @@ export function renderCover (content) { renderTo('.cover-main', html) renderCover.rendered = true - sticky() + event.sticky() } /** diff --git a/src/themes/basic/_layout.css b/src/themes/basic/_layout.css index 010b7826..218c0b7e 100644 --- a/src/themes/basic/_layout.css +++ b/src/themes/basic/_layout.css @@ -291,6 +291,40 @@ body.sticky { } } +.markdown-section p.tip { + padding: 12px 24px 12px 30px; + margin: 2em 0; + border-left: 4px solid #f66; + background-color: #f8f8f8; + position: relative; + border-bottom-right-radius: 2px; + border-top-right-radius: 2px; + + &:before { + position: absolute; + top: 14px; + left: -12px; + background-color: #f66; + color: $color-bg; + content: "!"; + size: 20px; + border-radius: 100%; + text-align: center; + line-height: 20px; + font-weight: bold; + font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + } + + code { + background-color: #efefef; + } + + em { + color: $color-text; + } +} + body.close { .sidebar { transform: translateX(-$sidebar-width); diff --git a/src/themes/buble.css b/src/themes/buble.css index 5aec3ce0..cd6ed3f7 100644 --- a/src/themes/buble.css +++ b/src/themes/buble.css @@ -238,15 +238,3 @@ code .token { -webkit-font-smoothing: initial; -moz-osx-font-smoothing: initial; } - -.content span.light { - color: #7f8c8d; -} - -.content span.info { - display: inline-block; - font-size: 0.85em; - margin-left: 20px; - vertical-align: middle; - width: 280px; -} diff --git a/src/themes/vue.css b/src/themes/vue.css index a21ac514..1c6039de 100644 --- a/src/themes/vue.css +++ b/src/themes/vue.css @@ -285,15 +285,3 @@ pre::after { text-align: right; top: 0; } - -.content span.light { - color: #7f8c8d; -} - -.content span.info { - display: inline-block; - font-size: 0.85em; - margin-left: 20px; - vertical-align: middle; - width: 280px; -}