| <template> |
| <div id="main-container"> |
| <div |
| id="editor-left-container" |
| :style="{ width: leftContainerSize + '%' }" |
| v-if="!shared.isMobile" |
| > |
| <el-tabs v-model="currentTab" type="border-card"> |
| <el-tab-pane name="code-editor"> |
| <span slot="label">{{ $t('editor.tabEditor') }}</span> |
| <el-container> |
| <el-header id="editor-control-panel"> |
| <div class="editor-controls"> |
| <a |
| href="javascript:;" |
| class="btn btn-default btn-sm" |
| @click="disposeAndRun" |
| >{{ $t('editor.run') }}</a |
| > |
| </div> |
| </el-header> |
| <el-main> |
| <CodeMonaco |
| v-if="shared.typeCheck" |
| id="code-panel" |
| :initialCode="initialCode" |
| ></CodeMonaco> |
| <CodeAce |
| v-else |
| id="code-panel" |
| :initialCode="initialCode" |
| ></CodeAce> |
| </el-main> |
| </el-container> |
| </el-tab-pane> |
| |
| <el-tab-pane |
| :label="$t('editor.tabFullCodePreview')" |
| name="full-code" |
| :lazy="true" |
| > |
| <el-container style="width: 100%; height: 100%"> |
| <el-header id="full-code-generate-config"> |
| <span class="full-code-generate-config-label"> |
| <!-- <i class="el-icon-setting"></i> 配置 --> |
| </span> |
| <el-switch |
| v-model="fullCodeConfig.minimal" |
| :active-text="$t('editor.minimalBundle')" |
| :inactive-text="''" |
| > |
| </el-switch> |
| <el-switch |
| v-model="fullCodeConfig.esm" |
| active-text="ES Modules" |
| :inactive-text="''" |
| > |
| </el-switch> |
| </el-header> |
| <el-main> |
| <FullCodePreview :code="fullCode"></FullCodePreview> |
| </el-main> |
| </el-container> |
| </el-tab-pane> |
| <el-tab-pane :label="$t('editor.tabOptionPreview')" name="full-option"> |
| <div id="option-outline"></div> |
| </el-tab-pane> |
| </el-tabs> |
| </div> |
| <div |
| class="handler" |
| id="h-handler" |
| @mousedown="onSplitterDragStart" |
| :style="{ left: leftContainerSize + '%' }" |
| v-if="!shared.isMobile" |
| ></div> |
| <Preview |
| :inEditor="true" |
| class="right-container" |
| ref="preview" |
| :style="{ |
| width: 100 - leftContainerSize + '%', |
| left: leftContainerSize + '%' |
| }" |
| ></Preview> |
| </div> |
| </template> |
| |
| <script> |
| import CodeAce from './CodeAce.vue'; |
| import CodeMonaco from './CodeMonaco.vue'; |
| import FullCodePreview from './FullCodePreview.vue'; |
| import Preview from './Preview.vue'; |
| import { store, loadExampleCode, parseSourceCode } from '../common/store'; |
| import { collectDeps, buildExampleCode } from '../../common/buildCode'; |
| import { mount } from '@lang/object-visualizer'; |
| |
| import './object-visualizer.css'; |
| |
| export default { |
| components: { |
| CodeAce, |
| CodeMonaco, |
| FullCodePreview, |
| Preview |
| }, |
| |
| data() { |
| return { |
| mousedown: false, |
| leftContainerSize: 40, |
| mobileMode: false, |
| shared: store, |
| initialCode: '', |
| |
| currentTab: 'code-editor', |
| |
| fullCode: '', |
| |
| fullCodeConfig: { |
| mimimal: false, |
| esm: true, |
| node: false // If is in node |
| } |
| }; |
| }, |
| |
| computed: { |
| currentTime() { |
| // Update time when message updated. |
| const message = this.shared.message; |
| |
| const time = new Date(); |
| const digits = [time.getHours(), time.getMinutes(), time.getSeconds()]; |
| let timeStr = ''; |
| for (let i = 0, len = digits.length; i < len; ++i) { |
| timeStr += (digits[i] < 10 ? '0' : '') + digits[i]; |
| if (i < len - 1) { |
| timeStr += ':'; |
| } |
| } |
| return timeStr; |
| } |
| }, |
| |
| mounted() { |
| if (store.isMobile) { |
| this.leftContainerSize = 0; |
| loadExampleCode().then((code) => { |
| // No editor available. Set to runCode directly. |
| store.runCode = parseSourceCode(code); |
| }); |
| } else { |
| loadExampleCode().then((code) => { |
| // Only set the code in editor. editor will sync to the store. |
| this.initialCode = parseSourceCode(code); |
| }); |
| |
| window.addEventListener('mousemove', (e) => { |
| if (this.mousedown) { |
| let percentage = e.clientX / window.innerWidth; |
| percentage = Math.min(0.9, Math.max(0.1, percentage)); |
| this.leftContainerSize = percentage * 100; |
| } |
| }); |
| |
| window.addEventListener('mouseup', (e) => { |
| this.mousedown = false; |
| }); |
| } |
| }, |
| |
| methods: { |
| onSplitterDragStart() { |
| this.mousedown = true; |
| }, |
| disposeAndRun() { |
| this.$refs.preview.refreshAll(); |
| }, |
| updateFullCode() { |
| const option = this.$refs.preview.getOption(); |
| if (!option) { |
| return; |
| } |
| const deps = collectDeps(option); |
| deps.push(store.renderer === 'svg' ? 'SVGRenderer' : 'CanvasRenderer'); |
| this.fullCode = buildExampleCode(store.sourceCode, deps, { |
| minimal: this.fullCodeConfig.minimal, |
| ts: store.typeCheck, |
| esm: this.fullCodeConfig.esm, |
| // legacy: true, |
| theme: store.darkMode ? 'dark' : '', |
| ROOT_PATH: store.cdnRoot |
| }); |
| }, |
| updateOptionOutline() { |
| const option = Object.freeze(this.$refs.preview.getOption()); |
| if (!option) { |
| return; |
| } |
| mount(option, this.$el.querySelector('#option-outline'), { |
| getKeys(object, path) { |
| return Object.keys(object).filter((key) => { |
| if (Array.isArray(object[key]) && !object[key].length) { |
| return false; |
| } |
| return true; |
| }); |
| }, |
| expandOnCreatedAndUpdated(path) { |
| return ( |
| path.length === 0 || (path[0] === 'series' && path.length <= 1) |
| ); |
| } |
| }); |
| }, |
| updateTabContent(tab) { |
| if (tab === 'full-code') { |
| this.updateFullCode(); |
| } else if (tab === 'full-option') { |
| this.updateOptionOutline(); |
| } |
| } |
| }, |
| |
| watch: { |
| 'shared.typeCheck'(enableTypeCheck) { |
| // Update initialCode to avoid code changed when switching editor |
| this.initialCode = store.sourceCode; |
| this.updateFullCode(); |
| }, |
| currentTab(tab) { |
| this.updateTabContent(tab); |
| }, |
| 'shared.runHash'() { |
| this.updateTabContent(this.currentTab); |
| }, |
| fullCodeConfig: { |
| deep: true, |
| handler() { |
| this.updateFullCode(); |
| } |
| } |
| } |
| }; |
| </script> |
| |
| <style lang="scss"> |
| @import '../style/color.scss'; |
| |
| $control-panel-height: 30px; |
| $pd-basic: 10px; |
| $handler-width: 5px; |
| |
| #main-container { |
| .handler { |
| position: absolute; |
| left: 50%; |
| |
| top: 0; |
| bottom: 0; |
| width: $handler-width; |
| |
| cursor: col-resize; |
| z-index: 100; |
| background-color: transparent; |
| border-left: 1px solid #ececec; |
| // border-right: 1px solid $clr-border; |
| } |
| } |
| |
| #editor-left-container { |
| position: absolute; |
| left: 0; |
| bottom: 0; |
| top: 0; |
| |
| width: 50%; |
| |
| .el-tab-pane { |
| height: 100%; |
| |
| .el-container { |
| width: 100%; |
| height: 100%; |
| } |
| |
| .el-header { |
| height: $control-panel-height !important; |
| position: relative; |
| z-index: 10; |
| padding: 0; |
| } |
| .el-main { |
| padding: 0; |
| position: relative; |
| |
| ::-webkit-scrollbar { |
| height: 8px; |
| width: 8px; |
| transition: all 0.3s ease-in-out; |
| border-radius: 2px; |
| } |
| |
| ::-webkit-scrollbar-button { |
| display: none; |
| } |
| |
| ::-webkit-scrollbar-thumb { |
| width: 8px; |
| min-height: 15px; |
| background: rgba(50, 50, 50, 0.6) !important; |
| transition: all 0.3s ease-in-out; |
| border-radius: 2px; |
| } |
| |
| ::-webkit-scrollbar-thumb:hover { |
| background: rgba(0, 0, 0, 0.5) !important; |
| } |
| } |
| } |
| |
| .el-tabs { |
| box-shadow: none; |
| } |
| |
| .el-tabs--border-card > .el-tabs__header { |
| border-bottom: none; |
| } |
| |
| .el-tabs__content { |
| position: absolute; |
| top: 34px; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| padding: 0; |
| } |
| |
| .el-tabs__item { |
| height: 34px; |
| line-height: 34px; |
| } |
| } |
| |
| #editor-control-panel, |
| #full-code-generate-config { |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
| } |
| |
| #option-outline { |
| // height: 100%; |
| // Fix safari |
| position: absolute; |
| left: 0; |
| top: 0; |
| bottom: 0; |
| right: 0; |
| |
| font-size: 13px; |
| |
| font-family: 'Source Code Pro', 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', |
| monospace; |
| } |
| |
| #full-code-generate-config { |
| .full-code-generate-config-label { |
| height: $control-panel-height; |
| line-height: $control-panel-height; |
| vertical-align: middle; |
| margin: 0 0 0 20px; |
| } |
| |
| .el-switch { |
| margin-right: 10px; |
| } |
| |
| .el-switch__label { |
| margin-left: 8px; |
| margin-top: -2px; |
| } |
| .el-switch__label * { |
| font-size: 12px; |
| } |
| } |
| |
| #editor-control-panel { |
| .setting-panel { |
| display: inline-block; |
| |
| .btn-group + .btn-group { |
| margin-left: $pd-basic; |
| } |
| } |
| |
| .editor-controls { |
| float: right; |
| |
| .el-switch__label { |
| margin-top: -3px; |
| } |
| .el-switch__label * { |
| font-size: 12px; |
| } |
| |
| .btn { |
| color: #fff; |
| border-radius: 0; |
| background-color: #409eff; |
| margin-left: $pd-basic; |
| border: none; |
| height: 30px; |
| width: 50px; |
| } |
| .btn:hover { |
| background-color: lighten($color: #409eff, $amount: 5); |
| } |
| } |
| } |
| |
| .right-container { |
| position: absolute; |
| right: 0; |
| |
| width: 50%; |
| height: 100%; |
| padding: 0; |
| padding-left: $handler-width; |
| border: none; |
| z-index: 30; |
| |
| background: $clr-bg; |
| } |
| </style> |