| <template> |
| <div class="doc-main" v-loading="loading"> |
| <div ref="docContentDom" |
| :class="[ |
| 'doc-content', |
| shared.showOptionExample ? 'option-example-actived' : '', |
| 'option-example-' + shared.computedOptionExampleLayout + '-layout' |
| ]"> |
| <h2 :id="pageId">{{pageTitle}}</h2> |
| <div |
| class="page-description" |
| v-if="pageDesc" |
| v-html="pageDesc" |
| v-highlight |
| ></div> |
| |
| <div v-if="pageDisplayOutline.children && pageDisplayOutline.children && 1 <= maxDepth"> |
| <h3>{{$t('content.properties')}}</h3> |
| <DocContentItemCard |
| v-for="child in pageDisplayOutline.children" |
| :key="child.path" |
| :node-data="child" |
| :desc-map="pageDescMap" |
| :max-depth="maxDepth" |
| :depth="1" |
| @scroll-to-self="scrollTo" |
| @toggle-expanded="handleCardExpandToggle" |
| ></DocContentItemCard> |
| </div> |
| </div> |
| <template v-if="showLiveExample"> |
| <LiveExample v-if="shared.showOptionExample" ref="liveExample"></LiveExample> |
| <div v-else class="open-option-example" @click="openOptionExample"> |
| <i class="el-icon-data-line"></i> {{ $t('example.titleShort') }} |
| </div> |
| </template> |
| </div> |
| </template> |
| |
| <script> |
| |
| import { |
| getPageTotalDescAsync, |
| getPageOutlineAsync, |
| convertPathToId, |
| getOutlineNode, |
| getDefaultPage |
| } from '../docHelper'; |
| import {store, getPagePath, isOptionDoc, updateOptionExampleLayout} from '../store'; |
| import {directTo} from '../route'; |
| import DocContentItemCard from './DocContentItemCard.vue' |
| import scrollIntoView from 'scroll-into-view'; |
| import Vue from 'vue'; |
| import LazyLoad from 'vanilla-lazyload'; |
| import LiveExample from './LiveExample.vue' |
| |
| export default { |
| components: { |
| DocContentItemCard, |
| LiveExample |
| }, |
| |
| data() { |
| return { |
| loading: false, |
| |
| pagePath: '', |
| |
| shared: store, |
| |
| maxDepth: Infinity, |
| |
| rootPageDescMap: {}, |
| |
| // Outline of this page |
| pageOutline: {}, |
| |
| pageDescMap: {} |
| } |
| }, |
| |
| computed: { |
| pageTitle() { |
| return this.pagePath; |
| }, |
| |
| pageId() { |
| return convertPathToId(this.pagePath); |
| }, |
| |
| pageDesc() { |
| const item = this.rootPageDescMap[this.pagePath] |
| || this.pageDescMap[this.pagePath]; |
| return item && item.desc; // In mobile. |
| }, |
| |
| pageExamples() { |
| const item = this.rootPageDescMap[this.pagePath] |
| || this.pageDescMap[this.pagePath]; |
| // Return an empty array by default. Or may not trigger it changed. |
| return (item && item.exampleBaseOptions) || []; |
| }, |
| |
| pageDisplayOutline() { |
| if (!this.shared.isMobile) { |
| return this.pageOutline; |
| } |
| else { |
| return getOutlineNode(getPagePath()); |
| } |
| }, |
| |
| showLiveExample() { |
| return !this.shared.isMobile && isOptionDoc(); |
| }, |
| |
| needScrollOffset() { |
| return this.shared.showOptionExample && !this.shared.isMobile |
| && this.shared.computedOptionExampleLayout === 'top'; |
| } |
| }, |
| |
| created() { |
| // Root page. |
| getPageTotalDescAsync('').then(rootPageDescMap => { |
| this.rootPageDescMap = Object.freeze(rootPageDescMap); |
| }); |
| |
| this._lazyload = new LazyLoad({ |
| elements_selector: 'iframe', |
| load_delay: 300 |
| }); |
| |
| this.updateCurrentPath(this.shared.currentPath, true); |
| |
| this.resize = this.resize.bind(this); |
| window.addEventListener('resize', this.resize); |
| |
| this.resize(); |
| }, |
| |
| destroyed() { |
| window.removeEventListener('resize', this.resize); |
| }, |
| |
| methods: { |
| resize() { |
| this.shared.optionExampleLayout === 'auto' && updateOptionExampleLayout('auto'); |
| Vue.nextTick(() => { |
| this.updateDocContentMargin(); |
| }); |
| }, |
| |
| updateLazyload() { |
| // console.log('Update lazy load'); |
| Vue.nextTick(() => { |
| this._lazyload.update(); |
| }); |
| }, |
| |
| handleCardExpandToggle() { |
| this.updateLazyload(); |
| }, |
| |
| scrollTo(path, time, timeDelay) { |
| // Scroll to. |
| setTimeout(() => { |
| //let container = store.isMobile ? document.body : this.$el.parentNode; |
| let offset = store.isMobile ? 100 : 20; |
| if (this.needScrollOffset) { |
| offset += this.$refs.liveExample.$el.offsetHeight; |
| } |
| // previous usage: document.querySelector('#' + convertPathToId(path)) |
| // Some special characters like `$` are not allowed in selector when using `document.querySelector`, |
| // use `document.getElementById` instead. |
| scrollIntoView(document.getElementById(convertPathToId(path)), { |
| time: time || 400, |
| align: { |
| top: 0, |
| topOffset: offset |
| } |
| }); |
| }, timeDelay || 0); |
| }, |
| |
| updateCurrentPath(newVal, firstTime) { |
| // Handling page count find issue. |
| if (newVal) { |
| if (!getOutlineNode(newVal)) { |
| // Redirect to default node |
| directTo(getDefaultPage(newVal)); |
| return; |
| } |
| } |
| else { |
| directTo(getDefaultPage()); |
| return; |
| } |
| |
| |
| let newPagePath = getPagePath(); |
| if (newPagePath === this.pagePath) { // Use title as hash. |
| this.scrollTo(newVal); |
| return; |
| } |
| |
| this.loading = true; |
| |
| this.pagePath = newPagePath; |
| // Fetch components. |
| getPageOutlineAsync(newVal).then(pageOutline => { |
| if (pageOutline.isRoot) { |
| this.maxDepth = 0; // No children |
| } |
| else if (this.shared.isMobile) { |
| this.maxDepth = 1; // Only one level |
| } |
| else { |
| this.maxDepth = Infinity; |
| } |
| |
| return getPageTotalDescAsync(newVal).then(pageDescMap => { |
| this.pageOutline = Object.freeze(Object.assign({}, pageOutline)); |
| |
| let newPageDescMap = {}; |
| let outlineRootName = newVal.split('.')[0]; |
| for (let key in pageDescMap) { |
| // Add key prefix |
| // For example: `series-bar.itemStyle` is `itemStyle` in the storage |
| newPageDescMap[outlineRootName + '.' + key] = pageDescMap[key]; |
| } |
| |
| this.pageDescMap = Object.freeze(newPageDescMap); |
| this.loading = false; |
| |
| this.scrollTo(newVal, 600, firstTime ? 300: 50); |
| this.updateLazyload(); |
| }); |
| }).catch(e => { |
| this.pageOutline = {}; |
| this.loading = false; |
| }); |
| }, |
| |
| openOptionExample() { |
| this.shared.showOptionExample = true; |
| }, |
| |
| updateDocContentMargin(isClose) { |
| if (!this.$refs.liveExample && !isClose) { |
| return; |
| } |
| // update margin of doc content |
| this.$refs.docContentDom.style.margin = ''; |
| if (!isClose) { |
| const marginDir = this.shared.computedOptionExampleLayout; |
| if (marginDir !== 'right') { |
| const marginStyleName = 'margin' + marginDir[0].toUpperCase() + marginDir.slice(1); |
| const marginValue = this.$refs.liveExample.$el.clientHeight; |
| this.$refs.docContentDom.style[marginStyleName] = marginValue + 'px'; |
| } |
| } |
| } |
| }, |
| |
| watch: { |
| 'shared.currentPath'(newVal) { |
| this.updateCurrentPath(newVal); |
| Vue.nextTick(() => { |
| this.updateDocContentMargin(); |
| }); |
| }, |
| 'pageExamples'(newVal) { |
| // { code, title, name } |
| // TODO: Code switch |
| if (newVal && newVal.length) { |
| this.shared.allOptionExamples = Object.freeze(newVal); |
| } |
| else { |
| this.shared.allOptionExamples = null; |
| } |
| }, |
| 'shared.computedOptionExampleLayout'() { |
| Vue.nextTick(() => { |
| this.updateDocContentMargin(); |
| }); |
| }, |
| 'shared.showOptionExample'(newVal) { |
| Vue.nextTick(() => { |
| this.updateDocContentMargin(!newVal); |
| }); |
| } |
| } |
| } |
| </script> |
| |
| |
| <style lang="scss"> |
| |
| @import "../style/mixin.scss"; |
| |
| .doc-main { |
| margin-left: 10px; |
| |
| .open-option-example { |
| position: fixed; |
| right: 0; |
| // bottom: 50px; |
| top: 50%; |
| padding: 10px; |
| border-radius: 20px 0 0 20px; |
| box-shadow: 0 0 10px rgba(0,0,0,0.2); |
| background: #fff; |
| cursor: pointer; |
| |
| font-size: 12px; |
| |
| &:hover { |
| background: #eee; |
| } |
| |
| i { |
| font-size: 16px; |
| vertical-align: middle; |
| } |
| } |
| } |
| |
| .doc-content { |
| text-align: left; |
| // color: #59636f; |
| color: #4d555e; |
| |
| // transition: margin-right 500ms cubic-bezier(0.215, 0.610, 0.355, 1); |
| |
| &.option-example-actived { |
| &.option-example-top-layout { |
| // margin-top: 42%; |
| } |
| &.option-example-bottom-layout { |
| // margin-bottom: 42%; |
| } |
| &.option-example-right-layout { |
| margin-right: 45%; |
| } |
| } |
| |
| |
| |
| h2 { |
| color: #B03A5B; |
| font-size: 34px; |
| border-bottom: 1px solid #ccc; |
| height: 45px; |
| line-height: 45px; |
| margin: 0; |
| margin-left: 15px; |
| font-weight: normal; |
| box-sizing: content-box; |
| } |
| |
| h3 { |
| font-weight: normal; |
| color: rgb(150, 150, 150); |
| font-size: 28px; |
| margin: 20px 0px 20px 15px; |
| } |
| |
| .page-description { |
| padding: 5px 0; |
| margin-left: 15px; |
| |
| @include description-html-formatter; |
| } |
| |
| .item-description { |
| margin: 0; |
| padding: 5px 0; |
| |
| @include description-html-formatter; |
| } |
| |
| table { |
| border: 1px solid #ddd; |
| border-collapse: collapse; |
| } |
| |
| th, td { |
| padding: 10px; |
| border: 1px solid #eee; |
| } |
| |
| } |
| |
| .ec-doc-tutorial { |
| .page-description { |
| h2 { |
| font-weight: normal; |
| font-size: 22px; |
| margin-left: 0; |
| margin-top: 40px; |
| } |
| } |
| } |
| |
| .ec-doc-mobile { |
| .doc-main { |
| margin-left: 0; |
| } |
| |
| .doc-content { |
| background: #f2f2f2; |
| margin-bottom: 100px; |
| } |
| .page-description { |
| padding: 5px 10px; |
| background: #fff; |
| // box-shadow: 0 0 20px rgba(0, 0, 0, 0.05); |
| } |
| h2 { |
| font-size: 22px; |
| font-weight: normal; |
| padding: 20px 10px; |
| border-bottom: none; |
| } |
| h3 { |
| font-size: 20px; |
| padding-left: 10px; |
| } |
| } |
| </style> |