diff options
Diffstat (limited to 'doc/docs/.vuepress')
42 files changed, 3740 insertions, 0 deletions
diff --git a/doc/docs/.vuepress/components/CompilerModal.vue b/doc/docs/.vuepress/components/CompilerModal.vue new file mode 100755 index 0000000..65ae9b0 --- /dev/null +++ b/doc/docs/.vuepress/components/CompilerModal.vue | |||
@@ -0,0 +1,32 @@ | |||
1 | <template> | ||
2 | <modal name="compiler" @before-open="prepare()" height="auto" width="80%" scrollable> | ||
3 | <yue-compiler :text="content" compileronly displayonly/> | ||
4 | </modal> | ||
5 | </template> | ||
6 | |||
7 | <script> | ||
8 | import Vue from 'vue'; | ||
9 | import VModal from 'vue-js-modal/dist/ssr.nocss'; | ||
10 | import 'vue-js-modal/dist/styles.css'; | ||
11 | Vue.use(VModal); | ||
12 | import YueCompiler from './YueCompiler.vue'; | ||
13 | |||
14 | export default { | ||
15 | components: { | ||
16 | YueCompiler, | ||
17 | }, | ||
18 | mounted () { | ||
19 | }, | ||
20 | data() { | ||
21 | return { | ||
22 | content: "", | ||
23 | }; | ||
24 | }, | ||
25 | methods: { | ||
26 | prepare() { | ||
27 | this.$data.content = window.yueCodes; | ||
28 | }, | ||
29 | }, | ||
30 | } | ||
31 | </script> | ||
32 | |||
diff --git a/doc/docs/.vuepress/components/YueCompiler.vue b/doc/docs/.vuepress/components/YueCompiler.vue new file mode 100755 index 0000000..4d72b92 --- /dev/null +++ b/doc/docs/.vuepress/components/YueCompiler.vue | |||
@@ -0,0 +1,383 @@ | |||
1 | <template> | ||
2 | <div style="width: 100%; height: auto;"> | ||
3 | <div class="parent" style="background-color: #f5f7ff;"> | ||
4 | <div class="childL" style="height: 2.5em;"> | ||
5 | <div class="childTitle">Yuescript {{ info }}</div> | ||
6 | </div> | ||
7 | <div class="childR" style="height: 2.5em;"> | ||
8 | <div class="childTitle">Lua</div> | ||
9 | </div> | ||
10 | <div class="childL" ref='yueEditor' style="height: 30em;"> | ||
11 | <ClientOnly> | ||
12 | <prism-editor class="my-editor" v-model="code" :highlight="highlighter" @input="codeChanged($event)" line-numbers :readonly="readonly"></prism-editor> | ||
13 | </ClientOnly> | ||
14 | </div> | ||
15 | <div class="childR" style="height: 30em;"> | ||
16 | <ClientOnly> | ||
17 | <prism-editor class="my-editor" v-model="compiled" :highlight="highlighter" @input="codeChanged($event)" readonly></prism-editor> | ||
18 | </ClientOnly> | ||
19 | </div> | ||
20 | </div> | ||
21 | <div v-if="!compileronly"> | ||
22 | <button class="button" @click="runCode()">Run!</button> | ||
23 | <textarea class="resultArea" readonly>{{ result }}</textarea> | ||
24 | </div> | ||
25 | </div> | ||
26 | </template> | ||
27 | |||
28 | <script> | ||
29 | import { PrismEditor } from 'vue-prism-editor'; | ||
30 | import 'vue-prism-editor/dist/prismeditor.min.css'; | ||
31 | import { highlight, languages } from 'prismjs/components/prism-core'; | ||
32 | import 'prismjs/components/prism-moonscript'; | ||
33 | import 'prismjs/components/prism-lua'; | ||
34 | |||
35 | export default { | ||
36 | props: { | ||
37 | compileronly: { | ||
38 | type: Boolean, | ||
39 | default: false, | ||
40 | }, | ||
41 | displayonly: { | ||
42 | type: Boolean, | ||
43 | default: false, | ||
44 | }, | ||
45 | text: { | ||
46 | type: String, | ||
47 | default: '', | ||
48 | }, | ||
49 | }, | ||
50 | components: { | ||
51 | PrismEditor, | ||
52 | }, | ||
53 | data() { | ||
54 | return { | ||
55 | info: 'Loading', | ||
56 | readonly: true, | ||
57 | code: '', | ||
58 | compiled: '', | ||
59 | result: '', | ||
60 | }; | ||
61 | }, | ||
62 | mounted () { | ||
63 | if (this.text !== '') { | ||
64 | this.$data.code = this.text; | ||
65 | this.codeChanged(this.text); | ||
66 | } | ||
67 | const check = ((self)=> { | ||
68 | return ()=> { | ||
69 | if (window.yue) { | ||
70 | self.$data.info = window.yue.version(); | ||
71 | self.$data.readonly = false; | ||
72 | const editor = self.$refs.yueEditor; | ||
73 | if (editor.children.length === 0) { | ||
74 | setTimeout(check, 100); | ||
75 | return; | ||
76 | } | ||
77 | const textArea = editor.children[0].children[1].children[0]; | ||
78 | textArea.focus(); | ||
79 | } else { | ||
80 | setTimeout(check, 100); | ||
81 | } | ||
82 | } | ||
83 | })(this); | ||
84 | check(); | ||
85 | }, | ||
86 | methods: { | ||
87 | runCode() { | ||
88 | if (window.yue && this.$data.compiled !== '') { | ||
89 | let res = ''; | ||
90 | try { | ||
91 | res = window.yue.exec(this.$data.code); | ||
92 | } catch (err) { | ||
93 | res = err; | ||
94 | } | ||
95 | this.$data.result = res; | ||
96 | } | ||
97 | }, | ||
98 | highlighter(code) { | ||
99 | return highlight(code, languages.moon); | ||
100 | }, | ||
101 | codeChanged(text) { | ||
102 | if (window.yue) { | ||
103 | let res = ['','compiler error, and please help opening an issue for this. Thanks a lot!']; | ||
104 | try { | ||
105 | res = window.yue.tolua(text, true, !this.displayonly, true); | ||
106 | if (res[0] !== '') { | ||
107 | this.$data.compiled = res[0]; | ||
108 | } else { | ||
109 | this.$data.compiled = res[1]; | ||
110 | } | ||
111 | } catch (error) { | ||
112 | this.$data.compiled = res[1]; | ||
113 | } | ||
114 | } | ||
115 | }, | ||
116 | } | ||
117 | } | ||
118 | </script> | ||
119 | |||
120 | <style scoped> | ||
121 | .resultArea { | ||
122 | float: left; | ||
123 | margin-right: 1em; | ||
124 | overflow: scroll; | ||
125 | overflow-wrap: break-word; | ||
126 | width: calc(100% - 120px); | ||
127 | height: 55px; | ||
128 | border-color: #b7ae8f; | ||
129 | outline: none; | ||
130 | resize: none; | ||
131 | margin-top: 5px; | ||
132 | } | ||
133 | .childL { | ||
134 | float: left; | ||
135 | width: 50%; | ||
136 | box-sizing: border-box; | ||
137 | background-clip: content-box; | ||
138 | background: #f5f7ff; | ||
139 | } | ||
140 | .childR { | ||
141 | float: left; | ||
142 | width: 50%; | ||
143 | box-sizing: border-box; | ||
144 | background-clip: content-box; | ||
145 | background: #f5f7ff; | ||
146 | } | ||
147 | .childTitle { | ||
148 | width: 100%; | ||
149 | font-size: 1.5em; | ||
150 | color: #b7ae8f; | ||
151 | text-align: center; | ||
152 | padding: 0.2em; | ||
153 | } | ||
154 | .button { | ||
155 | float: right; | ||
156 | border: none; | ||
157 | display: inline-block; | ||
158 | font-size: 1.2rem; | ||
159 | color: #fff; | ||
160 | background-color: #b7ae8f; | ||
161 | text-decoration: none; | ||
162 | padding: .8rem 1.6rem; | ||
163 | border-radius: 4px; | ||
164 | transition: background-color .1s ease; | ||
165 | box-sizing: border-box; | ||
166 | border-bottom: 1px solid #aaa07b; | ||
167 | margin-top: 10px; | ||
168 | margin-right: 5px; | ||
169 | } | ||
170 | .button:hover { | ||
171 | background-color: #beb69a; | ||
172 | } | ||
173 | .button:focus, | ||
174 | .button:active:focus, | ||
175 | .button.active:focus, | ||
176 | .button.focus, | ||
177 | .button:active.focus { | ||
178 | outline: none; | ||
179 | } | ||
180 | |||
181 | .my-editor { | ||
182 | 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; | ||
183 | line-height: 1.375; | ||
184 | direction: ltr; | ||
185 | text-align: left; | ||
186 | white-space: pre; | ||
187 | word-spacing: normal; | ||
188 | word-break: normal; | ||
189 | -moz-tab-size: 4; | ||
190 | -o-tab-size: 4; | ||
191 | tab-size: 4; | ||
192 | -webkit-hyphens: none; | ||
193 | -moz-hyphens: none; | ||
194 | -ms-hyphens: none; | ||
195 | hyphens: none; | ||
196 | background: #f5f7ff; | ||
197 | color: #5e6687; | ||
198 | font-size: 1em; | ||
199 | overflow: scroll; | ||
200 | } | ||
201 | |||
202 | .my-editor >>> .prism-editor__textarea:focus { | ||
203 | outline: none; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | Name: Base16 Atelier Sulphurpool Light | ||
208 | Author: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool) | ||
209 | |||
210 | Prism template by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/prism/) | ||
211 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) | ||
212 | |||
213 | */ | ||
214 | code[class*="language-"], | ||
215 | pre[class*="language-"] { | ||
216 | 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; | ||
217 | font-size: 1em; | ||
218 | line-height: 1.375; | ||
219 | direction: ltr; | ||
220 | text-align: left; | ||
221 | white-space: pre; | ||
222 | word-spacing: normal; | ||
223 | word-break: normal; | ||
224 | -moz-tab-size: 4; | ||
225 | -o-tab-size: 4; | ||
226 | tab-size: 4; | ||
227 | -webkit-hyphens: none; | ||
228 | -moz-hyphens: none; | ||
229 | -ms-hyphens: none; | ||
230 | hyphens: none; | ||
231 | background: #f5f7ff; | ||
232 | color: #5e6687; | ||
233 | } | ||
234 | |||
235 | pre > code[class*="language-"] { | ||
236 | font-size: 1em; | ||
237 | } | ||
238 | |||
239 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, | ||
240 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { | ||
241 | text-shadow: none; | ||
242 | background: #dfe2f1; | ||
243 | } | ||
244 | |||
245 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, | ||
246 | code[class*="language-"]::selection, code[class*="language-"] ::selection { | ||
247 | text-shadow: none; | ||
248 | background: #dfe2f1; | ||
249 | } | ||
250 | |||
251 | /* Code blocks */ | ||
252 | pre[class*="language-"] { | ||
253 | padding: 1em; | ||
254 | margin: .5em 0; | ||
255 | overflow: auto; | ||
256 | } | ||
257 | |||
258 | /* Inline code */ | ||
259 | :not(pre) > code[class*="language-"] { | ||
260 | padding: .1em; | ||
261 | border-radius: .3em; | ||
262 | } | ||
263 | |||
264 | .token.comment, | ||
265 | .token.prolog, | ||
266 | .token.doctype, | ||
267 | .token.cdata { | ||
268 | color: #898ea4; | ||
269 | } | ||
270 | |||
271 | .token.punctuation { | ||
272 | color: #5e6687; | ||
273 | } | ||
274 | |||
275 | .token.namespace { | ||
276 | opacity: .7; | ||
277 | } | ||
278 | |||
279 | .token.operator, | ||
280 | .token.boolean, | ||
281 | .token.number { | ||
282 | color: #c76b29; | ||
283 | } | ||
284 | |||
285 | .token.property { | ||
286 | color: #c08b30; | ||
287 | } | ||
288 | |||
289 | .token.tag { | ||
290 | color: #3d8fd1; | ||
291 | } | ||
292 | |||
293 | .token.string { | ||
294 | color: #22a2c9; | ||
295 | } | ||
296 | |||
297 | .token.selector { | ||
298 | color: #6679cc; | ||
299 | } | ||
300 | |||
301 | .token.attr-name { | ||
302 | color: #c76b29; | ||
303 | } | ||
304 | |||
305 | .token.entity, | ||
306 | .token.url, | ||
307 | .language-css .token.string, | ||
308 | .style .token.string { | ||
309 | color: #22a2c9; | ||
310 | } | ||
311 | |||
312 | .token.attr-value, | ||
313 | .token.keyword, | ||
314 | .token.control, | ||
315 | .token.directive, | ||
316 | .token.unit { | ||
317 | color: #ac9739; | ||
318 | } | ||
319 | |||
320 | .token.statement, | ||
321 | .token.regex, | ||
322 | .token.atrule { | ||
323 | color: #22a2c9; | ||
324 | } | ||
325 | |||
326 | .token.placeholder, | ||
327 | .token.variable { | ||
328 | color: #3d8fd1; | ||
329 | } | ||
330 | |||
331 | .token.deleted { | ||
332 | text-decoration: line-through; | ||
333 | } | ||
334 | |||
335 | .token.inserted { | ||
336 | border-bottom: 1px dotted #202746; | ||
337 | text-decoration: none; | ||
338 | } | ||
339 | |||
340 | .token.italic { | ||
341 | font-style: italic; | ||
342 | } | ||
343 | |||
344 | .token.important, | ||
345 | .token.bold { | ||
346 | font-weight: bold; | ||
347 | } | ||
348 | |||
349 | .token.important { | ||
350 | color: #c94922; | ||
351 | } | ||
352 | |||
353 | .token.entity { | ||
354 | cursor: help; | ||
355 | } | ||
356 | |||
357 | pre > code.highlight { | ||
358 | outline: 0.4em solid #c94922; | ||
359 | outline-offset: .4em; | ||
360 | } | ||
361 | |||
362 | /* overrides color-values for the Line Numbers plugin | ||
363 | * http://prismjs.com/plugins/line-numbers/ | ||
364 | */ | ||
365 | .line-numbers .line-numbers-rows { | ||
366 | border-right-color: #dfe2f1; | ||
367 | } | ||
368 | |||
369 | .line-numbers-rows > span:before { | ||
370 | color: #979db4; | ||
371 | } | ||
372 | |||
373 | /* overrides color-values for the Line Highlight plugin | ||
374 | * http://prismjs.com/plugins/line-highlight/ | ||
375 | */ | ||
376 | .line-highlight { | ||
377 | background: rgba(107, 115, 148, 0.2); | ||
378 | background: -webkit-linear-gradient(left, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); | ||
379 | background: linear-gradient(to right, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); | ||
380 | } | ||
381 | |||
382 | </style> | ||
383 | |||
diff --git a/doc/docs/.vuepress/components/YueDisplay.vue b/doc/docs/.vuepress/components/YueDisplay.vue new file mode 100755 index 0000000..b89805b --- /dev/null +++ b/doc/docs/.vuepress/components/YueDisplay.vue | |||
@@ -0,0 +1,49 @@ | |||
1 | <template> | ||
2 | <div> | ||
3 | <button class="button" @click="compile()">Compile</button> | ||
4 | <div style="display: none;"> | ||
5 | <slot></slot> | ||
6 | </div> | ||
7 | </div> | ||
8 | </template> | ||
9 | |||
10 | <script> | ||
11 | export default { | ||
12 | methods: { | ||
13 | compile() { | ||
14 | const node = this.$el.children[1]; | ||
15 | const code = node.innerText; | ||
16 | window.yueCodes = code; | ||
17 | this.$modal.show('compiler'); | ||
18 | }, | ||
19 | }, | ||
20 | } | ||
21 | </script> | ||
22 | |||
23 | <style scoped> | ||
24 | .button { | ||
25 | border: none; | ||
26 | display: inline-block; | ||
27 | font-size: 16px; | ||
28 | color: #fff; | ||
29 | background-color: #b7ae8f; | ||
30 | text-decoration: none; | ||
31 | padding: .4rem 0.8rem; | ||
32 | border-radius: 4px; | ||
33 | transition: background-color .1s ease; | ||
34 | box-sizing: border-box; | ||
35 | border-bottom: 1px solid #aaa07b; | ||
36 | margin-bottom: 1em; | ||
37 | } | ||
38 | .button:hover { | ||
39 | background-color: #beb69a; | ||
40 | } | ||
41 | .button:focus, | ||
42 | .button:active:focus, | ||
43 | .button.active:focus, | ||
44 | .button.focus, | ||
45 | .button:active.focus { | ||
46 | outline: none; | ||
47 | } | ||
48 | </style> | ||
49 | |||
diff --git a/doc/docs/.vuepress/config.js b/doc/docs/.vuepress/config.js new file mode 100755 index 0000000..fe34c46 --- /dev/null +++ b/doc/docs/.vuepress/config.js | |||
@@ -0,0 +1,77 @@ | |||
1 | const { description } = require('../../package') | ||
2 | |||
3 | module.exports = { | ||
4 | /** | ||
5 | * Ref:https://v1.vuepress.vuejs.org/config/#title | ||
6 | */ | ||
7 | title: 'Yuescript', | ||
8 | /** | ||
9 | * Ref:https://v1.vuepress.vuejs.org/config/#description | ||
10 | */ | ||
11 | description: description, | ||
12 | |||
13 | /** | ||
14 | * Extra tags to be injected to the page HTML `<head>` | ||
15 | * | ||
16 | * ref:https://v1.vuepress.vuejs.org/config/#head | ||
17 | */ | ||
18 | head: [ | ||
19 | ['meta', { name: 'theme-color', content: '#3eaf7c' }], | ||
20 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], | ||
21 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], | ||
22 | ['script', {}, `window.global = window;`], | ||
23 | ['script', {}, ` | ||
24 | var Module = { | ||
25 | onRuntimeInitialized: function() { | ||
26 | window.yue = Module; | ||
27 | window.Vue.$data.readonly = false; | ||
28 | window.Vue.$data.info = Module.version(); | ||
29 | } | ||
30 | }; | ||
31 | `], | ||
32 | ['script', {}, ` | ||
33 | var Module = { | ||
34 | onRuntimeInitialized: function() { | ||
35 | window.yue = Module; | ||
36 | } | ||
37 | }; | ||
38 | `], | ||
39 | ['script', { src: '/js/yuescript.js' }], | ||
40 | ], | ||
41 | |||
42 | /** | ||
43 | * Theme configuration, here is the default theme configuration for VuePress. | ||
44 | * | ||
45 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html | ||
46 | */ | ||
47 | themeConfig: { | ||
48 | repo: '', | ||
49 | editLinks: false, | ||
50 | docsDir: '', | ||
51 | editLinkText: '', | ||
52 | lastUpdated: false, | ||
53 | nav: [ | ||
54 | { | ||
55 | text: 'Document', | ||
56 | link: '/doc/' | ||
57 | }, | ||
58 | { | ||
59 | text: 'Try yue!', | ||
60 | link: '/try/', | ||
61 | }, | ||
62 | { | ||
63 | text: 'Github', | ||
64 | link: 'https://github.com/pigpigyyy/Yuescript' | ||
65 | } | ||
66 | ], | ||
67 | }, | ||
68 | |||
69 | /** | ||
70 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ | ||
71 | */ | ||
72 | plugins: [ | ||
73 | '@vuepress/plugin-back-to-top', | ||
74 | '@vuepress/plugin-medium-zoom', | ||
75 | '~plugins/vue-js-modal.js', | ||
76 | ] | ||
77 | } | ||
diff --git a/doc/docs/.vuepress/enhanceApp.js b/doc/docs/.vuepress/enhanceApp.js new file mode 100755 index 0000000..8452a86 --- /dev/null +++ b/doc/docs/.vuepress/enhanceApp.js | |||
@@ -0,0 +1,14 @@ | |||
1 | /** | ||
2 | * Client app enhancement file. | ||
3 | * | ||
4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements | ||
5 | */ | ||
6 | |||
7 | export default ({ | ||
8 | Vue, // the version of Vue being used in the VuePress app | ||
9 | options, // the options for the root Vue instance | ||
10 | router, // the router instance for the app | ||
11 | siteData // site metadata | ||
12 | }) => { | ||
13 | // ...apply enhancements for the site. | ||
14 | } | ||
diff --git a/doc/docs/.vuepress/public/image/yuescript.svg b/doc/docs/.vuepress/public/image/yuescript.svg new file mode 100644 index 0000000..b7e5884 --- /dev/null +++ b/doc/docs/.vuepress/public/image/yuescript.svg | |||
@@ -0,0 +1,53 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | ||
3 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ | ||
4 | <!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/"> | ||
5 | <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/"> | ||
6 | <!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/"> | ||
7 | <!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/"> | ||
8 | <!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/"> | ||
9 | <!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/"> | ||
10 | <!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/"> | ||
11 | <!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/"> | ||
12 | ]> | ||
13 | <svg version="1.1" id="图层_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;" | ||
14 | xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="" viewBox="250 200 95.3 441.9" xml:space="preserve"> | ||
15 | <g> | ||
16 | <g> | ||
17 | <path fill="#B4AC8F" d="M209.8,522.5c-3.7,0.8-8.1,1.6-11.7,2.3c0.5-2,1.9-7.8,2.9-11.5l-2.8-2.9l-3.3,14.5c0,0.2-0.1,0.4-0.3,0.6 | ||
18 | l-6,5.7l2.4,2.6l6.1-5.8c0.2-0.2,0.3-0.3,0.5-0.3l14.7-2.5L209.8,522.5z"/> | ||
19 | <path fill="#B4AC8F" d="M225.4,551.7c-1.9,2.7-4.2,3.9-7.8,1.5c-3.6-2.5-3.4-5.2-1.6-7.8l7.7-11.2l-2.9-2l-7.8,11.4 | ||
20 | c-2.8,4-2.3,7.9,3.2,11.7c5.9,4,9.7,2.4,12.2-1.3l7.7-11.3l-3-2L225.4,551.7z"/> | ||
21 | <polygon fill="#B4AC8F" points="241.1,569.1 255.5,574.8 256.9,572.7 245.3,568 248,561.2 258.6,565.4 259.5,563 249,558.8 | ||
22 | 251.4,552.9 262.4,557.3 263.3,555 249.1,549.3 "/> | ||
23 | <path fill="#B4AC8F" d="M283.6,559.6c-4.7-0.8-8.2,0.8-8.8,4.2c-0.5,3,1.2,4.9,6.3,7.2c4,1.8,5.2,3,4.8,5.3 | ||
24 | c-0.4,2.2-2.4,3.1-5.4,2.6c-2.9-0.5-4.7-2.2-4.8-4.8l-3.5-0.6c-0.3,3.9,2.4,6.8,7.6,7.8c5.7,1,9-1.1,9.6-4.6 | ||
25 | c0.5-2.9-0.7-5.2-6.6-7.9c-3.7-1.7-4.9-2.6-4.6-4.5c0.3-1.8,1.9-2.7,4.7-2.2c3,0.5,4.1,2.1,4.1,4.2l3.4,0.6 | ||
26 | C291,563.6,289.1,560.6,283.6,559.6z"/> | ||
27 | <path fill="#B4AC8F" d="M315.3,581.1c-5.2,0.2-7-4-7.1-8.4c-0.2-4.4,1.6-8.4,6.4-8.6c3.7-0.1,5.2,1.6,5.9,3.9l3.5-0.1 | ||
28 | c-0.6-3.5-3.4-6.5-9.4-6.3c-7.2,0.3-10.3,5.5-10.1,11.2c0.2,6.3,3.2,11,10.8,10.7c6.1-0.2,8.6-3.5,9.2-7l-3.5,0.1 | ||
29 | C320.4,579,319,581,315.3,581.1z"/> | ||
30 | <path fill="#B4AC8F" d="M356.7,570.7c-0.7-2.8-2.2-4.2-4.9-4.2c1.8-1.1,3.5-2.9,2.7-5.9c-0.9-3.5-4.5-4.6-8.9-3.4l-8.9,2.3l5.3,20.6 | ||
31 | l3.4-0.9L343,570l4.1-1.1c3.9-1,5.4-0.2,6.1,2.6l0.1,0.4c0.6,2.2,1.1,4,1.7,4.8l3.4-0.9c-0.6-1.1-1.1-2.9-1.6-4.8L356.7,570.7z | ||
32 | M346.8,566.5l-4.4,1.1l-1.7-6.7l5.1-1.3c2.8-0.7,4.8-0.1,5.3,2C351.8,564.5,349.8,565.8,346.8,566.5z"/> | ||
33 | |||
34 | <rect x="370.4" y="548.3" transform="matrix(0.9105 -0.4136 0.4136 0.9105 -197.8473 203.9409)" fill="#B4AC8F" width="3.5" height="21.3"/> | ||
35 | <path fill="#B4AC8F" d="M390,536.6l-7.2,4.9l12,17.6l2.9-2l-5.1-7.4l3.9-2.7c4.3-2.9,6.1-6.7,3.7-10.2 | ||
36 | C398.1,533.6,393.9,533.9,390,536.6z M395.1,545.1l-3.7,2.6l-4.2-6.2l3.8-2.6c2.6-1.8,5-2,6.4,0 | ||
37 | C399,541.3,397.7,543.3,395.1,545.1z"/> | ||
38 | <polygon fill="#B4AC8F" points="419.6,513.4 417.7,511.6 405.1,524.8 407,526.5 412,521.2 425.6,534.2 428.1,531.6 414.5,518.7 | ||
39 | "/> | ||
40 | <path fill="#B4AC8F" d="M334.2,260.2c-2.1-0.3-4.2-0.6-6.3-0.9v14.4v0.5c22.4,24.2,36.9,55.9,39,90.9H228.7c0,0-14,0.7-14,12.3 | ||
41 | c0,12.1,11.1,14.1,13.9,14.1s69.4,0,69.4,0s8.7-0.5,8.7,6.1c0,6.3-6.4,6-8.6,6H156.6c11-74.3,69.2-133.2,143.1-145.3 | ||
42 | c-79.7,4.2-144.2,66.5-151.9,145.3h-41.1c0,0-15.3-1.8-15.3,11.5c0,13.3,11.5,12.8,14.7,12.8c0.6,0,19.5,0,41.1,0 | ||
43 | c1.5,28.3,10.4,54.8,24.7,77.4l0.1-0.1c-8.8-17.9-14.5-37.6-16.6-58.4c31.5,0,73,0,75.3,0c3.6,0,13.7-0.5,13.7-12.2 | ||
44 | c0-11.6-12.4-11.9-12.4-11.9l-101.8,0c0,0-8.9,0.7-8.9-6.4s5.8-7.4,8.4-7.4c0.6,0,190.5,0,190.5,0s14.6,1.6,14.6-11.9 | ||
45 | c0-12-12.8-11.5-17.5-11.5c-4.7,0-65.1,0-65.1,0s-9.7,0.4-9.7-6.9c0-7.3,8.2-6.9,9.6-6.9c0.7,0,59.7,0,114.7,0c0,0.7,0,1.5,0,2.2 | ||
46 | c0,38.5-14.9,73.6-39.3,99.8v0c-21.6,23.3-50.7,39.6-83.5,45.1c6.3,3.9,12.9,7.3,19.8,10.2c23.9-6.8,45.6-19,63.6-35.2v0 | ||
47 | c14.7-13.2,27-29.2,36.1-47h71.1v-6.7h-67.9c9.1-20.2,14.2-42.6,14.2-66.2c0-0.7,0-1.5,0-2.2c26.9,0,51.4,0,65.9,0 | ||
48 | c5.1,14.9,7.9,30.8,7.9,47.4c0,27.5-7.6,53.3-20.9,75.4l11.2,9.1c15.2-24.6,24-53.5,24-84.5C469.4,339.2,410.8,272.6,334.2,260.2 | ||
49 | z M216.9,435.3c0,6.5-6.3,6.9-6.3,6.9s-27,0-55.4,0c-0.3-4.3-0.5-8.6-0.5-12.9c0-0.5,0-0.9,0-1.4c27.4,0,56.2,0,56.5,0 | ||
50 | C212.2,428,216.9,428.8,216.9,435.3z M381.2,365.1c-1.8-32.4-13.1-62.2-31.3-86.7c43.3,12.8,78.4,45.1,95,86.7H381.2z"/> | ||
51 | </g> | ||
52 | </g> | ||
53 | </svg> | ||
diff --git a/doc/docs/.vuepress/styles/index.styl b/doc/docs/.vuepress/styles/index.styl new file mode 100755 index 0000000..69327fd --- /dev/null +++ b/doc/docs/.vuepress/styles/index.styl | |||
@@ -0,0 +1,9 @@ | |||
1 | /** | ||
2 | * Custom Styles here. | ||
3 | * | ||
4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl | ||
5 | */ | ||
6 | |||
7 | .home .hero img | ||
8 | max-width 450px!important | ||
9 | |||
diff --git a/doc/docs/.vuepress/styles/palette.styl b/doc/docs/.vuepress/styles/palette.styl new file mode 100755 index 0000000..389aa76 --- /dev/null +++ b/doc/docs/.vuepress/styles/palette.styl | |||
@@ -0,0 +1,12 @@ | |||
1 | /** | ||
2 | * Custom palette here. | ||
3 | * | ||
4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl | ||
5 | */ | ||
6 | |||
7 | $accentColor = #b7ae8f | ||
8 | $textColor = #2c3e50 | ||
9 | $borderColor = #eaecef | ||
10 | $codeBgColor = #f5f7ff | ||
11 | |||
12 | $contentWidth = 900px | ||
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 | } | ||