Add new markdown grammar & improve sidebar performance.

This commit is contained in:
qingwei.li 2017-01-10 23:16:55 +08:00
commit ef53a07f49
7 changed files with 95 additions and 58 deletions

View file

@ -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 `<p class="tip">content</p>`
## 1.5.2 ## 1.5.2
### Bug fixes ### Bug fixes
- Fixed number at the beginning of the slug - Fixed number at the beginning of the slug

View file

@ -351,11 +351,14 @@ Scroll to the top on changing hash.
```html ```html
<script src="/lib/docsify.js" data-auto2top></script> <script src="/lib/docsify.js" data-auto2top></script>
<!-- Set offset top -->
<script src="/lib/docsify.js" data-auto2top="50"></script>
``` ```
```js ```js
window.$docsify = { window.$docsify = {
auto2top: true auto2top: true,
// auto2top: 50
} }
``` ```

View file

@ -7,10 +7,12 @@ import { isMobile } from './util'
export function scrollActiveSidebar () { export function scrollActiveSidebar () {
if (isMobile()) return if (isMobile()) return
let hoveredOverSidebar = false
const anchors = document.querySelectorAll('.anchor') const anchors = document.querySelectorAll('.anchor')
const sidebar = document.querySelector('aside.sidebar')
const nav = {} const nav = {}
const lis = document.querySelectorAll('.sidebar li') const lis = sidebar.querySelectorAll('li')
let active = null let active = sidebar.querySelector('li.active')
for (let i = 0, len = lis.length; i < len; i += 1) { for (let i = 0, len = lis.length; i < len; i += 1) {
const li = lis[i] const li = lis[i]
@ -25,27 +27,34 @@ export function scrollActiveSidebar () {
} }
function highlight () { function highlight () {
const top = document.body.scrollTop
let last
for (let i = 0, len = anchors.length; i < len; i += 1) { for (let i = 0, len = anchors.length; i < len; i += 1) {
const node = anchors[i] const node = anchors[i]
const bcr = node.getBoundingClientRect()
if (bcr.top < 10 && bcr.bottom > 10) { if (node.offsetTop > top) {
const li = nav[node.getAttribute('data-id')] if (!last) last = node
break
if (!li || li === active) return } else {
if (active) active.setAttribute('class', '') last = node
li.setAttribute('class', 'active')
active = li
return
} }
} }
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.removeEventListener('scroll', highlight)
window.addEventListener('scroll', highlight) window.addEventListener('scroll', highlight)
highlight() sidebar.addEventListener('mouseover', () => { hoveredOverSidebar = true })
sidebar.addEventListener('mouseleave', () => { hoveredOverSidebar = false })
} }
export function scrollIntoView () { export function scrollIntoView () {
@ -53,7 +62,9 @@ export function scrollIntoView () {
if (!id || !id.length) return if (!id || !id.length) return
const section = document.querySelector(decodeURIComponent(id[0])) 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 => { .forEach(node => {
if (host.indexOf(node.href) === 0 && !target) { if (host.indexOf(node.href) === 0 && !target) {
activeParent activeParent
? node.parentNode.setAttribute('class', 'active') ? node.parentNode.classList.add('active')
: node.setAttribute('class', 'active') : node.classList.add('active')
target = node target = node
} else { } else {
activeParent activeParent
? node.parentNode.removeAttribute('class') ? node.parentNode.classList.remove('active')
: node.removeAttribute('class') : node.classList.remove('active')
} }
}) })
@ -95,13 +106,16 @@ export function bindToggle (dom) {
dom.addEventListener('click', () => body.classList.toggle('close')) dom.addEventListener('click', () => body.classList.toggle('close'))
if (isMobile()) { if (isMobile()) {
document.querySelector('aside') const sidebar = document.querySelector('aside.sidebar')
.addEventListener('click', _ => body.classList.toggle('close')) 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 () { export function scroll2Top (offset = 0) {
document.body.scrollTop = 0 document.body.scrollTop = offset === true ? 0 : Number(offset)
} }
export function sticky () { export function sticky () {

View file

@ -1,13 +1,14 @@
import marked from 'marked' import marked from 'marked'
import Prism from 'prismjs' import Prism from 'prismjs'
import * as tpl from './tpl' 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' import { genTree, getRoute, isMobile, slugify, merge } from './util'
let OPTIONS = {} let OPTIONS = {}
let markdown = marked let markdown = marked
let toc = [] let toc = []
const CACHE = {} const CACHE = {}
const TIP_RE = /^!\s/
const renderTo = function (dom, content) { const renderTo = function (dom, content) {
dom = typeof dom === 'object' ? dom : document.querySelector(dom) dom = typeof dom === 'object' ? dom : document.querySelector(dom)
@ -55,6 +56,10 @@ export function init (options) {
return `<a href="${href}" title="${title || ''}">${text}</a>` return `<a href="${href}" title="${title || ''}">${text}</a>`
} }
renderer.paragraph = function (text) {
const isTip = TIP_RE.test(text)
return isTip ? `<p class="tip">${text.replace(TIP_RE, '')}</p>` : `<p>${text}</p>`
}
if (typeof OPTIONS.markdown === 'function') { if (typeof OPTIONS.markdown === 'function') {
markdown.setOptions({ renderer }) markdown.setOptions({ renderer })
@ -78,10 +83,10 @@ export function renderApp (dom, replace) {
document.body.insertBefore(nav, document.body.children[0]) document.body.insertBefore(nav, document.body.children[0])
// bind toggle // bind toggle
bindToggle('button.sidebar-toggle') event.bindToggle('button.sidebar-toggle')
// bind sticky effect // bind sticky effect
if (OPTIONS.coverpage) { if (OPTIONS.coverpage) {
!isMobile() && window.addEventListener('scroll', sticky) !isMobile() && window.addEventListener('scroll', event.sticky)
} else { } else {
document.body.classList.add('sticky') document.body.classList.add('sticky')
} }
@ -96,9 +101,9 @@ export function renderArticle (content) {
if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') { if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') {
const vm = new Vue({ el: 'main' }) // eslint-disable-line 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 CACHE.navbar = content
if (content) renderTo('nav', markdown(content)) if (content) renderTo('nav', markdown(content))
activeLink('nav') event.activeLink('nav')
} }
/** /**
@ -127,11 +132,11 @@ export function renderSidebar (content) {
} }
renderTo('aside.sidebar', html) renderTo('aside.sidebar', html)
const target = activeLink('aside.sidebar', true) const target = event.activeLink('aside.sidebar', true)
if (content) renderSubSidebar(target) if (target) renderSubSidebar(target)
toc = [] toc = []
scrollActiveSidebar() event.scrollActiveSidebar()
} }
export function renderSubSidebar (target) { export function renderSubSidebar (target) {
@ -171,7 +176,7 @@ export function renderCover (content) {
renderTo('.cover-main', html) renderTo('.cover-main', html)
renderCover.rendered = true renderCover.rendered = true
sticky() event.sticky()
} }
/** /**

View file

@ -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 { body.close {
.sidebar { .sidebar {
transform: translateX(-$sidebar-width); transform: translateX(-$sidebar-width);

View file

@ -238,15 +238,3 @@ code .token {
-webkit-font-smoothing: initial; -webkit-font-smoothing: initial;
-moz-osx-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;
}

View file

@ -285,15 +285,3 @@ pre::after {
text-align: right; text-align: right;
top: 0; 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;
}