From 3f9ac3d5f4dfbecf94556296f85eca7ab59d9a26 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Wed, 29 Jul 2020 13:44:53 +0300 Subject: [PATCH] Eject vuepress theme to customize 404 page to include sample app deeplinking --- docs/.vuepress/theme/AlgoliaSearchBox.vue | 156 +++++++++++ docs/.vuepress/theme/DropdownLink.vue | 181 +++++++++++++ docs/.vuepress/theme/DropdownTransition.vue | 33 +++ docs/.vuepress/theme/Home.vue | 162 ++++++++++++ docs/.vuepress/theme/Layout.vue | 183 +++++++++++++ docs/.vuepress/theme/NavLink.vue | 49 ++++ docs/.vuepress/theme/NavLinks.vue | 151 +++++++++++ docs/.vuepress/theme/Navbar.vue | 133 ++++++++++ docs/.vuepress/theme/NotFound.vue | 43 +++ docs/.vuepress/theme/Page.vue | 246 ++++++++++++++++++ docs/.vuepress/theme/SWUpdatePopup.vue | 85 ++++++ docs/.vuepress/theme/SearchBox.vue | 238 +++++++++++++++++ docs/.vuepress/theme/Sidebar.vue | 113 ++++++++ docs/.vuepress/theme/SidebarButton.vue | 28 ++ docs/.vuepress/theme/SidebarGroup.vue | 77 ++++++ docs/.vuepress/theme/SidebarLink.vue | 91 +++++++ docs/.vuepress/theme/search.svg | 1 + docs/.vuepress/theme/styles/arrow.styl | 22 ++ docs/.vuepress/theme/styles/code.styl | 129 +++++++++ docs/.vuepress/theme/styles/config.styl | 20 ++ .../.vuepress/theme/styles/custom-blocks.styl | 28 ++ docs/.vuepress/theme/styles/mobile.styl | 37 +++ docs/.vuepress/theme/styles/nprogress.styl | 48 ++++ docs/.vuepress/theme/styles/theme.styl | 190 ++++++++++++++ docs/.vuepress/theme/styles/toc.styl | 3 + docs/.vuepress/theme/styles/wrapper.styl | 9 + docs/.vuepress/theme/util.js | 216 +++++++++++++++ 27 files changed, 2672 insertions(+) create mode 100644 docs/.vuepress/theme/AlgoliaSearchBox.vue create mode 100644 docs/.vuepress/theme/DropdownLink.vue create mode 100644 docs/.vuepress/theme/DropdownTransition.vue create mode 100644 docs/.vuepress/theme/Home.vue create mode 100644 docs/.vuepress/theme/Layout.vue create mode 100644 docs/.vuepress/theme/NavLink.vue create mode 100644 docs/.vuepress/theme/NavLinks.vue create mode 100644 docs/.vuepress/theme/Navbar.vue create mode 100644 docs/.vuepress/theme/NotFound.vue create mode 100644 docs/.vuepress/theme/Page.vue create mode 100644 docs/.vuepress/theme/SWUpdatePopup.vue create mode 100644 docs/.vuepress/theme/SearchBox.vue create mode 100644 docs/.vuepress/theme/Sidebar.vue create mode 100644 docs/.vuepress/theme/SidebarButton.vue create mode 100644 docs/.vuepress/theme/SidebarGroup.vue create mode 100644 docs/.vuepress/theme/SidebarLink.vue create mode 100644 docs/.vuepress/theme/search.svg create mode 100644 docs/.vuepress/theme/styles/arrow.styl create mode 100644 docs/.vuepress/theme/styles/code.styl create mode 100644 docs/.vuepress/theme/styles/config.styl create mode 100644 docs/.vuepress/theme/styles/custom-blocks.styl create mode 100644 docs/.vuepress/theme/styles/mobile.styl create mode 100644 docs/.vuepress/theme/styles/nprogress.styl create mode 100644 docs/.vuepress/theme/styles/theme.styl create mode 100644 docs/.vuepress/theme/styles/toc.styl create mode 100644 docs/.vuepress/theme/styles/wrapper.styl create mode 100644 docs/.vuepress/theme/util.js diff --git a/docs/.vuepress/theme/AlgoliaSearchBox.vue b/docs/.vuepress/theme/AlgoliaSearchBox.vue new file mode 100644 index 00000000..0334ae0b --- /dev/null +++ b/docs/.vuepress/theme/AlgoliaSearchBox.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/docs/.vuepress/theme/DropdownLink.vue b/docs/.vuepress/theme/DropdownLink.vue new file mode 100644 index 00000000..e6000a60 --- /dev/null +++ b/docs/.vuepress/theme/DropdownLink.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/docs/.vuepress/theme/DropdownTransition.vue b/docs/.vuepress/theme/DropdownTransition.vue new file mode 100644 index 00000000..8c711a15 --- /dev/null +++ b/docs/.vuepress/theme/DropdownTransition.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/docs/.vuepress/theme/Home.vue b/docs/.vuepress/theme/Home.vue new file mode 100644 index 00000000..a172eb9b --- /dev/null +++ b/docs/.vuepress/theme/Home.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/docs/.vuepress/theme/Layout.vue b/docs/.vuepress/theme/Layout.vue new file mode 100644 index 00000000..ee2e6abe --- /dev/null +++ b/docs/.vuepress/theme/Layout.vue @@ -0,0 +1,183 @@ + + + + + + diff --git a/docs/.vuepress/theme/NavLink.vue b/docs/.vuepress/theme/NavLink.vue new file mode 100644 index 00000000..d9fa4886 --- /dev/null +++ b/docs/.vuepress/theme/NavLink.vue @@ -0,0 +1,49 @@ + + + diff --git a/docs/.vuepress/theme/NavLinks.vue b/docs/.vuepress/theme/NavLinks.vue new file mode 100644 index 00000000..4037f288 --- /dev/null +++ b/docs/.vuepress/theme/NavLinks.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/docs/.vuepress/theme/Navbar.vue b/docs/.vuepress/theme/Navbar.vue new file mode 100644 index 00000000..12795665 --- /dev/null +++ b/docs/.vuepress/theme/Navbar.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/docs/.vuepress/theme/NotFound.vue b/docs/.vuepress/theme/NotFound.vue new file mode 100644 index 00000000..405807b3 --- /dev/null +++ b/docs/.vuepress/theme/NotFound.vue @@ -0,0 +1,43 @@ + + + diff --git a/docs/.vuepress/theme/Page.vue b/docs/.vuepress/theme/Page.vue new file mode 100644 index 00000000..37d65b41 --- /dev/null +++ b/docs/.vuepress/theme/Page.vue @@ -0,0 +1,246 @@ + + + + + diff --git a/docs/.vuepress/theme/SWUpdatePopup.vue b/docs/.vuepress/theme/SWUpdatePopup.vue new file mode 100644 index 00000000..b224db31 --- /dev/null +++ b/docs/.vuepress/theme/SWUpdatePopup.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/docs/.vuepress/theme/SearchBox.vue b/docs/.vuepress/theme/SearchBox.vue new file mode 100644 index 00000000..98608552 --- /dev/null +++ b/docs/.vuepress/theme/SearchBox.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/docs/.vuepress/theme/Sidebar.vue b/docs/.vuepress/theme/Sidebar.vue new file mode 100644 index 00000000..7bcf0059 --- /dev/null +++ b/docs/.vuepress/theme/Sidebar.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/docs/.vuepress/theme/SidebarButton.vue b/docs/.vuepress/theme/SidebarButton.vue new file mode 100644 index 00000000..0a222434 --- /dev/null +++ b/docs/.vuepress/theme/SidebarButton.vue @@ -0,0 +1,28 @@ + + + diff --git a/docs/.vuepress/theme/SidebarGroup.vue b/docs/.vuepress/theme/SidebarGroup.vue new file mode 100644 index 00000000..119dfa14 --- /dev/null +++ b/docs/.vuepress/theme/SidebarGroup.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/docs/.vuepress/theme/SidebarLink.vue b/docs/.vuepress/theme/SidebarLink.vue new file mode 100644 index 00000000..8288bf96 --- /dev/null +++ b/docs/.vuepress/theme/SidebarLink.vue @@ -0,0 +1,91 @@ + + + diff --git a/docs/.vuepress/theme/search.svg b/docs/.vuepress/theme/search.svg new file mode 100644 index 00000000..03d83913 --- /dev/null +++ b/docs/.vuepress/theme/search.svg @@ -0,0 +1 @@ + diff --git a/docs/.vuepress/theme/styles/arrow.styl b/docs/.vuepress/theme/styles/arrow.styl new file mode 100644 index 00000000..20bffc0d --- /dev/null +++ b/docs/.vuepress/theme/styles/arrow.styl @@ -0,0 +1,22 @@ +@require './config' + +.arrow + display inline-block + width 0 + height 0 + &.up + border-left 4px solid transparent + border-right 4px solid transparent + border-bottom 6px solid $arrowBgColor + &.down + border-left 4px solid transparent + border-right 4px solid transparent + border-top 6px solid $arrowBgColor + &.right + border-top 4px solid transparent + border-bottom 4px solid transparent + border-left 6px solid $arrowBgColor + &.left + border-top 4px solid transparent + border-bottom 4px solid transparent + border-right 6px solid $arrowBgColor diff --git a/docs/.vuepress/theme/styles/code.styl b/docs/.vuepress/theme/styles/code.styl new file mode 100644 index 00000000..8383c6e3 --- /dev/null +++ b/docs/.vuepress/theme/styles/code.styl @@ -0,0 +1,129 @@ +@require './config' + +.content + code + color lighten($textColor, 20%) + padding 0.25rem 0.5rem + margin 0 + font-size 0.85em + background-color rgba(27,31,35,0.05) + border-radius 3px + +.content + pre, pre[class*="language-"] + line-height 1.4 + padding 1.25rem 1.5rem + margin 0.85rem 0 + background-color $codeBgColor + border-radius 6px + overflow auto + code + color #fff + padding 0 + background-color transparent + border-radius 0 + +div[class*="language-"] + position relative + background-color $codeBgColor + border-radius 6px + .highlight-lines + user-select none + padding-top 1.3rem + position absolute + top 0 + left 0 + width 100% + line-height 1.4 + .highlighted + background-color rgba(0, 0, 0, 66%) + pre, pre[class*="language-"] + background transparent + position relative + z-index 1 + &::before + position absolute + z-index 3 + top 0.8em + right 1em + font-size 0.75rem + color rgba(255, 255, 255, 0.4) + &:not(.line-numbers-mode) + .line-numbers-wrapper + display none + &.line-numbers-mode + .highlight-lines .highlighted + position relative + &:before + content ' ' + position absolute + z-index 3 + left 0 + top 0 + display block + width $lineNumbersWrapperWidth + height 100% + background-color rgba(0, 0, 0, 66%) + pre + padding-left $lineNumbersWrapperWidth + 1 rem + vertical-align middle + .line-numbers-wrapper + position absolute + top 0 + width $lineNumbersWrapperWidth + text-align center + color rgba(255, 255, 255, 0.3) + padding 1.25rem 0 + line-height 1.4 + br + user-select none + .line-number + position relative + z-index 4 + user-select none + font-size 0.85em + &::after + content '' + position absolute + z-index 2 + top 0 + left 0 + width $lineNumbersWrapperWidth + height 100% + border-radius 6px 0 0 6px + border-right 1px solid rgba(0, 0, 0, 66%) + background-color $codeBgColor + + +for lang in $codeLang + div{'[class~="language-' + lang + '"]'} + &:before + content ('' + lang) + +div[class~="language-javascript"] + &:before + content "js" + +div[class~="language-typescript"] + &:before + content "ts" + +div[class~="language-markup"] + &:before + content "html" + +div[class~="language-markdown"] + &:before + content "md" + +div[class~="language-json"]:before + content "json" + +div[class~="language-ruby"]:before + content "rb" + +div[class~="language-python"]:before + content "py" + +div[class~="language-bash"]:before + content "sh" diff --git a/docs/.vuepress/theme/styles/config.styl b/docs/.vuepress/theme/styles/config.styl new file mode 100644 index 00000000..c8e5d8e3 --- /dev/null +++ b/docs/.vuepress/theme/styles/config.styl @@ -0,0 +1,20 @@ +// colors +$accentColor = #3eaf7c +$textColor = #2c3e50 +$borderColor = #eaecef +$codeBgColor = #282c34 +$arrowBgColor = #ccc + +// layout +$navbarHeight = 3.6rem +$sidebarWidth = 20rem +$contentWidth = 740px + +// responsive breakpoints +$MQNarrow = 959px +$MQMobile = 719px +$MQMobileNarrow = 419px + +// code +$lineNumbersWrapperWidth = 3.5rem +$codeLang = js ts html md vue css sass scss less stylus go java c sh yaml py diff --git a/docs/.vuepress/theme/styles/custom-blocks.styl b/docs/.vuepress/theme/styles/custom-blocks.styl new file mode 100644 index 00000000..3ccc2df2 --- /dev/null +++ b/docs/.vuepress/theme/styles/custom-blocks.styl @@ -0,0 +1,28 @@ +.custom-block + .custom-block-title + font-weight 600 + margin-bottom -0.4rem + &.tip, &.warning, &.danger + padding .1rem 1.5rem + border-left-width .5rem + border-left-style solid + margin 1rem 0 + &.tip + background-color #f3f5f7 + border-color #42b983 + &.warning + background-color rgba(255,229,100,.3) + border-color darken(#ffe564, 35%) + color darken(#ffe564, 70%) + .custom-block-title + color darken(#ffe564, 50%) + a + color $textColor + &.danger + background-color #ffe6e6 + border-color darken(red, 20%) + color darken(red, 70%) + .custom-block-title + color darken(red, 40%) + a + color $textColor diff --git a/docs/.vuepress/theme/styles/mobile.styl b/docs/.vuepress/theme/styles/mobile.styl new file mode 100644 index 00000000..b35e59c5 --- /dev/null +++ b/docs/.vuepress/theme/styles/mobile.styl @@ -0,0 +1,37 @@ +@require './config' + +$mobileSidebarWidth = $sidebarWidth * 0.82 + +// narrow desktop / iPad +@media (max-width: $MQNarrow) + .sidebar + font-size 15px + width $mobileSidebarWidth + .page + padding-left $mobileSidebarWidth + +// wide mobile +@media (max-width: $MQMobile) + .sidebar + top 0 + padding-top $navbarHeight + transform translateX(-100%) + transition transform .2s ease + .page + padding-left 0 + .theme-container + &.sidebar-open + .sidebar + transform translateX(0) + &.no-navbar + .sidebar + padding-top: 0 + +// narrow mobile +@media (max-width: $MQMobileNarrow) + h1 + font-size 1.9rem + .content + div[class*="language-"] + margin 0.85rem -1.5rem + border-radius 0 diff --git a/docs/.vuepress/theme/styles/nprogress.styl b/docs/.vuepress/theme/styles/nprogress.styl new file mode 100644 index 00000000..f186a67e --- /dev/null +++ b/docs/.vuepress/theme/styles/nprogress.styl @@ -0,0 +1,48 @@ +#nprogress + pointer-events none + .bar + background $accentColor + position fixed + z-index 1031 + top 0 + left 0 + width 100% + height 2px + .peg + display block + position absolute + right 0px + width 100px + height 100% + box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor + opacity 1.0 + transform rotate(3deg) translate(0px, -4px) + .spinner + display block + position fixed + z-index 1031 + top 15px + right 15px + .spinner-icon + width 18px + height 18px + box-sizing border-box + border solid 2px transparent + border-top-color $accentColor + border-left-color $accentColor + border-radius 50% + animation nprogress-spinner 400ms linear infinite + +.nprogress-custom-parent + overflow hidden + position relative + +.nprogress-custom-parent #nprogress .spinner, +.nprogress-custom-parent #nprogress .bar + position absolute + +@keyframes nprogress-spinner + 0% + transform rotate(0deg) + 100% + transform rotate(360deg) diff --git a/docs/.vuepress/theme/styles/theme.styl b/docs/.vuepress/theme/styles/theme.styl new file mode 100644 index 00000000..a733861f --- /dev/null +++ b/docs/.vuepress/theme/styles/theme.styl @@ -0,0 +1,190 @@ +@require './config' +@require './nprogress' +@require './code' +@require './custom-blocks' +@require './arrow' +@require './wrapper' +@require './toc' + +html, body + padding 0 + margin 0 + +body + font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif + -webkit-font-smoothing antialiased + -moz-osx-font-smoothing grayscale + font-size 16px + color $textColor + +.page + padding-left $sidebarWidth + +.navbar + position fixed + z-index 20 + top 0 + left 0 + right 0 + height $navbarHeight + background-color #fff + box-sizing border-box + border-bottom 1px solid $borderColor + +.sidebar-mask + position fixed + z-index 9 + top 0 + left 0 + width 100vw + height 100vh + display none + +.sidebar + font-size 15px + background-color #fff + width $sidebarWidth + position fixed + z-index 10 + margin 0 + top $navbarHeight + left 0 + bottom 0 + box-sizing border-box + border-right 1px solid $borderColor + overflow-y auto + +.content:not(.custom) + @extend $wrapper + > *:first-child + margin-top $navbarHeight + a:hover + text-decoration underline + p.demo + padding 1rem 1.5rem + border 1px solid #ddd + border-radius 4px + img + max-width 100% + +.content.custom + padding 0 + margin 0 + img + max-width 100% + +a + font-weight 500 + color $accentColor + text-decoration none + +p a code + font-weight 400 + color $accentColor + +kbd + background #eee + border solid 0.15rem #ddd + border-bottom solid 0.25rem #ddd + border-radius 0.15rem + padding 0 0.15em + +blockquote + font-size 1.2rem + color #999 + border-left .25rem solid #dfe2e5 + margin-left 0 + padding-left 1rem + +ul, ol + padding-left 1.2em + +strong + font-weight 600 + +h1, h2, h3, h4, h5, h6 + font-weight 600 + line-height 1.25 + .content:not(.custom) > & + margin-top (0.5rem - $navbarHeight) + padding-top ($navbarHeight + 1rem) + margin-bottom 0 + &:first-child + margin-top -1.5rem + margin-bottom 1rem + + p, + pre, + .custom-block + margin-top 2rem + &:hover .header-anchor + opacity: 1 + +h1 + font-size 2.2rem + +h2 + font-size 1.65rem + padding-bottom .3rem + border-bottom 1px solid $borderColor + +h3 + font-size 1.35rem + +a.header-anchor + font-size 0.85em + float left + margin-left -0.87em + padding-right 0.23em + margin-top 0.125em + opacity 0 + &:hover + text-decoration none + +code, kbd, .line-number + font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace + +p, ul, ol + line-height 1.7 + +hr + border 0 + border-top 1px solid $borderColor + +table + border-collapse collapse + margin 1rem 0 + display: block + overflow-x: auto + +tr + border-top 1px solid #dfe2e5 + &:nth-child(2n) + background-color #f6f8fa + +th, td + border 1px solid #dfe2e5 + padding .6em 1em + +.custom-layout + padding-top $navbarHeight + +.theme-container + &.sidebar-open + .sidebar-mask + display: block + &.no-navbar + .content:not(.custom) > h1, h2, h3, h4, h5, h6 + margin-top 1.5rem + padding-top 0 + .sidebar + top 0 + .custom-layout + padding-top 0 + + +@media (min-width: ($MQMobile + 1px)) + .theme-container.no-sidebar + .sidebar + display none + .page + padding-left 0 + +@require './mobile.styl' diff --git a/docs/.vuepress/theme/styles/toc.styl b/docs/.vuepress/theme/styles/toc.styl new file mode 100644 index 00000000..d3e71069 --- /dev/null +++ b/docs/.vuepress/theme/styles/toc.styl @@ -0,0 +1,3 @@ +.table-of-contents + .badge + vertical-align middle diff --git a/docs/.vuepress/theme/styles/wrapper.styl b/docs/.vuepress/theme/styles/wrapper.styl new file mode 100644 index 00000000..a99262c7 --- /dev/null +++ b/docs/.vuepress/theme/styles/wrapper.styl @@ -0,0 +1,9 @@ +$wrapper + max-width $contentWidth + margin 0 auto + padding 2rem 2.5rem + @media (max-width: $MQNarrow) + padding 2rem + @media (max-width: $MQMobileNarrow) + padding 1.5rem + diff --git a/docs/.vuepress/theme/util.js b/docs/.vuepress/theme/util.js new file mode 100644 index 00000000..ef95bea5 --- /dev/null +++ b/docs/.vuepress/theme/util.js @@ -0,0 +1,216 @@ +export const hashRE = /#.*$/ +export const extRE = /\.(md|html)$/ +export const endingSlashRE = /\/$/ +export const outboundRE = /^(https?:|mailto:|tel:)/ + +export function normalize (path) { + return decodeURI(path) + .replace(hashRE, '') + .replace(extRE, '') +} + +export function getHash (path) { + const match = path.match(hashRE) + if (match) { + return match[0] + } +} + +export function isExternal (path) { + return outboundRE.test(path) +} + +export function isMailto (path) { + return /^mailto:/.test(path) +} + +export function isTel (path) { + return /^tel:/.test(path) +} + +export function ensureExt (path) { + if (isExternal(path)) { + return path + } + const hashMatch = path.match(hashRE) + const hash = hashMatch ? hashMatch[0] : '' + const normalized = normalize(path) + + if (endingSlashRE.test(normalized)) { + return path + } + return normalized + '.html' + hash +} + +export function isActive (route, path) { + const routeHash = route.hash + const linkHash = getHash(path) + if (linkHash && routeHash !== linkHash) { + return false + } + const routePath = normalize(route.path) + const pagePath = normalize(path) + return routePath === pagePath +} + +export function resolvePage (pages, rawPath, base) { + if (base) { + rawPath = resolvePath(rawPath, base) + } + const path = normalize(rawPath) + for (let i = 0; i < pages.length; i++) { + if (normalize(pages[i].path) === path) { + return Object.assign({}, pages[i], { + type: 'page', + path: ensureExt(rawPath) + }) + } + } + console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`) + return {} +} + +function resolvePath (relative, base, append) { + const firstChar = relative.charAt(0) + if (firstChar === '/') { + return relative + } + + if (firstChar === '?' || firstChar === '#') { + return base + relative + } + + const stack = base.split('/') + + // remove trailing segment if: + // - not appending + // - appending to trailing slash (last segment is empty) + if (!append || !stack[stack.length - 1]) { + stack.pop() + } + + // resolve relative path + const segments = relative.replace(/^\//, '').split('/') + for (let i = 0; i < segments.length; i++) { + const segment = segments[i] + if (segment === '..') { + stack.pop() + } else if (segment !== '.') { + stack.push(segment) + } + } + + // ensure leading slash + if (stack[0] !== '') { + stack.unshift('') + } + + return stack.join('/') +} + +export function resolveSidebarItems (page, route, site, localePath) { + const { pages, themeConfig } = site + + const localeConfig = localePath && themeConfig.locales + ? themeConfig.locales[localePath] || themeConfig + : themeConfig + + const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar + if (pageSidebarConfig === 'auto') { + return resolveHeaders(page) + } + + const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar + if (!sidebarConfig) { + return [] + } else { + const { base, config } = resolveMatchingConfig(route, sidebarConfig) + return config + ? config.map(item => resolveItem(item, pages, base)) + : [] + } +} + +function resolveHeaders (page) { + const headers = groupHeaders(page.headers || []) + return [{ + type: 'group', + collapsable: false, + title: page.title, + children: headers.map(h => ({ + type: 'auto', + title: h.title, + basePath: page.path, + path: page.path + '#' + h.slug, + children: h.children || [] + })) + }] +} + +export function groupHeaders (headers) { + // group h3s under h2 + headers = headers.map(h => Object.assign({}, h)) + let lastH2 + headers.forEach(h => { + if (h.level === 2) { + lastH2 = h + } else if (lastH2) { + (lastH2.children || (lastH2.children = [])).push(h) + } + }) + return headers.filter(h => h.level === 2) +} + +export function resolveNavLinkItem (linkItem) { + return Object.assign(linkItem, { + type: linkItem.items && linkItem.items.length ? 'links' : 'link' + }) +} + +export function resolveMatchingConfig (route, config) { + if (Array.isArray(config)) { + return { + base: '/', + config: config + } + } + for (const base in config) { + if (ensureEndingSlash(route.path).indexOf(base) === 0) { + return { + base, + config: config[base] + } + } + } + return {} +} + +function ensureEndingSlash (path) { + return /(\.html|\/)$/.test(path) + ? path + : path + '/' +} + +function resolveItem (item, pages, base, isNested) { + if (typeof item === 'string') { + return resolvePage(pages, item, base) + } else if (Array.isArray(item)) { + return Object.assign(resolvePage(pages, item[0], base), { + title: item[1] + }) + } else { + if (isNested) { + console.error( + '[vuepress] Nested sidebar groups are not supported. ' + + 'Consider using navbar + categories instead.' + ) + } + const children = item.children || [] + return { + type: 'group', + title: item.title, + children: children.map(child => resolveItem(child, pages, base, true)), + collapsable: item.collapsable !== false + } + } +}