diff options
Diffstat (limited to 'doc/docs/.vuepress/theme')
34 files changed, 3111 insertions, 0 deletions
diff --git a/doc/docs/.vuepress/theme/LICENSE b/doc/docs/.vuepress/theme/LICENSE new file mode 100644 index 0000000..15f1f7e --- /dev/null +++ b/doc/docs/.vuepress/theme/LICENSE | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | The MIT License (MIT) | ||
| 2 | |||
| 3 | Copyright (c) 2018-present, Yuxi (Evan) You | ||
| 4 | |||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | of this software and associated documentation files (the "Software"), to deal | ||
| 7 | in the Software without restriction, including without limitation the rights | ||
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | copies of the Software, and to permit persons to whom the Software is | ||
| 10 | furnished to do so, subject to the following conditions: | ||
| 11 | |||
| 12 | The above copyright notice and this permission notice shall be included in | ||
| 13 | all copies or substantial portions of the Software. | ||
| 14 | |||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 21 | THE SOFTWARE. | ||
diff --git a/doc/docs/.vuepress/theme/README.md b/doc/docs/.vuepress/theme/README.md new file mode 100644 index 0000000..fc49626 --- /dev/null +++ b/doc/docs/.vuepress/theme/README.md | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # @vuepress/theme-default | ||
| 2 | |||
| 3 | > theme-default for VuePress | ||
| 4 | |||
| 5 | ## Plugins | ||
| 6 | |||
| 7 | The default theme has the following plugin built in: | ||
| 8 | |||
| 9 | - [@vuepress/plugin-active-header-links](https://github.com/vuejs/vuepress/tree/master/packages/@vuepress/plugin-active-header-links) | ||
| 10 | - [@vuepress/plugin-google-analytics](https://github.com/vuejs/vuepress/tree/master/packages/%40vuepress/plugin-google-analytics) | ||
| 11 | - [@vuepress/plugin-search](https://github.com/vuejs/vuepress/tree/master/packages/%40vuepress/plugin-search) | ||
diff --git a/doc/docs/.vuepress/theme/components/AlgoliaSearchBox.vue b/doc/docs/.vuepress/theme/components/AlgoliaSearchBox.vue new file mode 100644 index 0000000..7071fb8 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/AlgoliaSearchBox.vue | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | <template> | ||
| 2 | <form | ||
| 3 | id="search-form" | ||
| 4 | class="algolia-search-wrapper search-box" | ||
| 5 | role="search" | ||
| 6 | > | ||
| 7 | <input | ||
| 8 | id="algolia-search-input" | ||
| 9 | class="search-query" | ||
| 10 | :placeholder="placeholder" | ||
| 11 | > | ||
| 12 | </form> | ||
| 13 | </template> | ||
| 14 | |||
| 15 | <script> | ||
| 16 | export default { | ||
| 17 | name: 'AlgoliaSearchBox', | ||
| 18 | |||
| 19 | props: ['options'], | ||
| 20 | |||
| 21 | data () { | ||
| 22 | return { | ||
| 23 | placeholder: undefined | ||
| 24 | } | ||
| 25 | }, | ||
| 26 | |||
| 27 | watch: { | ||
| 28 | $lang (newValue) { | ||
| 29 | this.update(this.options, newValue) | ||
| 30 | }, | ||
| 31 | |||
| 32 | options (newValue) { | ||
| 33 | this.update(newValue, this.$lang) | ||
| 34 | } | ||
| 35 | }, | ||
| 36 | |||
| 37 | mounted () { | ||
| 38 | this.initialize(this.options, this.$lang) | ||
| 39 | this.placeholder = this.$site.themeConfig.searchPlaceholder || '' | ||
| 40 | }, | ||
| 41 | |||
| 42 | methods: { | ||
| 43 | initialize (userOptions, lang) { | ||
| 44 | Promise.all([ | ||
| 45 | import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'), | ||
| 46 | import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css') | ||
| 47 | ]).then(([docsearch]) => { | ||
| 48 | docsearch = docsearch.default | ||
| 49 | const { algoliaOptions = {}} = userOptions | ||
| 50 | docsearch(Object.assign( | ||
| 51 | {}, | ||
| 52 | userOptions, | ||
| 53 | { | ||
| 54 | inputSelector: '#algolia-search-input', | ||
| 55 | // #697 Make docsearch work well at i18n mode. | ||
| 56 | algoliaOptions: { | ||
| 57 | ...algoliaOptions, | ||
| 58 | facetFilters: [`lang:${lang}`].concat(algoliaOptions.facetFilters || []) | ||
| 59 | }, | ||
| 60 | handleSelected: (input, event, suggestion) => { | ||
| 61 | const { pathname, hash } = new URL(suggestion.url) | ||
| 62 | const routepath = pathname.replace(this.$site.base, '/') | ||
| 63 | const _hash = decodeURIComponent(hash) | ||
| 64 | this.$router.push(`${routepath}${_hash}`) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | )) | ||
| 68 | }) | ||
| 69 | }, | ||
| 70 | |||
| 71 | update (options, lang) { | ||
| 72 | this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">' | ||
| 73 | this.initialize(options, lang) | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | </script> | ||
| 78 | |||
| 79 | <style lang="stylus"> | ||
| 80 | .algolia-search-wrapper | ||
| 81 | & > span | ||
| 82 | vertical-align middle | ||
| 83 | .algolia-autocomplete | ||
| 84 | line-height normal | ||
| 85 | .ds-dropdown-menu | ||
| 86 | background-color #fff | ||
| 87 | border 1px solid #999 | ||
| 88 | border-radius 4px | ||
| 89 | font-size 16px | ||
| 90 | margin 6px 0 0 | ||
| 91 | padding 4px | ||
| 92 | text-align left | ||
| 93 | &:before | ||
| 94 | border-color #999 | ||
| 95 | [class*=ds-dataset-] | ||
| 96 | border none | ||
| 97 | padding 0 | ||
| 98 | .ds-suggestions | ||
| 99 | margin-top 0 | ||
| 100 | .ds-suggestion | ||
| 101 | border-bottom 1px solid $borderColor | ||
| 102 | .algolia-docsearch-suggestion--highlight | ||
| 103 | color #2c815b | ||
| 104 | .algolia-docsearch-suggestion | ||
| 105 | border-color $borderColor | ||
| 106 | padding 0 | ||
| 107 | .algolia-docsearch-suggestion--category-header | ||
| 108 | padding 5px 10px | ||
| 109 | margin-top 0 | ||
| 110 | background $accentColor | ||
| 111 | color #fff | ||
| 112 | font-weight 600 | ||
| 113 | .algolia-docsearch-suggestion--highlight | ||
| 114 | background rgba(255, 255, 255, 0.6) | ||
| 115 | .algolia-docsearch-suggestion--wrapper | ||
| 116 | padding 0 | ||
| 117 | .algolia-docsearch-suggestion--title | ||
| 118 | font-weight 600 | ||
| 119 | margin-bottom 0 | ||
| 120 | color $textColor | ||
| 121 | .algolia-docsearch-suggestion--subcategory-column | ||
| 122 | vertical-align top | ||
| 123 | padding 5px 7px 5px 5px | ||
| 124 | border-color $borderColor | ||
| 125 | background #f1f3f5 | ||
| 126 | &:after | ||
| 127 | display none | ||
| 128 | .algolia-docsearch-suggestion--subcategory-column-text | ||
| 129 | color #555 | ||
| 130 | .algolia-docsearch-footer | ||
| 131 | border-color $borderColor | ||
| 132 | .ds-cursor .algolia-docsearch-suggestion--content | ||
| 133 | background-color #e7edf3 !important | ||
| 134 | color $textColor | ||
| 135 | |||
| 136 | @media (min-width: $MQMobile) | ||
| 137 | .algolia-search-wrapper | ||
| 138 | .algolia-autocomplete | ||
| 139 | .algolia-docsearch-suggestion | ||
| 140 | .algolia-docsearch-suggestion--subcategory-column | ||
| 141 | float none | ||
| 142 | width 150px | ||
| 143 | min-width 150px | ||
| 144 | display table-cell | ||
| 145 | .algolia-docsearch-suggestion--content | ||
| 146 | float none | ||
| 147 | display table-cell | ||
| 148 | width 100% | ||
| 149 | vertical-align top | ||
| 150 | .ds-dropdown-menu | ||
| 151 | min-width 515px !important | ||
| 152 | |||
| 153 | @media (max-width: $MQMobile) | ||
| 154 | .algolia-search-wrapper | ||
| 155 | .ds-dropdown-menu | ||
| 156 | min-width calc(100vw - 4rem) !important | ||
| 157 | max-width calc(100vw - 4rem) !important | ||
| 158 | .algolia-docsearch-suggestion--wrapper | ||
| 159 | padding 5px 7px 5px 5px !important | ||
| 160 | .algolia-docsearch-suggestion--subcategory-column | ||
| 161 | padding 0 !important | ||
| 162 | background white !important | ||
| 163 | .algolia-docsearch-suggestion--subcategory-column-text:after | ||
| 164 | content " > " | ||
| 165 | font-size 10px | ||
| 166 | line-height 14.4px | ||
| 167 | display inline-block | ||
| 168 | width 5px | ||
| 169 | margin -3px 3px 0 | ||
| 170 | vertical-align middle | ||
| 171 | |||
| 172 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/DropdownLink.vue b/doc/docs/.vuepress/theme/components/DropdownLink.vue new file mode 100644 index 0000000..be6563f --- /dev/null +++ b/doc/docs/.vuepress/theme/components/DropdownLink.vue | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | <template> | ||
| 2 | <div | ||
| 3 | class="dropdown-wrapper" | ||
| 4 | :class="{ open }" | ||
| 5 | > | ||
| 6 | <button | ||
| 7 | class="dropdown-title" | ||
| 8 | type="button" | ||
| 9 | :aria-label="dropdownAriaLabel" | ||
| 10 | @click="handleDropdown" | ||
| 11 | > | ||
| 12 | <span class="title">{{ item.text }}</span> | ||
| 13 | <span | ||
| 14 | class="arrow down" | ||
| 15 | /> | ||
| 16 | </button> | ||
| 17 | <button | ||
| 18 | class="mobile-dropdown-title" | ||
| 19 | type="button" | ||
| 20 | :aria-label="dropdownAriaLabel" | ||
| 21 | @click="setOpen(!open)" | ||
| 22 | > | ||
| 23 | <span class="title">{{ item.text }}</span> | ||
| 24 | <span | ||
| 25 | class="arrow" | ||
| 26 | :class="open ? 'down' : 'right'" | ||
| 27 | /> | ||
| 28 | </button> | ||
| 29 | |||
| 30 | <DropdownTransition> | ||
| 31 | <ul | ||
| 32 | v-show="open" | ||
| 33 | class="nav-dropdown" | ||
| 34 | > | ||
| 35 | <li | ||
| 36 | v-for="(subItem, index) in item.items" | ||
| 37 | :key="subItem.link || index" | ||
| 38 | class="dropdown-item" | ||
| 39 | > | ||
| 40 | <h4 v-if="subItem.type === 'links'"> | ||
| 41 | {{ subItem.text }} | ||
| 42 | </h4> | ||
| 43 | |||
| 44 | <ul | ||
| 45 | v-if="subItem.type === 'links'" | ||
| 46 | class="dropdown-subitem-wrapper" | ||
| 47 | > | ||
| 48 | <li | ||
| 49 | v-for="childSubItem in subItem.items" | ||
| 50 | :key="childSubItem.link" | ||
| 51 | class="dropdown-subitem" | ||
| 52 | > | ||
| 53 | <NavLink | ||
| 54 | :item="childSubItem" | ||
| 55 | @focusout=" | ||
| 56 | isLastItemOfArray(childSubItem, subItem.items) && | ||
| 57 | isLastItemOfArray(subItem, item.items) && | ||
| 58 | setOpen(false) | ||
| 59 | " | ||
| 60 | /> | ||
| 61 | </li> | ||
| 62 | </ul> | ||
| 63 | |||
| 64 | <NavLink | ||
| 65 | v-else | ||
| 66 | :item="subItem" | ||
| 67 | @focusout="isLastItemOfArray(subItem, item.items) && setOpen(false)" | ||
| 68 | /> | ||
| 69 | </li> | ||
| 70 | </ul> | ||
| 71 | </DropdownTransition> | ||
| 72 | </div> | ||
| 73 | </template> | ||
| 74 | |||
| 75 | <script> | ||
| 76 | import NavLink from '@theme/components/NavLink.vue' | ||
| 77 | import DropdownTransition from '@theme/components/DropdownTransition.vue' | ||
| 78 | import last from 'lodash/last' | ||
| 79 | |||
| 80 | export default { | ||
| 81 | name: 'DropdownLink', | ||
| 82 | |||
| 83 | components: { | ||
| 84 | NavLink, | ||
| 85 | DropdownTransition | ||
| 86 | }, | ||
| 87 | |||
| 88 | props: { | ||
| 89 | item: { | ||
| 90 | required: true | ||
| 91 | } | ||
| 92 | }, | ||
| 93 | |||
| 94 | data () { | ||
| 95 | return { | ||
| 96 | open: false | ||
| 97 | } | ||
| 98 | }, | ||
| 99 | |||
| 100 | computed: { | ||
| 101 | dropdownAriaLabel () { | ||
| 102 | return this.item.ariaLabel || this.item.text | ||
| 103 | } | ||
| 104 | }, | ||
| 105 | |||
| 106 | watch: { | ||
| 107 | $route () { | ||
| 108 | this.open = false | ||
| 109 | } | ||
| 110 | }, | ||
| 111 | |||
| 112 | methods: { | ||
| 113 | setOpen (value) { | ||
| 114 | this.open = value | ||
| 115 | }, | ||
| 116 | |||
| 117 | isLastItemOfArray (item, array) { | ||
| 118 | return last(array) === item | ||
| 119 | }, | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Open the dropdown when user tab and click from keyboard. | ||
| 123 | * | ||
| 124 | * Use event.detail to detect tab and click from keyboard. Ref: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail | ||
| 125 | * The Tab + Click is UIEvent > KeyboardEvent, so the detail is 0. | ||
| 126 | */ | ||
| 127 | handleDropdown () { | ||
| 128 | const isTriggerByTab = event.detail === 0 | ||
| 129 | if (isTriggerByTab) this.setOpen(!this.open) | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | </script> | ||
| 134 | |||
| 135 | <style lang="stylus"> | ||
| 136 | .dropdown-wrapper | ||
| 137 | cursor pointer | ||
| 138 | .dropdown-title | ||
| 139 | display block | ||
| 140 | font-size 0.9rem | ||
| 141 | font-family inherit | ||
| 142 | cursor inherit | ||
| 143 | padding inherit | ||
| 144 | line-height 1.4rem | ||
| 145 | background transparent | ||
| 146 | border none | ||
| 147 | font-weight 500 | ||
| 148 | color $textColor | ||
| 149 | &:hover | ||
| 150 | border-color transparent | ||
| 151 | .arrow | ||
| 152 | vertical-align middle | ||
| 153 | margin-top -1px | ||
| 154 | margin-left 0.4rem | ||
| 155 | .mobile-dropdown-title | ||
| 156 | @extends .dropdown-title | ||
| 157 | display none | ||
| 158 | font-weight 600 | ||
| 159 | font-size inherit | ||
| 160 | &:hover | ||
| 161 | color $accentColor | ||
| 162 | .nav-dropdown | ||
| 163 | .dropdown-item | ||
| 164 | color inherit | ||
| 165 | line-height 1.7rem | ||
| 166 | h4 | ||
| 167 | margin 0.45rem 0 0 | ||
| 168 | border-top 1px solid #eee | ||
| 169 | padding 1rem 1.5rem 0.45rem 1.25rem | ||
| 170 | .dropdown-subitem-wrapper | ||
| 171 | padding 0 | ||
| 172 | list-style none | ||
| 173 | .dropdown-subitem | ||
| 174 | font-size 0.9em | ||
| 175 | a | ||
| 176 | display block | ||
| 177 | line-height 1.7rem | ||
| 178 | position relative | ||
| 179 | border-bottom none | ||
| 180 | font-weight 400 | ||
| 181 | margin-bottom 0 | ||
| 182 | padding 0 1.5rem 0 1.25rem | ||
| 183 | &:hover | ||
| 184 | color $accentColor | ||
| 185 | &.router-link-active | ||
| 186 | color $accentColor | ||
| 187 | &::after | ||
| 188 | content "" | ||
| 189 | width 0 | ||
| 190 | height 0 | ||
| 191 | border-left 5px solid $accentColor | ||
| 192 | border-top 3px solid transparent | ||
| 193 | border-bottom 3px solid transparent | ||
| 194 | position absolute | ||
| 195 | top calc(50% - 2px) | ||
| 196 | left 9px | ||
| 197 | &:first-child h4 | ||
| 198 | margin-top 0 | ||
| 199 | padding-top 0 | ||
| 200 | border-top 0 | ||
| 201 | |||
| 202 | @media (max-width: $MQMobile) | ||
| 203 | .dropdown-wrapper | ||
| 204 | &.open .dropdown-title | ||
| 205 | margin-bottom 0.5rem | ||
| 206 | .dropdown-title | ||
| 207 | display: none | ||
| 208 | .mobile-dropdown-title | ||
| 209 | display: block | ||
| 210 | .nav-dropdown | ||
| 211 | transition height .1s ease-out | ||
| 212 | overflow hidden | ||
| 213 | .dropdown-item | ||
| 214 | h4 | ||
| 215 | border-top 0 | ||
| 216 | margin-top 0 | ||
| 217 | padding-top 0 | ||
| 218 | h4, & > a | ||
| 219 | font-size 15px | ||
| 220 | line-height 2rem | ||
| 221 | .dropdown-subitem | ||
| 222 | font-size 14px | ||
| 223 | padding-left 1rem | ||
| 224 | |||
| 225 | @media (min-width: $MQMobile) | ||
| 226 | .dropdown-wrapper | ||
| 227 | height 1.8rem | ||
| 228 | &:hover .nav-dropdown, | ||
| 229 | &.open .nav-dropdown | ||
| 230 | // override the inline style. | ||
| 231 | display block !important | ||
| 232 | &.open:blur | ||
| 233 | display none | ||
| 234 | .nav-dropdown | ||
| 235 | display none | ||
| 236 | // Avoid height shaked by clicking | ||
| 237 | height auto !important | ||
| 238 | box-sizing border-box; | ||
| 239 | max-height calc(100vh - 2.7rem) | ||
| 240 | overflow-y auto | ||
| 241 | position absolute | ||
| 242 | top 100% | ||
| 243 | right 0 | ||
| 244 | background-color #fff | ||
| 245 | padding 0.6rem 0 | ||
| 246 | border 1px solid #ddd | ||
| 247 | border-bottom-color #ccc | ||
| 248 | text-align left | ||
| 249 | border-radius 0.25rem | ||
| 250 | white-space nowrap | ||
| 251 | margin 0 | ||
| 252 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/DropdownTransition.vue b/doc/docs/.vuepress/theme/components/DropdownTransition.vue new file mode 100644 index 0000000..eeaf12b --- /dev/null +++ b/doc/docs/.vuepress/theme/components/DropdownTransition.vue | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | <template> | ||
| 2 | <transition | ||
| 3 | name="dropdown" | ||
| 4 | @enter="setHeight" | ||
| 5 | @after-enter="unsetHeight" | ||
| 6 | @before-leave="setHeight" | ||
| 7 | > | ||
| 8 | <slot /> | ||
| 9 | </transition> | ||
| 10 | </template> | ||
| 11 | |||
| 12 | <script> | ||
| 13 | export default { | ||
| 14 | name: 'DropdownTransition', | ||
| 15 | |||
| 16 | methods: { | ||
| 17 | setHeight (items) { | ||
| 18 | // explicitly set height so that it can be transitioned | ||
| 19 | items.style.height = items.scrollHeight + 'px' | ||
| 20 | }, | ||
| 21 | |||
| 22 | unsetHeight (items) { | ||
| 23 | items.style.height = '' | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } | ||
| 27 | </script> | ||
| 28 | |||
| 29 | <style lang="stylus"> | ||
| 30 | .dropdown-enter, .dropdown-leave-to | ||
| 31 | height 0 !important | ||
| 32 | |||
| 33 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/Home.vue b/doc/docs/.vuepress/theme/components/Home.vue new file mode 100644 index 0000000..acd8744 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/Home.vue | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | <template> | ||
| 2 | <main | ||
| 3 | class="home" | ||
| 4 | :aria-labelledby="data.heroText !== null ? 'main-title' : null" | ||
| 5 | > | ||
| 6 | <header class="hero"> | ||
| 7 | <img | ||
| 8 | v-if="data.heroImage" | ||
| 9 | :src="$withBase(data.heroImage)" | ||
| 10 | :alt="data.heroAlt || 'hero'" | ||
| 11 | > | ||
| 12 | |||
| 13 | <h1 | ||
| 14 | v-if="data.heroText !== null" | ||
| 15 | id="main-title" | ||
| 16 | > | ||
| 17 | {{ data.heroText || $title || 'Hello' }} | ||
| 18 | </h1> | ||
| 19 | |||
| 20 | <p | ||
| 21 | v-if="data.tagline !== null" | ||
| 22 | class="description" | ||
| 23 | > | ||
| 24 | {{ data.tagline || $description || 'Welcome to your VuePress site' }} | ||
| 25 | </p> | ||
| 26 | |||
| 27 | <p | ||
| 28 | v-if="data.actionText && data.actionLink" | ||
| 29 | class="action" | ||
| 30 | > | ||
| 31 | <NavLink | ||
| 32 | class="action-button" | ||
| 33 | :item="actionLink" | ||
| 34 | /> | ||
| 35 | </p> | ||
| 36 | </header> | ||
| 37 | |||
| 38 | <div | ||
| 39 | v-if="data.features && data.features.length" | ||
| 40 | class="features" | ||
| 41 | > | ||
| 42 | <div | ||
| 43 | v-for="(feature, index) in data.features" | ||
| 44 | :key="index" | ||
| 45 | class="feature" | ||
| 46 | > | ||
| 47 | <h2>{{ feature.title }}</h2> | ||
| 48 | <p>{{ feature.details }}</p> | ||
| 49 | </div> | ||
| 50 | </div> | ||
| 51 | |||
| 52 | <Content class="theme-default-content custom" /> | ||
| 53 | |||
| 54 | <div | ||
| 55 | v-if="data.footer" | ||
| 56 | class="footer" | ||
| 57 | > | ||
| 58 | {{ data.footer }} | ||
| 59 | </div> | ||
| 60 | </main> | ||
| 61 | </template> | ||
| 62 | |||
| 63 | <script> | ||
| 64 | import NavLink from '@theme/components/NavLink.vue' | ||
| 65 | |||
| 66 | export default { | ||
| 67 | name: 'Home', | ||
| 68 | |||
| 69 | components: { NavLink }, | ||
| 70 | |||
| 71 | computed: { | ||
| 72 | data () { | ||
| 73 | return this.$page.frontmatter | ||
| 74 | }, | ||
| 75 | |||
| 76 | actionLink () { | ||
| 77 | return { | ||
| 78 | link: this.data.actionLink, | ||
| 79 | text: this.data.actionText | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | </script> | ||
| 85 | |||
| 86 | <style lang="stylus"> | ||
| 87 | .home | ||
| 88 | padding $navbarHeight 2rem 0 | ||
| 89 | max-width $homePageWidth | ||
| 90 | margin 0px auto | ||
| 91 | display block | ||
| 92 | .hero | ||
| 93 | text-align center | ||
| 94 | img | ||
| 95 | max-width: 100% | ||
| 96 | max-height 280px | ||
| 97 | display block | ||
| 98 | margin 3rem auto 1.5rem | ||
| 99 | h1 | ||
| 100 | font-size 3rem | ||
| 101 | h1, .description, .action | ||
| 102 | margin 1.8rem auto | ||
| 103 | .description | ||
| 104 | max-width 35rem | ||
| 105 | font-size 1.6rem | ||
| 106 | line-height 1.3 | ||
| 107 | color lighten($textColor, 40%) | ||
| 108 | .action-button | ||
| 109 | display inline-block | ||
| 110 | font-size 1.2rem | ||
| 111 | color #fff | ||
| 112 | background-color $accentColor | ||
| 113 | padding 0.8rem 1.6rem | ||
| 114 | border-radius 4px | ||
| 115 | transition background-color .1s ease | ||
| 116 | box-sizing border-box | ||
| 117 | border-bottom 1px solid darken($accentColor, 10%) | ||
| 118 | &:hover | ||
| 119 | background-color lighten($accentColor, 10%) | ||
| 120 | .features | ||
| 121 | border-top 1px solid $borderColor | ||
| 122 | padding 1.2rem 0 | ||
| 123 | margin-top 2.5rem | ||
| 124 | display flex | ||
| 125 | flex-wrap wrap | ||
| 126 | align-items flex-start | ||
| 127 | align-content stretch | ||
| 128 | justify-content space-between | ||
| 129 | .feature | ||
| 130 | flex-grow 1 | ||
| 131 | flex-basis 30% | ||
| 132 | max-width 30% | ||
| 133 | h2 | ||
| 134 | font-size 1.4rem | ||
| 135 | font-weight 500 | ||
| 136 | border-bottom none | ||
| 137 | padding-bottom 0 | ||
| 138 | color lighten($textColor, 10%) | ||
| 139 | p | ||
| 140 | color lighten($textColor, 25%) | ||
| 141 | .footer | ||
| 142 | padding 2.5rem | ||
| 143 | border-top 1px solid $borderColor | ||
| 144 | text-align center | ||
| 145 | color lighten($textColor, 25%) | ||
| 146 | |||
| 147 | @media (max-width: $MQMobile) | ||
| 148 | .home | ||
| 149 | .features | ||
| 150 | flex-direction column | ||
| 151 | .feature | ||
| 152 | max-width 100% | ||
| 153 | padding 0 2.5rem | ||
| 154 | |||
| 155 | @media (max-width: $MQMobileNarrow) | ||
| 156 | .home | ||
| 157 | padding-left 1.5rem | ||
| 158 | padding-right 1.5rem | ||
| 159 | .hero | ||
| 160 | img | ||
| 161 | max-height 210px | ||
| 162 | margin 2rem auto 1.2rem | ||
| 163 | h1 | ||
| 164 | font-size 2rem | ||
| 165 | h1, .description, .action | ||
| 166 | margin 1.2rem auto | ||
| 167 | .description | ||
| 168 | font-size 1.2rem | ||
| 169 | .action-button | ||
| 170 | font-size 1rem | ||
| 171 | padding 0.6rem 1.2rem | ||
| 172 | .feature | ||
| 173 | h2 | ||
| 174 | font-size 1.25rem | ||
| 175 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/NavLink.vue b/doc/docs/.vuepress/theme/components/NavLink.vue new file mode 100644 index 0000000..f7e65a4 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/NavLink.vue | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | <template> | ||
| 2 | <RouterLink | ||
| 3 | v-if="isInternal" | ||
| 4 | class="nav-link" | ||
| 5 | :to="link" | ||
| 6 | :exact="exact" | ||
| 7 | @focusout.native="focusoutAction" | ||
| 8 | > | ||
| 9 | {{ item.text }} | ||
| 10 | </RouterLink> | ||
| 11 | <a | ||
| 12 | v-else | ||
| 13 | :href="link" | ||
| 14 | class="nav-link external" | ||
| 15 | :target="target" | ||
| 16 | :rel="rel" | ||
| 17 | @focusout="focusoutAction" | ||
| 18 | > | ||
| 19 | {{ item.text }} | ||
| 20 | <OutboundLink v-if="isBlankTarget" /> | ||
| 21 | </a> | ||
| 22 | </template> | ||
| 23 | |||
| 24 | <script> | ||
| 25 | import { isExternal, isMailto, isTel, ensureExt } from '../util' | ||
| 26 | |||
| 27 | export default { | ||
| 28 | name: 'NavLink', | ||
| 29 | |||
| 30 | props: { | ||
| 31 | item: { | ||
| 32 | required: true | ||
| 33 | } | ||
| 34 | }, | ||
| 35 | |||
| 36 | computed: { | ||
| 37 | link () { | ||
| 38 | return ensureExt(this.item.link) | ||
| 39 | }, | ||
| 40 | |||
| 41 | exact () { | ||
| 42 | if (this.$site.locales) { | ||
| 43 | return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link) | ||
| 44 | } | ||
| 45 | return this.link === '/' | ||
| 46 | }, | ||
| 47 | |||
| 48 | isNonHttpURI () { | ||
| 49 | return isMailto(this.link) || isTel(this.link) | ||
| 50 | }, | ||
| 51 | |||
| 52 | isBlankTarget () { | ||
| 53 | return this.target === '_blank' | ||
| 54 | }, | ||
| 55 | |||
| 56 | isInternal () { | ||
| 57 | return !isExternal(this.link) && !this.isBlankTarget | ||
| 58 | }, | ||
| 59 | |||
| 60 | target () { | ||
| 61 | if (this.isNonHttpURI) { | ||
| 62 | return null | ||
| 63 | } | ||
| 64 | if (this.item.target) { | ||
| 65 | return this.item.target | ||
| 66 | } | ||
| 67 | return isExternal(this.link) ? '_blank' : '' | ||
| 68 | }, | ||
| 69 | |||
| 70 | rel () { | ||
| 71 | if (this.isNonHttpURI) { | ||
| 72 | return null | ||
| 73 | } | ||
| 74 | if (this.item.rel === false) { | ||
| 75 | return null | ||
| 76 | } | ||
| 77 | if (this.item.rel) { | ||
| 78 | return this.item.rel | ||
| 79 | } | ||
| 80 | return this.isBlankTarget ? 'noopener noreferrer' : null | ||
| 81 | } | ||
| 82 | }, | ||
| 83 | |||
| 84 | methods: { | ||
| 85 | focusoutAction () { | ||
| 86 | this.$emit('focusout') | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | </script> | ||
diff --git a/doc/docs/.vuepress/theme/components/NavLinks.vue b/doc/docs/.vuepress/theme/components/NavLinks.vue new file mode 100644 index 0000000..2656ae2 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/NavLinks.vue | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | <template> | ||
| 2 | <nav | ||
| 3 | v-if="userLinks.length || repoLink" | ||
| 4 | class="nav-links" | ||
| 5 | > | ||
| 6 | <!-- user links --> | ||
| 7 | <div | ||
| 8 | v-for="item in userLinks" | ||
| 9 | :key="item.link" | ||
| 10 | class="nav-item" | ||
| 11 | > | ||
| 12 | <DropdownLink | ||
| 13 | v-if="item.type === 'links'" | ||
| 14 | :item="item" | ||
| 15 | /> | ||
| 16 | <NavLink | ||
| 17 | v-else | ||
| 18 | :item="item" | ||
| 19 | /> | ||
| 20 | </div> | ||
| 21 | |||
| 22 | <!-- repo link --> | ||
| 23 | <a | ||
| 24 | v-if="repoLink" | ||
| 25 | :href="repoLink" | ||
| 26 | class="repo-link" | ||
| 27 | target="_blank" | ||
| 28 | rel="noopener noreferrer" | ||
| 29 | > | ||
| 30 | {{ repoLabel }} | ||
| 31 | <OutboundLink /> | ||
| 32 | </a> | ||
| 33 | </nav> | ||
| 34 | </template> | ||
| 35 | |||
| 36 | <script> | ||
| 37 | import DropdownLink from '@theme/components/DropdownLink.vue' | ||
| 38 | import { resolveNavLinkItem } from '../util' | ||
| 39 | import NavLink from '@theme/components/NavLink.vue' | ||
| 40 | |||
| 41 | export default { | ||
| 42 | name: 'NavLinks', | ||
| 43 | |||
| 44 | components: { | ||
| 45 | NavLink, | ||
| 46 | DropdownLink | ||
| 47 | }, | ||
| 48 | |||
| 49 | computed: { | ||
| 50 | userNav () { | ||
| 51 | return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || [] | ||
| 52 | }, | ||
| 53 | |||
| 54 | nav () { | ||
| 55 | const { locales } = this.$site | ||
| 56 | if (locales && Object.keys(locales).length > 1) { | ||
| 57 | const currentLink = this.$page.path | ||
| 58 | const routes = this.$router.options.routes | ||
| 59 | const themeLocales = this.$site.themeConfig.locales || {} | ||
| 60 | const languageDropdown = { | ||
| 61 | text: this.$themeLocaleConfig.selectText || 'Languages', | ||
| 62 | ariaLabel: this.$themeLocaleConfig.ariaLabel || 'Select language', | ||
| 63 | items: Object.keys(locales).map(path => { | ||
| 64 | const locale = locales[path] | ||
| 65 | const text = themeLocales[path] && themeLocales[path].label || locale.lang | ||
| 66 | let link | ||
| 67 | // Stay on the current page | ||
| 68 | if (locale.lang === this.$lang) { | ||
| 69 | link = currentLink | ||
| 70 | } else { | ||
| 71 | // Try to stay on the same page | ||
| 72 | link = currentLink.replace(this.$localeConfig.path, path) | ||
| 73 | // fallback to homepage | ||
| 74 | if (!routes.some(route => route.path === link)) { | ||
| 75 | link = path | ||
| 76 | } | ||
| 77 | } | ||
| 78 | return { text, link } | ||
| 79 | }) | ||
| 80 | } | ||
| 81 | return [...this.userNav, languageDropdown] | ||
| 82 | } | ||
| 83 | return this.userNav | ||
| 84 | }, | ||
| 85 | |||
| 86 | userLinks () { | ||
| 87 | return (this.nav || []).map(link => { | ||
| 88 | return Object.assign(resolveNavLinkItem(link), { | ||
| 89 | items: (link.items || []).map(resolveNavLinkItem) | ||
| 90 | }) | ||
| 91 | }) | ||
| 92 | }, | ||
| 93 | |||
| 94 | repoLink () { | ||
| 95 | const { repo } = this.$site.themeConfig | ||
| 96 | if (repo) { | ||
| 97 | return /^https?:/.test(repo) | ||
| 98 | ? repo | ||
| 99 | : `https://github.com/${repo}` | ||
| 100 | } | ||
| 101 | return null | ||
| 102 | }, | ||
| 103 | |||
| 104 | repoLabel () { | ||
| 105 | if (!this.repoLink) return | ||
| 106 | if (this.$site.themeConfig.repoLabel) { | ||
| 107 | return this.$site.themeConfig.repoLabel | ||
| 108 | } | ||
| 109 | |||
| 110 | const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0] | ||
| 111 | const platforms = ['GitHub', 'GitLab', 'Bitbucket'] | ||
| 112 | for (let i = 0; i < platforms.length; i++) { | ||
| 113 | const platform = platforms[i] | ||
| 114 | if (new RegExp(platform, 'i').test(repoHost)) { | ||
| 115 | return platform | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | return 'Source' | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | </script> | ||
| 124 | |||
| 125 | <style lang="stylus"> | ||
| 126 | .nav-links | ||
| 127 | display inline-block | ||
| 128 | a | ||
| 129 | line-height 1.4rem | ||
| 130 | color inherit | ||
| 131 | &:hover, &.router-link-active | ||
| 132 | color $accentColor | ||
| 133 | .nav-item | ||
| 134 | position relative | ||
| 135 | display inline-block | ||
| 136 | margin-left 1.5rem | ||
| 137 | line-height 2rem | ||
| 138 | &:first-child | ||
| 139 | margin-left 0 | ||
| 140 | .repo-link | ||
| 141 | margin-left 1.5rem | ||
| 142 | |||
| 143 | @media (max-width: $MQMobile) | ||
| 144 | .nav-links | ||
| 145 | .nav-item, .repo-link | ||
| 146 | margin-left 0 | ||
| 147 | |||
| 148 | @media (min-width: $MQMobile) | ||
| 149 | .nav-links a | ||
| 150 | &:hover, &.router-link-active | ||
| 151 | color $textColor | ||
| 152 | .nav-item > a:not(.external) | ||
| 153 | &:hover, &.router-link-active | ||
| 154 | margin-bottom -2px | ||
| 155 | border-bottom 2px solid lighten($accentColor, 8%) | ||
| 156 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/Navbar.vue b/doc/docs/.vuepress/theme/components/Navbar.vue new file mode 100644 index 0000000..f8dd49c --- /dev/null +++ b/doc/docs/.vuepress/theme/components/Navbar.vue | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | <template> | ||
| 2 | <header class="navbar"> | ||
| 3 | <SidebarButton @toggle-sidebar="$emit('toggle-sidebar')" /> | ||
| 4 | |||
| 5 | <RouterLink | ||
| 6 | :to="$localePath" | ||
| 7 | class="home-link" | ||
| 8 | > | ||
| 9 | <img | ||
| 10 | v-if="$site.themeConfig.logo" | ||
| 11 | class="logo" | ||
| 12 | :src="$withBase($site.themeConfig.logo)" | ||
| 13 | :alt="$siteTitle" | ||
| 14 | > | ||
| 15 | <span | ||
| 16 | v-if="$siteTitle" | ||
| 17 | ref="siteName" | ||
| 18 | class="site-name" | ||
| 19 | :class="{ 'can-hide': $site.themeConfig.logo }" | ||
| 20 | >{{ $siteTitle }}</span> | ||
| 21 | </RouterLink> | ||
| 22 | |||
| 23 | <div | ||
| 24 | class="links" | ||
| 25 | :style="linksWrapMaxWidth ? { | ||
| 26 | 'max-width': linksWrapMaxWidth + 'px' | ||
| 27 | } : {}" | ||
| 28 | > | ||
| 29 | <AlgoliaSearchBox | ||
| 30 | v-if="isAlgoliaSearch" | ||
| 31 | :options="algolia" | ||
| 32 | /> | ||
| 33 | <SearchBox v-else-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false" /> | ||
| 34 | <NavLinks class="can-hide" /> | ||
| 35 | </div> | ||
| 36 | </header> | ||
| 37 | </template> | ||
| 38 | |||
| 39 | <script> | ||
| 40 | import AlgoliaSearchBox from '@AlgoliaSearchBox' | ||
| 41 | import SearchBox from '@SearchBox' | ||
| 42 | import SidebarButton from '@theme/components/SidebarButton.vue' | ||
| 43 | import NavLinks from '@theme/components/NavLinks.vue' | ||
| 44 | |||
| 45 | export default { | ||
| 46 | name: 'Navbar', | ||
| 47 | |||
| 48 | components: { | ||
| 49 | SidebarButton, | ||
| 50 | NavLinks, | ||
| 51 | SearchBox, | ||
| 52 | AlgoliaSearchBox | ||
| 53 | }, | ||
| 54 | |||
| 55 | data () { | ||
| 56 | return { | ||
| 57 | linksWrapMaxWidth: null | ||
| 58 | } | ||
| 59 | }, | ||
| 60 | |||
| 61 | computed: { | ||
| 62 | algolia () { | ||
| 63 | return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {} | ||
| 64 | }, | ||
| 65 | |||
| 66 | isAlgoliaSearch () { | ||
| 67 | return this.algolia && this.algolia.apiKey && this.algolia.indexName | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | |||
| 71 | mounted () { | ||
| 72 | const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl | ||
| 73 | const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight')) | ||
| 74 | const handleLinksWrapWidth = () => { | ||
| 75 | if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) { | ||
| 76 | this.linksWrapMaxWidth = null | ||
| 77 | } else { | ||
| 78 | this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING | ||
| 79 | - (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0) | ||
| 80 | } | ||
| 81 | } | ||
| 82 | handleLinksWrapWidth() | ||
| 83 | window.addEventListener('resize', handleLinksWrapWidth, false) | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | function css (el, property) { | ||
| 88 | // NOTE: Known bug, will return 'auto' if style value is 'auto' | ||
| 89 | const win = el.ownerDocument.defaultView | ||
| 90 | // null means not to return pseudo styles | ||
| 91 | return win.getComputedStyle(el, null)[property] | ||
| 92 | } | ||
| 93 | </script> | ||
| 94 | |||
| 95 | <style lang="stylus"> | ||
| 96 | $navbar-vertical-padding = 0.7rem | ||
| 97 | $navbar-horizontal-padding = 1.5rem | ||
| 98 | |||
| 99 | .navbar | ||
| 100 | padding $navbar-vertical-padding $navbar-horizontal-padding | ||
| 101 | line-height $navbarHeight - 1.4rem | ||
| 102 | a, span, img | ||
| 103 | display inline-block | ||
| 104 | .logo | ||
| 105 | height $navbarHeight - 1.4rem | ||
| 106 | min-width $navbarHeight - 1.4rem | ||
| 107 | margin-right 0.8rem | ||
| 108 | vertical-align top | ||
| 109 | .site-name | ||
| 110 | font-size 1.3rem | ||
| 111 | font-weight 600 | ||
| 112 | color $textColor | ||
| 113 | position relative | ||
| 114 | .links | ||
| 115 | padding-left 1.5rem | ||
| 116 | box-sizing border-box | ||
| 117 | background-color white | ||
| 118 | white-space nowrap | ||
| 119 | font-size 0.9rem | ||
| 120 | position absolute | ||
| 121 | right $navbar-horizontal-padding | ||
| 122 | top $navbar-vertical-padding | ||
| 123 | display flex | ||
| 124 | .search-box | ||
| 125 | flex: 0 0 auto | ||
| 126 | vertical-align top | ||
| 127 | |||
| 128 | @media (max-width: $MQMobile) | ||
| 129 | .navbar | ||
| 130 | padding-left 4rem | ||
| 131 | .can-hide | ||
| 132 | display none | ||
| 133 | .links | ||
| 134 | padding-left 1.5rem | ||
| 135 | .site-name | ||
| 136 | width calc(100vw - 9.4rem) | ||
| 137 | overflow hidden | ||
| 138 | white-space nowrap | ||
| 139 | text-overflow ellipsis | ||
| 140 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/Page.vue b/doc/docs/.vuepress/theme/components/Page.vue new file mode 100644 index 0000000..04ec7cb --- /dev/null +++ b/doc/docs/.vuepress/theme/components/Page.vue | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | <template> | ||
| 2 | <main class="page"> | ||
| 3 | <slot name="top" /> | ||
| 4 | |||
| 5 | <Content class="theme-default-content" /> | ||
| 6 | <PageEdit /> | ||
| 7 | |||
| 8 | <PageNav v-bind="{ sidebarItems }" /> | ||
| 9 | |||
| 10 | <slot name="bottom" /> | ||
| 11 | </main> | ||
| 12 | </template> | ||
| 13 | |||
| 14 | <script> | ||
| 15 | import PageEdit from '@theme/components/PageEdit.vue' | ||
| 16 | import PageNav from '@theme/components/PageNav.vue' | ||
| 17 | |||
| 18 | export default { | ||
| 19 | components: { PageEdit, PageNav }, | ||
| 20 | props: ['sidebarItems'] | ||
| 21 | } | ||
| 22 | </script> | ||
| 23 | |||
| 24 | <style lang="stylus"> | ||
| 25 | @require '../styles/wrapper.styl' | ||
| 26 | |||
| 27 | .page | ||
| 28 | padding-bottom 2rem | ||
| 29 | display block | ||
| 30 | |||
| 31 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/PageEdit.vue b/doc/docs/.vuepress/theme/components/PageEdit.vue new file mode 100644 index 0000000..cf9b2d2 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/PageEdit.vue | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | <template> | ||
| 2 | <footer class="page-edit"> | ||
| 3 | <div | ||
| 4 | v-if="editLink" | ||
| 5 | class="edit-link" | ||
| 6 | > | ||
| 7 | <a | ||
| 8 | :href="editLink" | ||
| 9 | target="_blank" | ||
| 10 | rel="noopener noreferrer" | ||
| 11 | >{{ editLinkText }}</a> | ||
| 12 | <OutboundLink /> | ||
| 13 | </div> | ||
| 14 | |||
| 15 | <div | ||
| 16 | v-if="lastUpdated" | ||
| 17 | class="last-updated" | ||
| 18 | > | ||
| 19 | <span class="prefix">{{ lastUpdatedText }}:</span> | ||
| 20 | <span class="time">{{ lastUpdated }}</span> | ||
| 21 | </div> | ||
| 22 | </footer> | ||
| 23 | </template> | ||
| 24 | |||
| 25 | <script> | ||
| 26 | import isNil from 'lodash/isNil' | ||
| 27 | import { endingSlashRE, outboundRE } from '../util' | ||
| 28 | |||
| 29 | export default { | ||
| 30 | name: 'PageEdit', | ||
| 31 | |||
| 32 | computed: { | ||
| 33 | lastUpdated () { | ||
| 34 | return this.$page.lastUpdated | ||
| 35 | }, | ||
| 36 | |||
| 37 | lastUpdatedText () { | ||
| 38 | if (typeof this.$themeLocaleConfig.lastUpdated === 'string') { | ||
| 39 | return this.$themeLocaleConfig.lastUpdated | ||
| 40 | } | ||
| 41 | if (typeof this.$site.themeConfig.lastUpdated === 'string') { | ||
| 42 | return this.$site.themeConfig.lastUpdated | ||
| 43 | } | ||
| 44 | return 'Last Updated' | ||
| 45 | }, | ||
| 46 | |||
| 47 | editLink () { | ||
| 48 | const showEditLink = isNil(this.$page.frontmatter.editLink) | ||
| 49 | ? this.$site.themeConfig.editLinks | ||
| 50 | : this.$page.frontmatter.editLink | ||
| 51 | |||
| 52 | const { | ||
| 53 | repo, | ||
| 54 | docsDir = '', | ||
| 55 | docsBranch = 'master', | ||
| 56 | docsRepo = repo | ||
| 57 | } = this.$site.themeConfig | ||
| 58 | |||
| 59 | if (showEditLink && docsRepo && this.$page.relativePath) { | ||
| 60 | return this.createEditLink( | ||
| 61 | repo, | ||
| 62 | docsRepo, | ||
| 63 | docsDir, | ||
| 64 | docsBranch, | ||
| 65 | this.$page.relativePath | ||
| 66 | ) | ||
| 67 | } | ||
| 68 | return null | ||
| 69 | }, | ||
| 70 | |||
| 71 | editLinkText () { | ||
| 72 | return ( | ||
| 73 | this.$themeLocaleConfig.editLinkText | ||
| 74 | || this.$site.themeConfig.editLinkText | ||
| 75 | || `Edit this page` | ||
| 76 | ) | ||
| 77 | } | ||
| 78 | }, | ||
| 79 | |||
| 80 | methods: { | ||
| 81 | createEditLink (repo, docsRepo, docsDir, docsBranch, path) { | ||
| 82 | const bitbucket = /bitbucket.org/ | ||
| 83 | if (bitbucket.test(docsRepo)) { | ||
| 84 | const base = docsRepo | ||
| 85 | return ( | ||
| 86 | base.replace(endingSlashRE, '') | ||
| 87 | + `/src` | ||
| 88 | + `/${docsBranch}/` | ||
| 89 | + (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') | ||
| 90 | + path | ||
| 91 | + `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default` | ||
| 92 | ) | ||
| 93 | } | ||
| 94 | |||
| 95 | const gitlab = /gitlab.com/ | ||
| 96 | if (gitlab.test(docsRepo)) { | ||
| 97 | const base = docsRepo | ||
| 98 | return ( | ||
| 99 | base.replace(endingSlashRE, '') | ||
| 100 | + `/-/edit` | ||
| 101 | + `/${docsBranch}/` | ||
| 102 | + (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') | ||
| 103 | + path | ||
| 104 | ) | ||
| 105 | } | ||
| 106 | |||
| 107 | const base = outboundRE.test(docsRepo) | ||
| 108 | ? docsRepo | ||
| 109 | : `https://github.com/${docsRepo}` | ||
| 110 | return ( | ||
| 111 | base.replace(endingSlashRE, '') | ||
| 112 | + '/edit' | ||
| 113 | + `/${docsBranch}/` | ||
| 114 | + (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') | ||
| 115 | + path | ||
| 116 | ) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | </script> | ||
| 121 | |||
| 122 | <style lang="stylus"> | ||
| 123 | @require '../styles/wrapper.styl' | ||
| 124 | |||
| 125 | .page-edit | ||
| 126 | @extend $wrapper | ||
| 127 | padding-top 1rem | ||
| 128 | padding-bottom 1rem | ||
| 129 | overflow auto | ||
| 130 | |||
| 131 | .edit-link | ||
| 132 | display inline-block | ||
| 133 | a | ||
| 134 | color lighten($textColor, 25%) | ||
| 135 | margin-right 0.25rem | ||
| 136 | .last-updated | ||
| 137 | float right | ||
| 138 | font-size 0.9em | ||
| 139 | .prefix | ||
| 140 | font-weight 500 | ||
| 141 | color lighten($textColor, 25%) | ||
| 142 | .time | ||
| 143 | font-weight 400 | ||
| 144 | color #767676 | ||
| 145 | |||
| 146 | @media (max-width: $MQMobile) | ||
| 147 | .page-edit | ||
| 148 | .edit-link | ||
| 149 | margin-bottom 0.5rem | ||
| 150 | .last-updated | ||
| 151 | font-size 0.8em | ||
| 152 | float none | ||
| 153 | text-align left | ||
| 154 | |||
| 155 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/PageNav.vue b/doc/docs/.vuepress/theme/components/PageNav.vue new file mode 100644 index 0000000..4c19aae --- /dev/null +++ b/doc/docs/.vuepress/theme/components/PageNav.vue | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | <template> | ||
| 2 | <div | ||
| 3 | v-if="prev || next" | ||
| 4 | class="page-nav" | ||
| 5 | > | ||
| 6 | <p class="inner"> | ||
| 7 | <span | ||
| 8 | v-if="prev" | ||
| 9 | class="prev" | ||
| 10 | > | ||
| 11 | ← | ||
| 12 | <a | ||
| 13 | v-if="prev.type === 'external'" | ||
| 14 | class="prev" | ||
| 15 | :href="prev.path" | ||
| 16 | target="_blank" | ||
| 17 | rel="noopener noreferrer" | ||
| 18 | > | ||
| 19 | {{ prev.title || prev.path }} | ||
| 20 | |||
| 21 | <OutboundLink /> | ||
| 22 | </a> | ||
| 23 | |||
| 24 | <RouterLink | ||
| 25 | v-else | ||
| 26 | class="prev" | ||
| 27 | :to="prev.path" | ||
| 28 | > | ||
| 29 | {{ prev.title || prev.path }} | ||
| 30 | </RouterLink> | ||
| 31 | </span> | ||
| 32 | |||
| 33 | <span | ||
| 34 | v-if="next" | ||
| 35 | class="next" | ||
| 36 | > | ||
| 37 | <a | ||
| 38 | v-if="next.type === 'external'" | ||
| 39 | :href="next.path" | ||
| 40 | target="_blank" | ||
| 41 | rel="noopener noreferrer" | ||
| 42 | > | ||
| 43 | {{ next.title || next.path }} | ||
| 44 | |||
| 45 | <OutboundLink /> | ||
| 46 | </a> | ||
| 47 | |||
| 48 | <RouterLink | ||
| 49 | v-else | ||
| 50 | :to="next.path" | ||
| 51 | > | ||
| 52 | {{ next.title || next.path }} | ||
| 53 | </RouterLink> | ||
| 54 | → | ||
| 55 | </span> | ||
| 56 | </p> | ||
| 57 | </div> | ||
| 58 | </template> | ||
| 59 | |||
| 60 | <script> | ||
| 61 | import { resolvePage } from '../util' | ||
| 62 | import isString from 'lodash/isString' | ||
| 63 | import isNil from 'lodash/isNil' | ||
| 64 | |||
| 65 | export default { | ||
| 66 | name: 'PageNav', | ||
| 67 | |||
| 68 | props: ['sidebarItems'], | ||
| 69 | |||
| 70 | computed: { | ||
| 71 | prev () { | ||
| 72 | return resolvePageLink(LINK_TYPES.PREV, this) | ||
| 73 | }, | ||
| 74 | |||
| 75 | next () { | ||
| 76 | return resolvePageLink(LINK_TYPES.NEXT, this) | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | function resolvePrev (page, items) { | ||
| 82 | return find(page, items, -1) | ||
| 83 | } | ||
| 84 | |||
| 85 | function resolveNext (page, items) { | ||
| 86 | return find(page, items, 1) | ||
| 87 | } | ||
| 88 | |||
| 89 | const LINK_TYPES = { | ||
| 90 | NEXT: { | ||
| 91 | resolveLink: resolveNext, | ||
| 92 | getThemeLinkConfig: ({ nextLinks }) => nextLinks, | ||
| 93 | getPageLinkConfig: ({ frontmatter }) => frontmatter.next | ||
| 94 | }, | ||
| 95 | PREV: { | ||
| 96 | resolveLink: resolvePrev, | ||
| 97 | getThemeLinkConfig: ({ prevLinks }) => prevLinks, | ||
| 98 | getPageLinkConfig: ({ frontmatter }) => frontmatter.prev | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | function resolvePageLink ( | ||
| 103 | linkType, | ||
| 104 | { $themeConfig, $page, $route, $site, sidebarItems } | ||
| 105 | ) { | ||
| 106 | const { resolveLink, getThemeLinkConfig, getPageLinkConfig } = linkType | ||
| 107 | |||
| 108 | // Get link config from theme | ||
| 109 | const themeLinkConfig = getThemeLinkConfig($themeConfig) | ||
| 110 | |||
| 111 | // Get link config from current page | ||
| 112 | const pageLinkConfig = getPageLinkConfig($page) | ||
| 113 | |||
| 114 | // Page link config will overwrite global theme link config if defined | ||
| 115 | const link = isNil(pageLinkConfig) ? themeLinkConfig : pageLinkConfig | ||
| 116 | |||
| 117 | if (link === false) { | ||
| 118 | return | ||
| 119 | } else if (isString(link)) { | ||
| 120 | return resolvePage($site.pages, link, $route.path) | ||
| 121 | } else { | ||
| 122 | return resolveLink($page, sidebarItems) | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | function find (page, items, offset) { | ||
| 127 | const res = [] | ||
| 128 | flatten(items, res) | ||
| 129 | for (let i = 0; i < res.length; i++) { | ||
| 130 | const cur = res[i] | ||
| 131 | if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) { | ||
| 132 | return res[i + offset] | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | function flatten (items, res) { | ||
| 138 | for (let i = 0, l = items.length; i < l; i++) { | ||
| 139 | if (items[i].type === 'group') { | ||
| 140 | flatten(items[i].children || [], res) | ||
| 141 | } else { | ||
| 142 | res.push(items[i]) | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | </script> | ||
| 147 | |||
| 148 | <style lang="stylus"> | ||
| 149 | @require '../styles/wrapper.styl' | ||
| 150 | |||
| 151 | .page-nav | ||
| 152 | @extend $wrapper | ||
| 153 | padding-top 1rem | ||
| 154 | padding-bottom 0 | ||
| 155 | .inner | ||
| 156 | min-height 2rem | ||
| 157 | margin-top 0 | ||
| 158 | border-top 1px solid $borderColor | ||
| 159 | padding-top 1rem | ||
| 160 | overflow auto // clear float | ||
| 161 | .next | ||
| 162 | float right | ||
| 163 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/Sidebar.vue b/doc/docs/.vuepress/theme/components/Sidebar.vue new file mode 100644 index 0000000..e70e333 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/Sidebar.vue | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | <template> | ||
| 2 | <aside class="sidebar"> | ||
| 3 | <NavLinks /> | ||
| 4 | |||
| 5 | <slot name="top" /> | ||
| 6 | |||
| 7 | <SidebarLinks | ||
| 8 | :depth="0" | ||
| 9 | :items="items" | ||
| 10 | /> | ||
| 11 | <slot name="bottom" /> | ||
| 12 | </aside> | ||
| 13 | </template> | ||
| 14 | |||
| 15 | <script> | ||
| 16 | import SidebarLinks from '@theme/components/SidebarLinks.vue' | ||
| 17 | import NavLinks from '@theme/components/NavLinks.vue' | ||
| 18 | |||
| 19 | export default { | ||
| 20 | name: 'Sidebar', | ||
| 21 | |||
| 22 | components: { SidebarLinks, NavLinks }, | ||
| 23 | |||
| 24 | props: ['items'] | ||
| 25 | } | ||
| 26 | </script> | ||
| 27 | |||
| 28 | <style lang="stylus"> | ||
| 29 | .sidebar | ||
| 30 | ul | ||
| 31 | padding 0 | ||
| 32 | margin 0 | ||
| 33 | list-style-type none | ||
| 34 | a | ||
| 35 | display inline-block | ||
| 36 | .nav-links | ||
| 37 | display none | ||
| 38 | border-bottom 1px solid $borderColor | ||
| 39 | padding 0.5rem 0 0.75rem 0 | ||
| 40 | a | ||
| 41 | font-weight 600 | ||
| 42 | .nav-item, .repo-link | ||
| 43 | display block | ||
| 44 | line-height 1.25rem | ||
| 45 | font-size 1.1em | ||
| 46 | padding 0.5rem 0 0.5rem 1.5rem | ||
| 47 | & > .sidebar-links | ||
| 48 | padding 1.5rem 0 | ||
| 49 | & > li > a.sidebar-link | ||
| 50 | font-size 1.1em | ||
| 51 | line-height 1.7 | ||
| 52 | font-weight bold | ||
| 53 | & > li:not(:first-child) | ||
| 54 | margin-top .75rem | ||
| 55 | |||
| 56 | @media (max-width: $MQMobile) | ||
| 57 | .sidebar | ||
| 58 | .nav-links | ||
| 59 | display block | ||
| 60 | .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after | ||
| 61 | top calc(1rem - 2px) | ||
| 62 | & > .sidebar-links | ||
| 63 | padding 1rem 0 | ||
| 64 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/SidebarButton.vue b/doc/docs/.vuepress/theme/components/SidebarButton.vue new file mode 100644 index 0000000..3f54afd --- /dev/null +++ b/doc/docs/.vuepress/theme/components/SidebarButton.vue | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | <template> | ||
| 2 | <div | ||
| 3 | class="sidebar-button" | ||
| 4 | @click="$emit('toggle-sidebar')" | ||
| 5 | > | ||
| 6 | <svg | ||
| 7 | class="icon" | ||
| 8 | xmlns="http://www.w3.org/2000/svg" | ||
| 9 | aria-hidden="true" | ||
| 10 | role="img" | ||
| 11 | viewBox="0 0 448 512" | ||
| 12 | > | ||
| 13 | <path | ||
| 14 | fill="currentColor" | ||
| 15 | d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" | ||
| 16 | class="" | ||
| 17 | /> | ||
| 18 | </svg> | ||
| 19 | </div> | ||
| 20 | </template> | ||
| 21 | |||
| 22 | <style lang="stylus"> | ||
| 23 | .sidebar-button | ||
| 24 | cursor pointer | ||
| 25 | display none | ||
| 26 | width 1.25rem | ||
| 27 | height 1.25rem | ||
| 28 | position absolute | ||
| 29 | padding 0.6rem | ||
| 30 | top 0.6rem | ||
| 31 | left 1rem | ||
| 32 | .icon | ||
| 33 | display block | ||
| 34 | width 1.25rem | ||
| 35 | height 1.25rem | ||
| 36 | |||
| 37 | @media (max-width: $MQMobile) | ||
| 38 | .sidebar-button | ||
| 39 | display block | ||
| 40 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/SidebarGroup.vue b/doc/docs/.vuepress/theme/components/SidebarGroup.vue new file mode 100644 index 0000000..d7f1929 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/SidebarGroup.vue | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | <template> | ||
| 2 | <section | ||
| 3 | class="sidebar-group" | ||
| 4 | :class="[ | ||
| 5 | { | ||
| 6 | collapsable, | ||
| 7 | 'is-sub-group': depth !== 0 | ||
| 8 | }, | ||
| 9 | `depth-${depth}` | ||
| 10 | ]" | ||
| 11 | > | ||
| 12 | <RouterLink | ||
| 13 | v-if="item.path" | ||
| 14 | class="sidebar-heading clickable" | ||
| 15 | :class="{ | ||
| 16 | open, | ||
| 17 | 'active': isActive($route, item.path) | ||
| 18 | }" | ||
| 19 | :to="item.path" | ||
| 20 | @click.native="$emit('toggle')" | ||
| 21 | > | ||
| 22 | <span>{{ item.title }}</span> | ||
| 23 | <span | ||
| 24 | v-if="collapsable" | ||
| 25 | class="arrow" | ||
| 26 | :class="open ? 'down' : 'right'" | ||
| 27 | /> | ||
| 28 | </RouterLink> | ||
| 29 | |||
| 30 | <p | ||
| 31 | v-else | ||
| 32 | class="sidebar-heading" | ||
| 33 | :class="{ open }" | ||
| 34 | @click="$emit('toggle')" | ||
| 35 | > | ||
| 36 | <span>{{ item.title }}</span> | ||
| 37 | <span | ||
| 38 | v-if="collapsable" | ||
| 39 | class="arrow" | ||
| 40 | :class="open ? 'down' : 'right'" | ||
| 41 | /> | ||
| 42 | </p> | ||
| 43 | |||
| 44 | <DropdownTransition> | ||
| 45 | <SidebarLinks | ||
| 46 | v-if="open || !collapsable" | ||
| 47 | class="sidebar-group-items" | ||
| 48 | :items="item.children" | ||
| 49 | :sidebar-depth="item.sidebarDepth" | ||
| 50 | :initial-open-group-index="item.initialOpenGroupIndex" | ||
| 51 | :depth="depth + 1" | ||
| 52 | /> | ||
| 53 | </DropdownTransition> | ||
| 54 | </section> | ||
| 55 | </template> | ||
| 56 | |||
| 57 | <script> | ||
| 58 | import { isActive } from '../util' | ||
| 59 | import DropdownTransition from '@theme/components/DropdownTransition.vue' | ||
| 60 | |||
| 61 | export default { | ||
| 62 | name: 'SidebarGroup', | ||
| 63 | |||
| 64 | components: { | ||
| 65 | DropdownTransition | ||
| 66 | }, | ||
| 67 | |||
| 68 | props: [ | ||
| 69 | 'item', | ||
| 70 | 'open', | ||
| 71 | 'collapsable', | ||
| 72 | 'depth' | ||
| 73 | ], | ||
| 74 | |||
| 75 | // ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components | ||
| 76 | beforeCreate () { | ||
| 77 | this.$options.components.SidebarLinks = require('@theme/components/SidebarLinks.vue').default | ||
| 78 | }, | ||
| 79 | |||
| 80 | methods: { isActive } | ||
| 81 | } | ||
| 82 | </script> | ||
| 83 | |||
| 84 | <style lang="stylus"> | ||
| 85 | .sidebar-group | ||
| 86 | .sidebar-group | ||
| 87 | padding-left 0.5em | ||
| 88 | &:not(.collapsable) | ||
| 89 | .sidebar-heading:not(.clickable) | ||
| 90 | cursor auto | ||
| 91 | color inherit | ||
| 92 | // refine styles of nested sidebar groups | ||
| 93 | &.is-sub-group | ||
| 94 | padding-left 0 | ||
| 95 | & > .sidebar-heading | ||
| 96 | font-size 0.95em | ||
| 97 | line-height 1.4 | ||
| 98 | font-weight normal | ||
| 99 | padding-left 2rem | ||
| 100 | &:not(.clickable) | ||
| 101 | opacity 0.5 | ||
| 102 | & > .sidebar-group-items | ||
| 103 | padding-left 1rem | ||
| 104 | & > li > .sidebar-link | ||
| 105 | font-size: 0.95em; | ||
| 106 | border-left none | ||
| 107 | &.depth-2 | ||
| 108 | & > .sidebar-heading | ||
| 109 | border-left none | ||
| 110 | |||
| 111 | .sidebar-heading | ||
| 112 | color $textColor | ||
| 113 | transition color .15s ease | ||
| 114 | cursor pointer | ||
| 115 | font-size 1.1em | ||
| 116 | font-weight bold | ||
| 117 | // text-transform uppercase | ||
| 118 | padding 0.35rem 1.5rem 0.35rem 1.25rem | ||
| 119 | width 100% | ||
| 120 | box-sizing border-box | ||
| 121 | margin 0 | ||
| 122 | border-left 0.25rem solid transparent | ||
| 123 | &.open, &:hover | ||
| 124 | color inherit | ||
| 125 | .arrow | ||
| 126 | position relative | ||
| 127 | top -0.12em | ||
| 128 | left 0.5em | ||
| 129 | &.clickable | ||
| 130 | &.active | ||
| 131 | font-weight 600 | ||
| 132 | color $accentColor | ||
| 133 | border-left-color $accentColor | ||
| 134 | &:hover | ||
| 135 | color $accentColor | ||
| 136 | |||
| 137 | .sidebar-group-items | ||
| 138 | transition height .1s ease-out | ||
| 139 | font-size 0.95em | ||
| 140 | overflow hidden | ||
| 141 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/SidebarLink.vue b/doc/docs/.vuepress/theme/components/SidebarLink.vue new file mode 100644 index 0000000..4cd7665 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/SidebarLink.vue | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | <script> | ||
| 2 | import { isActive, hashRE, groupHeaders } from '../util' | ||
| 3 | |||
| 4 | export default { | ||
| 5 | functional: true, | ||
| 6 | |||
| 7 | props: ['item', 'sidebarDepth'], | ||
| 8 | |||
| 9 | render (h, | ||
| 10 | { | ||
| 11 | parent: { | ||
| 12 | $page, | ||
| 13 | $site, | ||
| 14 | $route, | ||
| 15 | $themeConfig, | ||
| 16 | $themeLocaleConfig | ||
| 17 | }, | ||
| 18 | props: { | ||
| 19 | item, | ||
| 20 | sidebarDepth | ||
| 21 | } | ||
| 22 | }) { | ||
| 23 | // use custom active class matching logic | ||
| 24 | // due to edge case of paths ending with / + hash | ||
| 25 | const selfActive = isActive($route, item.path) | ||
| 26 | // for sidebar: auto pages, a hash link should be active if one of its child | ||
| 27 | // matches | ||
| 28 | const active = item.type === 'auto' | ||
| 29 | ? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug)) | ||
| 30 | : selfActive | ||
| 31 | const link = item.type === 'external' | ||
| 32 | ? renderExternal(h, item.path, item.title || item.path) | ||
| 33 | : renderLink(h, item.path, item.title || item.path, active) | ||
| 34 | |||
| 35 | const maxDepth = [ | ||
| 36 | $page.frontmatter.sidebarDepth, | ||
| 37 | sidebarDepth, | ||
| 38 | $themeLocaleConfig.sidebarDepth, | ||
| 39 | $themeConfig.sidebarDepth, | ||
| 40 | 1 | ||
| 41 | ].find(depth => depth !== undefined) | ||
| 42 | |||
| 43 | const displayAllHeaders = $themeLocaleConfig.displayAllHeaders | ||
| 44 | || $themeConfig.displayAllHeaders | ||
| 45 | |||
| 46 | if (item.type === 'auto') { | ||
| 47 | return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)] | ||
| 48 | } else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) { | ||
| 49 | const children = groupHeaders(item.headers) | ||
| 50 | return [link, renderChildren(h, children, item.path, $route, maxDepth)] | ||
| 51 | } else { | ||
| 52 | return link | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | function renderLink (h, to, text, active, level) { | ||
| 58 | const component = { | ||
| 59 | props: { | ||
| 60 | to, | ||
| 61 | activeClass: '', | ||
| 62 | exactActiveClass: '' | ||
| 63 | }, | ||
| 64 | class: { | ||
| 65 | active, | ||
| 66 | 'sidebar-link': true | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | if (level > 2) { | ||
| 71 | component.style = { | ||
| 72 | 'padding-left': level + 'rem' | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | return h('RouterLink', component, text) | ||
| 77 | } | ||
| 78 | |||
| 79 | function renderChildren (h, children, path, route, maxDepth, depth = 1) { | ||
| 80 | if (!children || depth > maxDepth) return null | ||
| 81 | return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => { | ||
| 82 | const active = isActive(route, path + '#' + c.slug) | ||
| 83 | return h('li', { class: 'sidebar-sub-header' }, [ | ||
| 84 | renderLink(h, path + '#' + c.slug, c.title, active, c.level - 1), | ||
| 85 | renderChildren(h, c.children, path, route, maxDepth, depth + 1) | ||
| 86 | ]) | ||
| 87 | })) | ||
| 88 | } | ||
| 89 | |||
| 90 | function renderExternal (h, to, text) { | ||
| 91 | return h('a', { | ||
| 92 | attrs: { | ||
| 93 | href: to, | ||
| 94 | target: '_blank', | ||
| 95 | rel: 'noopener noreferrer' | ||
| 96 | }, | ||
| 97 | class: { | ||
| 98 | 'sidebar-link': true | ||
| 99 | } | ||
| 100 | }, [text, h('OutboundLink')]) | ||
| 101 | } | ||
| 102 | </script> | ||
| 103 | |||
| 104 | <style lang="stylus"> | ||
| 105 | .sidebar .sidebar-sub-headers | ||
| 106 | padding-left 1rem | ||
| 107 | font-size 0.95em | ||
| 108 | |||
| 109 | a.sidebar-link | ||
| 110 | font-size 1em | ||
| 111 | font-weight 400 | ||
| 112 | display inline-block | ||
| 113 | color $textColor | ||
| 114 | border-left 0.25rem solid transparent | ||
| 115 | padding 0.35rem 1rem 0.35rem 1.25rem | ||
| 116 | line-height 1.4 | ||
| 117 | width: 100% | ||
| 118 | box-sizing: border-box | ||
| 119 | &:hover | ||
| 120 | color $accentColor | ||
| 121 | &.active | ||
| 122 | font-weight 600 | ||
| 123 | color $accentColor | ||
| 124 | border-left-color $accentColor | ||
| 125 | .sidebar-group & | ||
| 126 | padding-left 2rem | ||
| 127 | .sidebar-sub-headers & | ||
| 128 | padding-top 0.25rem | ||
| 129 | padding-bottom 0.25rem | ||
| 130 | border-left none | ||
| 131 | &.active | ||
| 132 | font-weight 500 | ||
| 133 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/components/SidebarLinks.vue b/doc/docs/.vuepress/theme/components/SidebarLinks.vue new file mode 100644 index 0000000..55e6288 --- /dev/null +++ b/doc/docs/.vuepress/theme/components/SidebarLinks.vue | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | <template> | ||
| 2 | <ul | ||
| 3 | v-if="items.length" | ||
| 4 | class="sidebar-links" | ||
| 5 | > | ||
| 6 | <li | ||
| 7 | v-for="(item, i) in items" | ||
| 8 | :key="i" | ||
| 9 | > | ||
| 10 | <SidebarGroup | ||
| 11 | v-if="item.type === 'group'" | ||
| 12 | :item="item" | ||
| 13 | :open="i === openGroupIndex" | ||
| 14 | :collapsable="item.collapsable || item.collapsible" | ||
| 15 | :depth="depth" | ||
| 16 | @toggle="toggleGroup(i)" | ||
| 17 | /> | ||
| 18 | <SidebarLink | ||
| 19 | v-else | ||
| 20 | :sidebar-depth="sidebarDepth" | ||
| 21 | :item="item" | ||
| 22 | /> | ||
| 23 | </li> | ||
| 24 | </ul> | ||
| 25 | </template> | ||
| 26 | |||
| 27 | <script> | ||
| 28 | import SidebarGroup from '@theme/components/SidebarGroup.vue' | ||
| 29 | import SidebarLink from '@theme/components/SidebarLink.vue' | ||
| 30 | import { isActive } from '../util' | ||
| 31 | |||
| 32 | export default { | ||
| 33 | name: 'SidebarLinks', | ||
| 34 | |||
| 35 | components: { SidebarGroup, SidebarLink }, | ||
| 36 | |||
| 37 | props: [ | ||
| 38 | 'items', | ||
| 39 | 'depth', // depth of current sidebar links | ||
| 40 | 'sidebarDepth', // depth of headers to be extracted | ||
| 41 | 'initialOpenGroupIndex' | ||
| 42 | ], | ||
| 43 | |||
| 44 | data () { | ||
| 45 | return { | ||
| 46 | openGroupIndex: this.initialOpenGroupIndex || 0 | ||
| 47 | } | ||
| 48 | }, | ||
| 49 | |||
| 50 | watch: { | ||
| 51 | '$route' () { | ||
| 52 | this.refreshIndex() | ||
| 53 | } | ||
| 54 | }, | ||
| 55 | |||
| 56 | created () { | ||
| 57 | this.refreshIndex() | ||
| 58 | }, | ||
| 59 | |||
| 60 | methods: { | ||
| 61 | refreshIndex () { | ||
| 62 | const index = resolveOpenGroupIndex( | ||
| 63 | this.$route, | ||
| 64 | this.items | ||
| 65 | ) | ||
| 66 | if (index > -1) { | ||
| 67 | this.openGroupIndex = index | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | |||
| 71 | toggleGroup (index) { | ||
| 72 | this.openGroupIndex = index === this.openGroupIndex ? -1 : index | ||
| 73 | }, | ||
| 74 | |||
| 75 | isActive (page) { | ||
| 76 | return isActive(this.$route, page.regularPath) | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | function resolveOpenGroupIndex (route, items) { | ||
| 82 | for (let i = 0; i < items.length; i++) { | ||
| 83 | const item = items[i] | ||
| 84 | if (descendantIsActive(route, item)) { | ||
| 85 | return i | ||
| 86 | } | ||
| 87 | } | ||
| 88 | return -1 | ||
| 89 | } | ||
| 90 | |||
| 91 | function descendantIsActive (route, item) { | ||
| 92 | if (item.type === 'group') { | ||
| 93 | const childIsActive = item.path && isActive(route, item.path) | ||
| 94 | const grandChildIsActive = item.children.some(child => { | ||
| 95 | if (child.type === 'group') { | ||
| 96 | return descendantIsActive(route, child) | ||
| 97 | } else { | ||
| 98 | return child.type === 'page' && isActive(route, child.path) | ||
| 99 | } | ||
| 100 | }) | ||
| 101 | |||
| 102 | return childIsActive || grandChildIsActive | ||
| 103 | } | ||
| 104 | return false | ||
| 105 | } | ||
| 106 | </script> | ||
diff --git a/doc/docs/.vuepress/theme/global-components/Badge.vue b/doc/docs/.vuepress/theme/global-components/Badge.vue new file mode 100644 index 0000000..53951f9 --- /dev/null +++ b/doc/docs/.vuepress/theme/global-components/Badge.vue | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | <script> | ||
| 2 | export default { | ||
| 3 | functional: true, | ||
| 4 | props: { | ||
| 5 | type: { | ||
| 6 | type: String, | ||
| 7 | default: 'tip' | ||
| 8 | }, | ||
| 9 | text: String, | ||
| 10 | vertical: { | ||
| 11 | type: String, | ||
| 12 | default: 'top' | ||
| 13 | } | ||
| 14 | }, | ||
| 15 | render (h, { props, slots }) { | ||
| 16 | return h('span', { | ||
| 17 | class: ['badge', props.type], | ||
| 18 | style: { | ||
| 19 | verticalAlign: props.vertical | ||
| 20 | } | ||
| 21 | }, props.text || slots().default) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | </script> | ||
| 25 | |||
| 26 | <style lang="stylus" scoped> | ||
| 27 | .badge | ||
| 28 | display inline-block | ||
| 29 | font-size 14px | ||
| 30 | height 18px | ||
| 31 | line-height 18px | ||
| 32 | border-radius 3px | ||
| 33 | padding 0 6px | ||
| 34 | color white | ||
| 35 | background-color #42b983 | ||
| 36 | &.tip, &.green | ||
| 37 | background-color $badgeTipColor | ||
| 38 | &.error | ||
| 39 | background-color $badgeErrorColor | ||
| 40 | &.warning, &.warn, &.yellow | ||
| 41 | background-color $badgeWarningColor | ||
| 42 | & + & | ||
| 43 | margin-left 5px | ||
| 44 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/global-components/CodeBlock.vue b/doc/docs/.vuepress/theme/global-components/CodeBlock.vue new file mode 100644 index 0000000..d59d85b --- /dev/null +++ b/doc/docs/.vuepress/theme/global-components/CodeBlock.vue | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | <template> | ||
| 2 | <div | ||
| 3 | class="theme-code-block" | ||
| 4 | :class="{ 'theme-code-block__active': active }" | ||
| 5 | > | ||
| 6 | <slot /> | ||
| 7 | </div> | ||
| 8 | </template> | ||
| 9 | |||
| 10 | <script> | ||
| 11 | export default { | ||
| 12 | name: 'CodeBlock', | ||
| 13 | props: { | ||
| 14 | title: { | ||
| 15 | type: String, | ||
| 16 | required: true | ||
| 17 | }, | ||
| 18 | active: { | ||
| 19 | type: Boolean, | ||
| 20 | default: false | ||
| 21 | } | ||
| 22 | }, | ||
| 23 | mounted () { | ||
| 24 | if (this.$parent && this.$parent.loadTabs) { | ||
| 25 | this.$parent.loadTabs() | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | </script> | ||
| 30 | |||
| 31 | <style scoped> | ||
| 32 | .theme-code-block { | ||
| 33 | display: none; | ||
| 34 | } | ||
| 35 | .theme-code-block__active { | ||
| 36 | display: block; | ||
| 37 | } | ||
| 38 | .theme-code-block > pre { | ||
| 39 | background-color: orange; | ||
| 40 | } | ||
| 41 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/global-components/CodeGroup.vue b/doc/docs/.vuepress/theme/global-components/CodeGroup.vue new file mode 100644 index 0000000..ac6ec55 --- /dev/null +++ b/doc/docs/.vuepress/theme/global-components/CodeGroup.vue | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | <template> | ||
| 2 | <ClientOnly> | ||
| 3 | <div class="theme-code-group"> | ||
| 4 | <div class="theme-code-group__nav"> | ||
| 5 | <ul class="theme-code-group__ul"> | ||
| 6 | <li | ||
| 7 | v-for="(tab, i) in codeTabs" | ||
| 8 | :key="tab.title" | ||
| 9 | class="theme-code-group__li" | ||
| 10 | > | ||
| 11 | <button | ||
| 12 | class="theme-code-group__nav-tab" | ||
| 13 | :class="{ | ||
| 14 | 'theme-code-group__nav-tab-active': i === activeCodeTabIndex, | ||
| 15 | }" | ||
| 16 | @click="changeCodeTab(i)" | ||
| 17 | > | ||
| 18 | {{ tab.title }} | ||
| 19 | </button> | ||
| 20 | </li> | ||
| 21 | </ul> | ||
| 22 | </div> | ||
| 23 | <slot /> | ||
| 24 | <pre | ||
| 25 | v-if="codeTabs.length < 1" | ||
| 26 | class="pre-blank" | ||
| 27 | >// Make sure to add code blocks to your code group</pre> | ||
| 28 | </div> | ||
| 29 | </ClientOnly> | ||
| 30 | </template> | ||
| 31 | |||
| 32 | <script> | ||
| 33 | export default { | ||
| 34 | name: 'CodeGroup', | ||
| 35 | data () { | ||
| 36 | return { | ||
| 37 | codeTabs: [], | ||
| 38 | activeCodeTabIndex: -1 | ||
| 39 | } | ||
| 40 | }, | ||
| 41 | watch: { | ||
| 42 | activeCodeTabIndex (index) { | ||
| 43 | this.activateCodeTab(index) | ||
| 44 | } | ||
| 45 | }, | ||
| 46 | mounted () { | ||
| 47 | this.loadTabs() | ||
| 48 | }, | ||
| 49 | methods: { | ||
| 50 | changeCodeTab (index) { | ||
| 51 | this.activeCodeTabIndex = index | ||
| 52 | }, | ||
| 53 | loadTabs () { | ||
| 54 | this.codeTabs = (this.$slots.default || []).filter(slot => Boolean(slot.componentOptions)).map((slot, index) => { | ||
| 55 | if (slot.componentOptions.propsData.active === '') { | ||
| 56 | this.activeCodeTabIndex = index | ||
| 57 | } | ||
| 58 | |||
| 59 | return { | ||
| 60 | title: slot.componentOptions.propsData.title, | ||
| 61 | elm: slot.elm | ||
| 62 | } | ||
| 63 | }) | ||
| 64 | |||
| 65 | if (this.activeCodeTabIndex === -1 && this.codeTabs.length > 0) { | ||
| 66 | this.activeCodeTabIndex = 0 | ||
| 67 | } | ||
| 68 | |||
| 69 | this.activateCodeTab(0) | ||
| 70 | }, | ||
| 71 | activateCodeTab (index) { | ||
| 72 | this.codeTabs.forEach(tab => { | ||
| 73 | if (tab.elm) { | ||
| 74 | tab.elm.classList.remove('theme-code-block__active') | ||
| 75 | } | ||
| 76 | }) | ||
| 77 | |||
| 78 | if (this.codeTabs[index].elm) { | ||
| 79 | this.codeTabs[index].elm.classList.add('theme-code-block__active') | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | </script> | ||
| 85 | |||
| 86 | <style lang="stylus" scoped> | ||
| 87 | .theme-code-group {} | ||
| 88 | .theme-code-group__nav { | ||
| 89 | margin-bottom: -35px; | ||
| 90 | background-color: $codeBgColor; | ||
| 91 | padding-bottom: 22px; | ||
| 92 | border-top-left-radius: 6px; | ||
| 93 | border-top-right-radius: 6px; | ||
| 94 | padding-left: 10px; | ||
| 95 | padding-top: 10px; | ||
| 96 | } | ||
| 97 | .theme-code-group__ul { | ||
| 98 | margin: auto 0; | ||
| 99 | padding-left: 0; | ||
| 100 | display: inline-flex; | ||
| 101 | list-style: none; | ||
| 102 | } | ||
| 103 | .theme-code-group__li {} | ||
| 104 | .theme-code-group__nav-tab { | ||
| 105 | border: 0; | ||
| 106 | padding: 5px; | ||
| 107 | cursor: pointer; | ||
| 108 | background-color: transparent; | ||
| 109 | font-size: 0.85em; | ||
| 110 | line-height: 1.4; | ||
| 111 | color: rgba(255, 255, 255, 0.9); | ||
| 112 | font-weight: 600; | ||
| 113 | } | ||
| 114 | .theme-code-group__nav-tab-active { | ||
| 115 | border-bottom: #42b983 1px solid; | ||
| 116 | } | ||
| 117 | .pre-blank { | ||
| 118 | color: #42b983; | ||
| 119 | } | ||
| 120 | </style> | ||
diff --git a/doc/docs/.vuepress/theme/index.js b/doc/docs/.vuepress/theme/index.js new file mode 100644 index 0000000..baaf102 --- /dev/null +++ b/doc/docs/.vuepress/theme/index.js | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | const path = require('path') | ||
| 2 | |||
| 3 | // Theme API. | ||
| 4 | module.exports = (options, ctx) => { | ||
| 5 | const { themeConfig, siteConfig } = ctx | ||
| 6 | |||
| 7 | // resolve algolia | ||
| 8 | const isAlgoliaSearch = ( | ||
| 9 | themeConfig.algolia | ||
| 10 | || Object | ||
| 11 | .keys(siteConfig.locales && themeConfig.locales || {}) | ||
| 12 | .some(base => themeConfig.locales[base].algolia) | ||
| 13 | ) | ||
| 14 | |||
| 15 | const enableSmoothScroll = themeConfig.smoothScroll === true | ||
| 16 | |||
| 17 | return { | ||
| 18 | alias () { | ||
| 19 | return { | ||
| 20 | '@AlgoliaSearchBox': isAlgoliaSearch | ||
| 21 | ? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue') | ||
| 22 | : path.resolve(__dirname, 'noopModule.js') | ||
| 23 | } | ||
| 24 | }, | ||
| 25 | |||
| 26 | plugins: [ | ||
| 27 | ['@vuepress/active-header-links', options.activeHeaderLinks], | ||
| 28 | '@vuepress/search', | ||
| 29 | '@vuepress/plugin-nprogress', | ||
| 30 | ['container', { | ||
| 31 | type: 'tip', | ||
| 32 | defaultTitle: { | ||
| 33 | '/': 'TIP', | ||
| 34 | '/zh/': '提示' | ||
| 35 | } | ||
| 36 | }], | ||
| 37 | ['container', { | ||
| 38 | type: 'warning', | ||
| 39 | defaultTitle: { | ||
| 40 | '/': 'WARNING', | ||
| 41 | '/zh/': '注意' | ||
| 42 | } | ||
| 43 | }], | ||
| 44 | ['container', { | ||
| 45 | type: 'danger', | ||
| 46 | defaultTitle: { | ||
| 47 | '/': 'WARNING', | ||
| 48 | '/zh/': '警告' | ||
| 49 | } | ||
| 50 | }], | ||
| 51 | ['container', { | ||
| 52 | type: 'details', | ||
| 53 | before: info => `<details class="custom-block details">${info ? `<summary>${info}</summary>` : ''}\n`, | ||
| 54 | after: () => '</details>\n' | ||
| 55 | }], | ||
| 56 | ['smooth-scroll', enableSmoothScroll] | ||
| 57 | ] | ||
| 58 | } | ||
| 59 | } | ||
diff --git a/doc/docs/.vuepress/theme/layouts/404.vue b/doc/docs/.vuepress/theme/layouts/404.vue new file mode 100644 index 0000000..2cbfa0f --- /dev/null +++ b/doc/docs/.vuepress/theme/layouts/404.vue | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | <template> | ||
| 2 | <div class="theme-container"> | ||
| 3 | <div class="theme-default-content"> | ||
| 4 | <h1>404</h1> | ||
| 5 | |||
| 6 | <blockquote>{{ getMsg() }}</blockquote> | ||
| 7 | |||
| 8 | <RouterLink to="/"> | ||
| 9 | Take me home. | ||
| 10 | </RouterLink> | ||
| 11 | </div> | ||
| 12 | </div> | ||
| 13 | </template> | ||
| 14 | |||
| 15 | <script> | ||
| 16 | const msgs = [ | ||
| 17 | `There's nothing here.`, | ||
| 18 | `How did we get here?`, | ||
| 19 | `That's a Four-Oh-Four.`, | ||
| 20 | `Looks like we've got some broken links.` | ||
| 21 | ] | ||
| 22 | |||
| 23 | export default { | ||
| 24 | methods: { | ||
| 25 | getMsg () { | ||
| 26 | return msgs[Math.floor(Math.random() * msgs.length)] | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | </script> | ||
diff --git a/doc/docs/.vuepress/theme/layouts/Layout.vue b/doc/docs/.vuepress/theme/layouts/Layout.vue new file mode 100644 index 0000000..3298070 --- /dev/null +++ b/doc/docs/.vuepress/theme/layouts/Layout.vue | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | <template> | ||
| 2 | <div | ||
| 3 | class="theme-container" | ||
| 4 | :class="pageClasses" | ||
| 5 | @touchstart="onTouchStart" | ||
| 6 | @touchend="onTouchEnd" | ||
| 7 | > | ||
| 8 | <Navbar | ||
| 9 | v-if="shouldShowNavbar" | ||
| 10 | @toggle-sidebar="toggleSidebar" | ||
| 11 | /> | ||
| 12 | |||
| 13 | <div | ||
| 14 | class="sidebar-mask" | ||
| 15 | @click="toggleSidebar(false)" | ||
| 16 | /> | ||
| 17 | |||
| 18 | <Sidebar | ||
| 19 | :items="sidebarItems" | ||
| 20 | @toggle-sidebar="toggleSidebar" | ||
| 21 | > | ||
| 22 | <template #top> | ||
| 23 | <slot name="sidebar-top" /> | ||
| 24 | </template> | ||
| 25 | <template #bottom> | ||
| 26 | <slot name="sidebar-bottom" /> | ||
| 27 | </template> | ||
| 28 | </Sidebar> | ||
| 29 | |||
| 30 | <Home v-if="$page.frontmatter.home" /> | ||
| 31 | |||
| 32 | <Page | ||
| 33 | v-else | ||
| 34 | :sidebar-items="sidebarItems" | ||
| 35 | > | ||
| 36 | <template #top> | ||
| 37 | <slot name="page-top" /> | ||
| 38 | </template> | ||
| 39 | <template #bottom> | ||
| 40 | <slot name="page-bottom" /> | ||
| 41 | </template> | ||
| 42 | </Page> | ||
| 43 | </div> | ||
| 44 | </template> | ||
| 45 | |||
| 46 | <script> | ||
| 47 | import Home from '@theme/components/Home.vue' | ||
| 48 | import Navbar from '@theme/components/Navbar.vue' | ||
| 49 | import Page from '@theme/components/Page.vue' | ||
| 50 | import Sidebar from '@theme/components/Sidebar.vue' | ||
| 51 | import { resolveSidebarItems } from '../util' | ||
| 52 | |||
| 53 | export default { | ||
| 54 | name: 'Layout', | ||
| 55 | |||
| 56 | components: { | ||
| 57 | Home, | ||
| 58 | Page, | ||
| 59 | Sidebar, | ||
| 60 | Navbar | ||
| 61 | }, | ||
| 62 | |||
| 63 | data () { | ||
| 64 | return { | ||
| 65 | isSidebarOpen: false | ||
| 66 | } | ||
| 67 | }, | ||
| 68 | |||
| 69 | computed: { | ||
| 70 | shouldShowNavbar () { | ||
| 71 | const { themeConfig } = this.$site | ||
| 72 | const { frontmatter } = this.$page | ||
| 73 | if ( | ||
| 74 | frontmatter.navbar === false | ||
| 75 | || themeConfig.navbar === false) { | ||
| 76 | return false | ||
| 77 | } | ||
| 78 | return ( | ||
| 79 | this.$title | ||
| 80 | || themeConfig.logo | ||
| 81 | || themeConfig.repo | ||
| 82 | || themeConfig.nav | ||
| 83 | || this.$themeLocaleConfig.nav | ||
| 84 | ) | ||
| 85 | }, | ||
| 86 | |||
| 87 | shouldShowSidebar () { | ||
| 88 | const { frontmatter } = this.$page | ||
| 89 | return ( | ||
| 90 | !frontmatter.home | ||
| 91 | && frontmatter.sidebar !== false | ||
| 92 | && this.sidebarItems.length | ||
| 93 | ) | ||
| 94 | }, | ||
| 95 | |||
| 96 | sidebarItems () { | ||
| 97 | return resolveSidebarItems( | ||
| 98 | this.$page, | ||
| 99 | this.$page.regularPath, | ||
| 100 | this.$site, | ||
| 101 | this.$localePath | ||
| 102 | ) | ||
| 103 | }, | ||
| 104 | |||
| 105 | pageClasses () { | ||
| 106 | const userPageClass = this.$page.frontmatter.pageClass | ||
| 107 | return [ | ||
| 108 | { | ||
| 109 | 'no-navbar': !this.shouldShowNavbar, | ||
| 110 | 'sidebar-open': this.isSidebarOpen, | ||
| 111 | 'no-sidebar': !this.shouldShowSidebar | ||
| 112 | }, | ||
| 113 | userPageClass | ||
| 114 | ] | ||
| 115 | } | ||
| 116 | }, | ||
| 117 | |||
| 118 | mounted () { | ||
| 119 | this.$router.afterEach(() => { | ||
| 120 | this.isSidebarOpen = false | ||
| 121 | }) | ||
| 122 | }, | ||
| 123 | |||
| 124 | methods: { | ||
| 125 | toggleSidebar (to) { | ||
| 126 | this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen | ||
| 127 | this.$emit('toggle-sidebar', this.isSidebarOpen) | ||
| 128 | }, | ||
| 129 | |||
| 130 | // side swipe | ||
| 131 | onTouchStart (e) { | ||
| 132 | this.touchStart = { | ||
| 133 | x: e.changedTouches[0].clientX, | ||
| 134 | y: e.changedTouches[0].clientY | ||
| 135 | } | ||
| 136 | }, | ||
| 137 | |||
| 138 | onTouchEnd (e) { | ||
| 139 | const dx = e.changedTouches[0].clientX - this.touchStart.x | ||
| 140 | const dy = e.changedTouches[0].clientY - this.touchStart.y | ||
| 141 | if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) { | ||
| 142 | if (dx > 0 && this.touchStart.x <= 80) { | ||
| 143 | this.toggleSidebar(true) | ||
| 144 | } else { | ||
| 145 | this.toggleSidebar(false) | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | </script> | ||
diff --git a/doc/docs/.vuepress/theme/noopModule.js b/doc/docs/.vuepress/theme/noopModule.js new file mode 100644 index 0000000..b1c6ea4 --- /dev/null +++ b/doc/docs/.vuepress/theme/noopModule.js | |||
| @@ -0,0 +1 @@ | |||
| export default {} | |||
diff --git a/doc/docs/.vuepress/theme/package.json b/doc/docs/.vuepress/theme/package.json new file mode 100644 index 0000000..d5dfc26 --- /dev/null +++ b/doc/docs/.vuepress/theme/package.json | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | { | ||
| 2 | "name": "@vuepress/theme-default", | ||
| 3 | "version": "1.8.2", | ||
| 4 | "description": "Default theme for VuePress", | ||
| 5 | "keywords": [ | ||
| 6 | "documentation", | ||
| 7 | "generator", | ||
| 8 | "vue", | ||
| 9 | "vuepress" | ||
| 10 | ], | ||
| 11 | "homepage": "https://github.com/vuejs/vuepress/blob/master/packages/@vuepress/theme-default#readme", | ||
| 12 | "bugs": { | ||
| 13 | "url": "https://github.com/vuejs/vuepress/issues" | ||
| 14 | }, | ||
| 15 | "repository": { | ||
| 16 | "type": "git", | ||
| 17 | "url": "git+https://github.com/vuejs/vuepress.git", | ||
| 18 | "directory": "packages/@vuepress/theme-default" | ||
| 19 | }, | ||
| 20 | "license": "MIT", | ||
| 21 | "author": "Evan You", | ||
| 22 | "main": "index.js", | ||
| 23 | "dependencies": { | ||
| 24 | "@vuepress/plugin-active-header-links": "1.8.2", | ||
| 25 | "@vuepress/plugin-nprogress": "1.8.2", | ||
| 26 | "@vuepress/plugin-search": "1.8.2", | ||
| 27 | "docsearch.js": "^2.5.2", | ||
| 28 | "lodash": "^4.17.15", | ||
| 29 | "stylus": "^0.54.8", | ||
| 30 | "stylus-loader": "^3.0.2", | ||
| 31 | "vuepress-plugin-container": "^2.0.2", | ||
| 32 | "vuepress-plugin-smooth-scroll": "^0.0.3" | ||
| 33 | }, | ||
| 34 | "publishConfig": { | ||
| 35 | "access": "public" | ||
| 36 | }, | ||
| 37 | "maintainers": [ | ||
| 38 | { | ||
| 39 | "name": "ULIVZ", | ||
| 40 | "email": "chl814@foxmail.com" | ||
| 41 | } | ||
| 42 | ], | ||
| 43 | "gitHead": "11eed0f0f105b97a7324cfa8e59d7d27bb966842" | ||
| 44 | } | ||
diff --git a/doc/docs/.vuepress/theme/styles/arrow.styl b/doc/docs/.vuepress/theme/styles/arrow.styl new file mode 100644 index 0000000..20bffc0 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/arrow.styl | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | @require './config' | ||
| 2 | |||
| 3 | .arrow | ||
| 4 | display inline-block | ||
| 5 | width 0 | ||
| 6 | height 0 | ||
| 7 | &.up | ||
| 8 | border-left 4px solid transparent | ||
| 9 | border-right 4px solid transparent | ||
| 10 | border-bottom 6px solid $arrowBgColor | ||
| 11 | &.down | ||
| 12 | border-left 4px solid transparent | ||
| 13 | border-right 4px solid transparent | ||
| 14 | border-top 6px solid $arrowBgColor | ||
| 15 | &.right | ||
| 16 | border-top 4px solid transparent | ||
| 17 | border-bottom 4px solid transparent | ||
| 18 | border-left 6px solid $arrowBgColor | ||
| 19 | &.left | ||
| 20 | border-top 4px solid transparent | ||
| 21 | border-bottom 4px solid transparent | ||
| 22 | border-right 6px solid $arrowBgColor | ||
diff --git a/doc/docs/.vuepress/theme/styles/code.styl b/doc/docs/.vuepress/theme/styles/code.styl new file mode 100644 index 0000000..0cb4081 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/code.styl | |||
| @@ -0,0 +1,176 @@ | |||
| 1 | /* | ||
| 2 | |||
| 3 | Name: Base16 Atelier Sulphurpool Light | ||
| 4 | Author: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool) | ||
| 5 | |||
| 6 | Prism template by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/prism/) | ||
| 7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) | ||
| 8 | |||
| 9 | */ | ||
| 10 | code[class*="language-"], | ||
| 11 | pre[class*="language-"] { | ||
| 12 | font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace; | ||
| 13 | font-size: 1em; | ||
| 14 | line-height: 1.375; | ||
| 15 | direction: ltr; | ||
| 16 | text-align: left; | ||
| 17 | white-space: pre; | ||
| 18 | word-spacing: normal; | ||
| 19 | word-break: normal; | ||
| 20 | -moz-tab-size: 4; | ||
| 21 | -o-tab-size: 4; | ||
| 22 | tab-size: 4; | ||
| 23 | -webkit-hyphens: none; | ||
| 24 | -moz-hyphens: none; | ||
| 25 | -ms-hyphens: none; | ||
| 26 | hyphens: none; | ||
| 27 | background: #f5f7ff; | ||
| 28 | color: #5e6687; | ||
| 29 | } | ||
| 30 | |||
| 31 | pre > code[class*="language-"] { | ||
| 32 | font-size: 1em; | ||
| 33 | } | ||
| 34 | |||
| 35 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, | ||
| 36 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { | ||
| 37 | text-shadow: none; | ||
| 38 | background: #dfe2f1; | ||
| 39 | } | ||
| 40 | |||
| 41 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, | ||
| 42 | code[class*="language-"]::selection, code[class*="language-"] ::selection { | ||
| 43 | text-shadow: none; | ||
| 44 | background: #dfe2f1; | ||
| 45 | } | ||
| 46 | |||
| 47 | /* Code blocks */ | ||
| 48 | pre[class*="language-"] { | ||
| 49 | padding: 1em; | ||
| 50 | margin: .5em 0; | ||
| 51 | overflow: auto; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Inline code */ | ||
| 55 | :not(pre) > code[class*="language-"] { | ||
| 56 | padding: .1em; | ||
| 57 | border-radius: .3em; | ||
| 58 | } | ||
| 59 | |||
| 60 | .token.comment, | ||
| 61 | .token.prolog, | ||
| 62 | .token.doctype, | ||
| 63 | .token.cdata { | ||
| 64 | color: #898ea4; | ||
| 65 | } | ||
| 66 | |||
| 67 | .token.punctuation { | ||
| 68 | color: #5e6687; | ||
| 69 | } | ||
| 70 | |||
| 71 | .token.namespace { | ||
| 72 | opacity: .7; | ||
| 73 | } | ||
| 74 | |||
| 75 | .token.operator, | ||
| 76 | .token.boolean, | ||
| 77 | .token.number { | ||
| 78 | color: #c76b29; | ||
| 79 | } | ||
| 80 | |||
| 81 | .token.property { | ||
| 82 | color: #c08b30; | ||
| 83 | } | ||
| 84 | |||
| 85 | .token.tag { | ||
| 86 | color: #3d8fd1; | ||
| 87 | } | ||
| 88 | |||
| 89 | .token.string { | ||
| 90 | color: #22a2c9; | ||
| 91 | } | ||
| 92 | |||
| 93 | .token.selector { | ||
| 94 | color: #6679cc; | ||
| 95 | } | ||
| 96 | |||
| 97 | .token.attr-name { | ||
| 98 | color: #c76b29; | ||
| 99 | } | ||
| 100 | |||
| 101 | .token.entity, | ||
| 102 | .token.url, | ||
| 103 | .language-css .token.string, | ||
| 104 | .style .token.string { | ||
| 105 | color: #22a2c9; | ||
| 106 | } | ||
| 107 | |||
| 108 | .token.attr-value, | ||
| 109 | .token.keyword, | ||
| 110 | .token.control, | ||
| 111 | .token.directive, | ||
| 112 | .token.unit { | ||
| 113 | color: #ac9739; | ||
| 114 | } | ||
| 115 | |||
| 116 | .token.statement, | ||
| 117 | .token.regex, | ||
| 118 | .token.atrule { | ||
| 119 | color: #22a2c9; | ||
| 120 | } | ||
| 121 | |||
| 122 | .token.placeholder, | ||
| 123 | .token.variable { | ||
| 124 | color: #3d8fd1; | ||
| 125 | } | ||
| 126 | |||
| 127 | .token.deleted { | ||
| 128 | text-decoration: line-through; | ||
| 129 | } | ||
| 130 | |||
| 131 | .token.inserted { | ||
| 132 | border-bottom: 1px dotted #202746; | ||
| 133 | text-decoration: none; | ||
| 134 | } | ||
| 135 | |||
| 136 | .token.italic { | ||
| 137 | font-style: italic; | ||
| 138 | } | ||
| 139 | |||
| 140 | .token.important, | ||
| 141 | .token.bold { | ||
| 142 | font-weight: bold; | ||
| 143 | } | ||
| 144 | |||
| 145 | .token.important { | ||
| 146 | color: #c94922; | ||
| 147 | } | ||
| 148 | |||
| 149 | .token.entity { | ||
| 150 | cursor: help; | ||
| 151 | } | ||
| 152 | |||
| 153 | pre > code.highlight { | ||
| 154 | outline: 0.4em solid #c94922; | ||
| 155 | outline-offset: .4em; | ||
| 156 | } | ||
| 157 | |||
| 158 | /* overrides color-values for the Line Numbers plugin | ||
| 159 | * http://prismjs.com/plugins/line-numbers/ | ||
| 160 | */ | ||
| 161 | .line-numbers .line-numbers-rows { | ||
| 162 | border-right-color: #dfe2f1; | ||
| 163 | } | ||
| 164 | |||
| 165 | .line-numbers-rows > span:before { | ||
| 166 | color: #979db4; | ||
| 167 | } | ||
| 168 | |||
| 169 | /* overrides color-values for the Line Highlight plugin | ||
| 170 | * http://prismjs.com/plugins/line-highlight/ | ||
| 171 | */ | ||
| 172 | .line-highlight { | ||
| 173 | background: rgba(107, 115, 148, 0.2); | ||
| 174 | background: -webkit-linear-gradient(left, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); | ||
| 175 | background: linear-gradient(to right, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); | ||
| 176 | } | ||
diff --git a/doc/docs/.vuepress/theme/styles/config.styl b/doc/docs/.vuepress/theme/styles/config.styl new file mode 100644 index 0000000..9e40321 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/config.styl | |||
| @@ -0,0 +1 @@ | |||
| $contentClass = '.theme-default-content' | |||
diff --git a/doc/docs/.vuepress/theme/styles/custom-blocks.styl b/doc/docs/.vuepress/theme/styles/custom-blocks.styl new file mode 100644 index 0000000..5b86816 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/custom-blocks.styl | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | .custom-block | ||
| 2 | .custom-block-title | ||
| 3 | font-weight 600 | ||
| 4 | margin-bottom -0.4rem | ||
| 5 | &.tip, &.warning, &.danger | ||
| 6 | padding .1rem 1.5rem | ||
| 7 | border-left-width .5rem | ||
| 8 | border-left-style solid | ||
| 9 | margin 1rem 0 | ||
| 10 | &.tip | ||
| 11 | background-color #f3f5f7 | ||
| 12 | border-color #42b983 | ||
| 13 | &.warning | ||
| 14 | background-color rgba(255,229,100,.3) | ||
| 15 | border-color darken(#ffe564, 35%) | ||
| 16 | color darken(#ffe564, 70%) | ||
| 17 | .custom-block-title | ||
| 18 | color darken(#ffe564, 50%) | ||
| 19 | a | ||
| 20 | color $textColor | ||
| 21 | &.danger | ||
| 22 | background-color #ffe6e6 | ||
| 23 | border-color darken(red, 20%) | ||
| 24 | color darken(red, 70%) | ||
| 25 | .custom-block-title | ||
| 26 | color darken(red, 40%) | ||
| 27 | a | ||
| 28 | color $textColor | ||
| 29 | &.details | ||
| 30 | display block | ||
| 31 | position relative | ||
| 32 | border-radius 2px | ||
| 33 | margin 1.6em 0 | ||
| 34 | padding 1.6em | ||
| 35 | background-color #eee | ||
| 36 | h4 | ||
| 37 | margin-top 0 | ||
| 38 | figure, p | ||
| 39 | &:last-child | ||
| 40 | margin-bottom 0 | ||
| 41 | padding-bottom 0 | ||
| 42 | summary | ||
| 43 | outline none | ||
| 44 | cursor pointer | ||
diff --git a/doc/docs/.vuepress/theme/styles/index.styl b/doc/docs/.vuepress/theme/styles/index.styl new file mode 100644 index 0000000..ecef658 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/index.styl | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | @require './config' | ||
| 2 | @require './code' | ||
| 3 | @require './custom-blocks' | ||
| 4 | @require './arrow' | ||
| 5 | @require './wrapper' | ||
| 6 | @require './toc' | ||
| 7 | |||
| 8 | html, body | ||
| 9 | padding 0 | ||
| 10 | margin 0 | ||
| 11 | background-color #fff | ||
| 12 | |||
| 13 | body | ||
| 14 | font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif | ||
| 15 | -webkit-font-smoothing antialiased | ||
| 16 | -moz-osx-font-smoothing grayscale | ||
| 17 | font-size 16px | ||
| 18 | color $textColor | ||
| 19 | |||
| 20 | .page | ||
| 21 | padding-left $sidebarWidth | ||
| 22 | |||
| 23 | .navbar | ||
| 24 | position fixed | ||
| 25 | z-index 20 | ||
| 26 | top 0 | ||
| 27 | left 0 | ||
| 28 | right 0 | ||
| 29 | height $navbarHeight | ||
| 30 | background-color #fff | ||
| 31 | box-sizing border-box | ||
| 32 | border-bottom 1px solid $borderColor | ||
| 33 | |||
| 34 | .sidebar-mask | ||
| 35 | position fixed | ||
| 36 | z-index 9 | ||
| 37 | top 0 | ||
| 38 | left 0 | ||
| 39 | width 100vw | ||
| 40 | height 100vh | ||
| 41 | display none | ||
| 42 | |||
| 43 | .sidebar | ||
| 44 | font-size 16px | ||
| 45 | background-color #fff | ||
| 46 | width $sidebarWidth | ||
| 47 | position fixed | ||
| 48 | z-index 10 | ||
| 49 | margin 0 | ||
| 50 | top $navbarHeight | ||
| 51 | left 0 | ||
| 52 | bottom 0 | ||
| 53 | box-sizing border-box | ||
| 54 | border-right 1px solid $borderColor | ||
| 55 | overflow-y auto | ||
| 56 | |||
| 57 | {$contentClass}:not(.custom) | ||
| 58 | @extend $wrapper | ||
| 59 | > *:first-child | ||
| 60 | margin-top $navbarHeight | ||
| 61 | |||
| 62 | a:hover | ||
| 63 | text-decoration underline | ||
| 64 | |||
| 65 | p.demo | ||
| 66 | padding 1rem 1.5rem | ||
| 67 | border 1px solid #ddd | ||
| 68 | border-radius 4px | ||
| 69 | |||
| 70 | img | ||
| 71 | max-width 100% | ||
| 72 | |||
| 73 | {$contentClass}.custom | ||
| 74 | padding 0 | ||
| 75 | margin 0 | ||
| 76 | |||
| 77 | img | ||
| 78 | max-width 100% | ||
| 79 | |||
| 80 | a | ||
| 81 | font-weight 500 | ||
| 82 | color $accentColor | ||
| 83 | text-decoration none | ||
| 84 | |||
| 85 | p a code | ||
| 86 | font-weight 400 | ||
| 87 | color $accentColor | ||
| 88 | |||
| 89 | kbd | ||
| 90 | background #eee | ||
| 91 | border solid 0.15rem #ddd | ||
| 92 | border-bottom solid 0.25rem #ddd | ||
| 93 | border-radius 0.15rem | ||
| 94 | padding 0 0.15em | ||
| 95 | |||
| 96 | blockquote | ||
| 97 | font-size 1rem | ||
| 98 | color #999; | ||
| 99 | border-left .2rem solid #dfe2e5 | ||
| 100 | margin 1rem 0 | ||
| 101 | padding .25rem 0 .25rem 1rem | ||
| 102 | |||
| 103 | & > p | ||
| 104 | margin 0 | ||
| 105 | |||
| 106 | ul, ol | ||
| 107 | padding-left 1.2em | ||
| 108 | |||
| 109 | strong | ||
| 110 | font-weight 600 | ||
| 111 | |||
| 112 | h1, h2, h3, h4, h5, h6 | ||
| 113 | font-weight 600 | ||
| 114 | line-height 1.25 | ||
| 115 | |||
| 116 | {$contentClass}:not(.custom) > & | ||
| 117 | margin-top (0.5rem - $navbarHeight) | ||
| 118 | padding-top ($navbarHeight + 1rem) | ||
| 119 | margin-bottom 0 | ||
| 120 | |||
| 121 | &:first-child | ||
| 122 | margin-top -1.5rem | ||
| 123 | margin-bottom 1rem | ||
| 124 | |||
| 125 | + p, + pre, + .custom-block | ||
| 126 | margin-top 2rem | ||
| 127 | |||
| 128 | &:focus .header-anchor, | ||
| 129 | &:hover .header-anchor | ||
| 130 | opacity: 1 | ||
| 131 | |||
| 132 | h1 | ||
| 133 | font-size 2.2rem | ||
| 134 | |||
| 135 | h2 | ||
| 136 | font-size 1.65rem | ||
| 137 | padding-bottom .3rem | ||
| 138 | border-bottom 1px solid $borderColor | ||
| 139 | |||
| 140 | h3 | ||
| 141 | font-size 1.35rem | ||
| 142 | |||
| 143 | a.header-anchor | ||
| 144 | font-size 0.85em | ||
| 145 | float left | ||
| 146 | margin-left -0.87em | ||
| 147 | padding-right 0.23em | ||
| 148 | margin-top 0.125em | ||
| 149 | opacity 0 | ||
| 150 | |||
| 151 | &:focus, | ||
| 152 | &:hover | ||
| 153 | text-decoration none | ||
| 154 | |||
| 155 | code, kbd, .line-number | ||
| 156 | font-family Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace | ||
| 157 | |||
| 158 | p, ul, ol | ||
| 159 | line-height 1.7 | ||
| 160 | |||
| 161 | hr | ||
| 162 | border 0 | ||
| 163 | border-top 1px solid $borderColor | ||
| 164 | |||
| 165 | table | ||
| 166 | border-collapse collapse | ||
| 167 | margin 1rem 0 | ||
| 168 | display: block | ||
| 169 | overflow-x: auto | ||
| 170 | |||
| 171 | tr | ||
| 172 | border-top 1px solid #dfe2e5 | ||
| 173 | |||
| 174 | &:nth-child(2n) | ||
| 175 | background-color #f6f8fa | ||
| 176 | |||
| 177 | th, td | ||
| 178 | border 1px solid #dfe2e5 | ||
| 179 | padding .6em 1em | ||
| 180 | |||
| 181 | .theme-container | ||
| 182 | &.sidebar-open | ||
| 183 | .sidebar-mask | ||
| 184 | display: block | ||
| 185 | |||
| 186 | &.no-navbar | ||
| 187 | {$contentClass}:not(.custom) > h1, h2, h3, h4, h5, h6 | ||
| 188 | margin-top 1.5rem | ||
| 189 | padding-top 0 | ||
| 190 | |||
| 191 | .sidebar | ||
| 192 | top 0 | ||
| 193 | |||
| 194 | @media (min-width: ($MQMobile + 1px)) | ||
| 195 | .theme-container.no-sidebar | ||
| 196 | .sidebar | ||
| 197 | display none | ||
| 198 | |||
| 199 | .page | ||
| 200 | padding-left 0 | ||
| 201 | |||
| 202 | @require 'mobile.styl' | ||
diff --git a/doc/docs/.vuepress/theme/styles/mobile.styl b/doc/docs/.vuepress/theme/styles/mobile.styl new file mode 100644 index 0000000..f5bd327 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/mobile.styl | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | @require './config' | ||
| 2 | |||
| 3 | $mobileSidebarWidth = $sidebarWidth * 0.82 | ||
| 4 | |||
| 5 | // narrow desktop / iPad | ||
| 6 | @media (max-width: $MQNarrow) | ||
| 7 | .sidebar | ||
| 8 | font-size 15px | ||
| 9 | width $mobileSidebarWidth | ||
| 10 | .page | ||
| 11 | padding-left $mobileSidebarWidth | ||
| 12 | |||
| 13 | // wide mobile | ||
| 14 | @media (max-width: $MQMobile) | ||
| 15 | .sidebar | ||
| 16 | top 0 | ||
| 17 | padding-top $navbarHeight | ||
| 18 | transform translateX(-100%) | ||
| 19 | transition transform .2s ease | ||
| 20 | .page | ||
| 21 | padding-left 0 | ||
| 22 | .theme-container | ||
| 23 | &.sidebar-open | ||
| 24 | .sidebar | ||
| 25 | transform translateX(0) | ||
| 26 | &.no-navbar | ||
| 27 | .sidebar | ||
| 28 | padding-top: 0 | ||
| 29 | |||
| 30 | // narrow mobile | ||
| 31 | @media (max-width: $MQMobileNarrow) | ||
| 32 | h1 | ||
| 33 | font-size 1.9rem | ||
| 34 | {$contentClass} | ||
| 35 | div[class*="language-"] | ||
| 36 | margin 0.85rem -1.5rem | ||
| 37 | border-radius 0 | ||
diff --git a/doc/docs/.vuepress/theme/styles/toc.styl b/doc/docs/.vuepress/theme/styles/toc.styl new file mode 100644 index 0000000..d3e7106 --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/toc.styl | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | .table-of-contents | ||
| 2 | .badge | ||
| 3 | vertical-align middle | ||
diff --git a/doc/docs/.vuepress/theme/styles/wrapper.styl b/doc/docs/.vuepress/theme/styles/wrapper.styl new file mode 100644 index 0000000..a99262c --- /dev/null +++ b/doc/docs/.vuepress/theme/styles/wrapper.styl | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | $wrapper | ||
| 2 | max-width $contentWidth | ||
| 3 | margin 0 auto | ||
| 4 | padding 2rem 2.5rem | ||
| 5 | @media (max-width: $MQNarrow) | ||
| 6 | padding 2rem | ||
| 7 | @media (max-width: $MQMobileNarrow) | ||
| 8 | padding 1.5rem | ||
| 9 | |||
diff --git a/doc/docs/.vuepress/theme/util/index.js b/doc/docs/.vuepress/theme/util/index.js new file mode 100644 index 0000000..92fcd3b --- /dev/null +++ b/doc/docs/.vuepress/theme/util/index.js | |||
| @@ -0,0 +1,244 @@ | |||
| 1 | export const hashRE = /#.*$/ | ||
| 2 | export const extRE = /\.(md|html)$/ | ||
| 3 | export const endingSlashRE = /\/$/ | ||
| 4 | export const outboundRE = /^[a-z]+:/i | ||
| 5 | |||
| 6 | export function normalize (path) { | ||
| 7 | return decodeURI(path) | ||
| 8 | .replace(hashRE, '') | ||
| 9 | .replace(extRE, '') | ||
| 10 | } | ||
| 11 | |||
| 12 | export function getHash (path) { | ||
| 13 | const match = path.match(hashRE) | ||
| 14 | if (match) { | ||
| 15 | return match[0] | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | export function isExternal (path) { | ||
| 20 | return outboundRE.test(path) | ||
| 21 | } | ||
| 22 | |||
| 23 | export function isMailto (path) { | ||
| 24 | return /^mailto:/.test(path) | ||
| 25 | } | ||
| 26 | |||
| 27 | export function isTel (path) { | ||
| 28 | return /^tel:/.test(path) | ||
| 29 | } | ||
| 30 | |||
| 31 | export function ensureExt (path) { | ||
| 32 | if (isExternal(path)) { | ||
| 33 | return path | ||
| 34 | } | ||
| 35 | const hashMatch = path.match(hashRE) | ||
| 36 | const hash = hashMatch ? hashMatch[0] : '' | ||
| 37 | const normalized = normalize(path) | ||
| 38 | |||
| 39 | if (endingSlashRE.test(normalized)) { | ||
| 40 | return path | ||
| 41 | } | ||
| 42 | return normalized + '.html' + hash | ||
| 43 | } | ||
| 44 | |||
| 45 | export function isActive (route, path) { | ||
| 46 | const routeHash = decodeURIComponent(route.hash) | ||
| 47 | const linkHash = getHash(path) | ||
| 48 | if (linkHash && routeHash !== linkHash) { | ||
| 49 | return false | ||
| 50 | } | ||
| 51 | const routePath = normalize(route.path) | ||
| 52 | const pagePath = normalize(path) | ||
| 53 | return routePath === pagePath | ||
| 54 | } | ||
| 55 | |||
| 56 | export function resolvePage (pages, rawPath, base) { | ||
| 57 | if (isExternal(rawPath)) { | ||
| 58 | return { | ||
| 59 | type: 'external', | ||
| 60 | path: rawPath | ||
| 61 | } | ||
| 62 | } | ||
| 63 | if (base) { | ||
| 64 | rawPath = resolvePath(rawPath, base) | ||
| 65 | } | ||
| 66 | const path = normalize(rawPath) | ||
| 67 | for (let i = 0; i < pages.length; i++) { | ||
| 68 | if (normalize(pages[i].regularPath) === path) { | ||
| 69 | return Object.assign({}, pages[i], { | ||
| 70 | type: 'page', | ||
| 71 | path: ensureExt(pages[i].path) | ||
| 72 | }) | ||
| 73 | } | ||
| 74 | } | ||
| 75 | console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`) | ||
| 76 | return {} | ||
| 77 | } | ||
| 78 | |||
| 79 | function resolvePath (relative, base, append) { | ||
| 80 | const firstChar = relative.charAt(0) | ||
| 81 | if (firstChar === '/') { | ||
| 82 | return relative | ||
| 83 | } | ||
| 84 | |||
| 85 | if (firstChar === '?' || firstChar === '#') { | ||
| 86 | return base + relative | ||
| 87 | } | ||
| 88 | |||
| 89 | const stack = base.split('/') | ||
| 90 | |||
| 91 | // remove trailing segment if: | ||
| 92 | // - not appending | ||
| 93 | // - appending to trailing slash (last segment is empty) | ||
| 94 | if (!append || !stack[stack.length - 1]) { | ||
| 95 | stack.pop() | ||
| 96 | } | ||
| 97 | |||
| 98 | // resolve relative path | ||
| 99 | const segments = relative.replace(/^\//, '').split('/') | ||
| 100 | for (let i = 0; i < segments.length; i++) { | ||
| 101 | const segment = segments[i] | ||
| 102 | if (segment === '..') { | ||
| 103 | stack.pop() | ||
| 104 | } else if (segment !== '.') { | ||
| 105 | stack.push(segment) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | // ensure leading slash | ||
| 110 | if (stack[0] !== '') { | ||
| 111 | stack.unshift('') | ||
| 112 | } | ||
| 113 | |||
| 114 | return stack.join('/') | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * @param { Page } page | ||
| 119 | * @param { string } regularPath | ||
| 120 | * @param { SiteData } site | ||
| 121 | * @param { string } localePath | ||
| 122 | * @returns { SidebarGroup } | ||
| 123 | */ | ||
| 124 | export function resolveSidebarItems (page, regularPath, site, localePath) { | ||
| 125 | const { pages, themeConfig } = site | ||
| 126 | |||
| 127 | const localeConfig = localePath && themeConfig.locales | ||
| 128 | ? themeConfig.locales[localePath] || themeConfig | ||
| 129 | : themeConfig | ||
| 130 | |||
| 131 | const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar | ||
| 132 | if (pageSidebarConfig === 'auto') { | ||
| 133 | return resolveHeaders(page) | ||
| 134 | } | ||
| 135 | |||
| 136 | const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar | ||
| 137 | if (!sidebarConfig) { | ||
| 138 | return [] | ||
| 139 | } else { | ||
| 140 | const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig) | ||
| 141 | if (config === 'auto') { | ||
| 142 | return resolveHeaders(page) | ||
| 143 | } | ||
| 144 | return config | ||
| 145 | ? config.map(item => resolveItem(item, pages, base)) | ||
| 146 | : [] | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * @param { Page } page | ||
| 152 | * @returns { SidebarGroup } | ||
| 153 | */ | ||
| 154 | function resolveHeaders (page) { | ||
| 155 | const headers = groupHeaders(page.headers || []) | ||
| 156 | return [{ | ||
| 157 | type: 'group', | ||
| 158 | collapsable: false, | ||
| 159 | title: page.title, | ||
| 160 | path: null, | ||
| 161 | children: headers.map(h => ({ | ||
| 162 | type: 'auto', | ||
| 163 | title: h.title, | ||
| 164 | basePath: page.path, | ||
| 165 | path: page.path + '#' + h.slug, | ||
| 166 | children: h.children || [] | ||
| 167 | })) | ||
| 168 | }] | ||
| 169 | } | ||
| 170 | |||
| 171 | export function groupHeaders (headers) { | ||
| 172 | // group h3s under h2 | ||
| 173 | headers = headers.map(h => Object.assign({}, h)) | ||
| 174 | let lastH2 | ||
| 175 | headers.forEach(h => { | ||
| 176 | if (h.level === 2) { | ||
| 177 | lastH2 = h | ||
| 178 | } else if (lastH2) { | ||
| 179 | (lastH2.children || (lastH2.children = [])).push(h) | ||
| 180 | } | ||
| 181 | }) | ||
| 182 | return headers.filter(h => h.level === 2) | ||
| 183 | } | ||
| 184 | |||
| 185 | export function resolveNavLinkItem (linkItem) { | ||
| 186 | return Object.assign(linkItem, { | ||
| 187 | type: linkItem.items && linkItem.items.length ? 'links' : 'link' | ||
| 188 | }) | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * @param { Route } route | ||
| 193 | * @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config | ||
| 194 | * @returns { base: string, config: SidebarConfig } | ||
| 195 | */ | ||
| 196 | export function resolveMatchingConfig (regularPath, config) { | ||
| 197 | if (Array.isArray(config)) { | ||
| 198 | return { | ||
| 199 | base: '/', | ||
| 200 | config: config | ||
| 201 | } | ||
| 202 | } | ||
| 203 | for (const base in config) { | ||
| 204 | if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) { | ||
| 205 | return { | ||
| 206 | base, | ||
| 207 | config: config[base] | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } | ||
| 211 | return {} | ||
| 212 | } | ||
| 213 | |||
| 214 | function ensureEndingSlash (path) { | ||
| 215 | return /(\.html|\/)$/.test(path) | ||
| 216 | ? path | ||
| 217 | : path + '/' | ||
| 218 | } | ||
| 219 | |||
| 220 | function resolveItem (item, pages, base, groupDepth = 1) { | ||
| 221 | if (typeof item === 'string') { | ||
| 222 | return resolvePage(pages, item, base) | ||
| 223 | } else if (Array.isArray(item)) { | ||
| 224 | return Object.assign(resolvePage(pages, item[0], base), { | ||
| 225 | title: item[1] | ||
| 226 | }) | ||
| 227 | } else { | ||
| 228 | const children = item.children || [] | ||
| 229 | if (children.length === 0 && item.path) { | ||
| 230 | return Object.assign(resolvePage(pages, item.path, base), { | ||
| 231 | title: item.title | ||
| 232 | }) | ||
| 233 | } | ||
| 234 | return { | ||
| 235 | type: 'group', | ||
| 236 | path: item.path, | ||
| 237 | title: item.title, | ||
| 238 | sidebarDepth: item.sidebarDepth, | ||
| 239 | initialOpenGroupIndex: item.initialOpenGroupIndex, | ||
| 240 | children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)), | ||
| 241 | collapsable: item.collapsable !== false | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
