feat: move live demo into iframes

This commit is contained in:
Justineo 2022-04-26 15:45:48 +08:00
parent f3d12243b9
commit c64a77286e
No known key found for this signature in database
GPG Key ID: B73F0979CF18A0EA
8 changed files with 194 additions and 83 deletions

View File

@ -10,7 +10,9 @@
width="calc(100% - 40px)"
height="400px"
>
<slot/>
<one-iframe global-style="body { margin: 0 !important; } .veui-layout { min-width: auto !important; }">
<slot/>
</one-iframe>
</browser-window>
<slot v-else/>
</section>
@ -104,6 +106,7 @@ import toast from 'veui/plugins/toast'
import { BrowserWindow } from 'vue-windows'
import { getLocale } from '../common/i18n'
import { play } from '../common/play'
import OneIframe from './OneIframe'
import OneEditLink from './OneEditLink'
import OneRepl from './OneRepl'
import 'veui-theme-dls-icons/copy'
@ -119,6 +122,7 @@ export default {
'veui-button': Button,
'veui-icon': Icon,
BrowserWindow,
OneIframe,
OneEditLink,
OneRepl
},
@ -223,12 +227,10 @@ Icon.register({
padding 30px
& >>> .style-module_body__14MV-
overflow hidden
transform translate(0, 0)
padding 0
& >>> .veui-layout
min-width auto
.desc
border 1px solid #eee
padding 18px 20px

98
components/OneIframe.vue Normal file
View File

@ -0,0 +1,98 @@
<template>
<fragment>
<iframe
ref="iframe"
class="one-iframe"
/>
<slot/>
</fragment>
</template>
<script>
import { Fragment } from 'vue-frag'
export default {
name: 'one-iframe',
components: {
Fragment
},
props: {
globalStyle: String
},
watch: {
globalStyle (value) {
if (this.style) {
this.style.textContent = value
}
}
},
beforeDestroy () {
if (this.contents) {
this.contents.forEach(node => {
this.$refs.iframe.parentNode.appendChild(node)
})
}
if (this.mo) {
this.mo.disconnect()
}
},
mounted () {
const links = document.querySelectorAll('link[rel=stylesheet]')
const styles = document.querySelectorAll('style')
const { iframe } = this.$refs
const { body, head } = iframe.contentDocument
this.contents = this.$el.frag.filter(node => node !== iframe);
[...links, ...styles].forEach(node => {
const clone = node.cloneNode(true)
head.appendChild(clone)
})
this.contents.forEach(node => {
body.appendChild(node)
})
if (this.globalStyle) {
const style = document.createElement('style')
style.textContent = this.globalStyle
head.appendChild(style)
this.style = style
}
this.watchLiveStyle(head)
},
methods: {
watchLiveStyle (head) {
this.liveStyle = document.createComment('')
head.appendChild(this.liveStyle)
this.mo = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.target === document.head) {
const style = (this.liveSource = [...mutation.addedNodes].find(
node => node.nodeName === 'STYLE' && node.dataset.cssscoper
))
if (style) {
const liveStyle = style.cloneNode(true)
head.replaceChild(liveStyle, this.liveStyle)
this.liveStyle = liveStyle
}
} else if (mutation.target === this.liveSource) {
this.liveStyle.textContent = this.liveSource.textContent
}
}
})
this.mo.observe(document.head, { childList: true })
}
}
}
</script>
<style lang="stylus" scoped>
.one-iframe
display block
width 100%
height 100%
border none
overflow auto
</style>

View File

@ -68,14 +68,16 @@
'live-preview-browser': browser
}"
>
<v-live-preview
class="editor-preview"
:code="transformedCode"
:requires="imports"
:check-variable-availability="false"
@success="dismissError"
@error="handleError"
/>
<one-iframe global-style="body { margin: 0 !important; } body > article { margin: 24px 36px; } .veui-layout { min-width: auto !important; }">
<v-live-preview
class="editor-preview"
:code="transformedCode"
:requires="imports"
:check-variable-availability="false"
@success="dismissError"
@error="handleError"
/>
</one-iframe>
<transition name="editor-error">
<veui-alert
v-if="error"
@ -113,6 +115,7 @@ import { getLocale } from '../common/i18n'
import { play } from '../common/play'
import { transformLessCode } from '../common/transform'
import { loadPref, savePref } from '../common/util'
import OneIframe from './OneIframe'
Vue.use(toast)
@ -158,7 +161,8 @@ export default {
'v-splitpanes': Splitpanes,
'v-pane': Pane,
'v-monaco-editor': MonacoEditor,
'v-live-preview': VueLivePreview
'v-live-preview': VueLivePreview,
OneIframe
},
directives: {
tooltip

View File

@ -4,50 +4,48 @@
class="one-nav"
:class="{ expanded }"
>
<header>
<h2>VEUI</h2>
<section class="desc">
<a href="https://github.com/ecomfe/veui">
<img
alt="VEUI on GitHub"
src="https://img.shields.io/github/stars/ecomfe/veui?label=stars&logo=github"
height="20"
>
</a>
<nuxt-link
:class="{
'locale-swith': true,
disabled: altLocale.disabled,
}"
:to="altLocale.disabled ? '' : altLocale.to"
>
{{ altLocale.label }}
</nuxt-link>
</section>
<section class="search">
<one-search/>
</section>
<div
class="toggle"
@click="toggleMenu"
>
<veui-icon
class="expanded-icon"
name="chevron-left"
/>
<veui-icon
class="collapsed-icon"
name="hamburger"
/>
</div>
</header>
<veui-menu
class="one-menu"
:items="menuItems"
:expanded.sync="menuExpanded"
>
<template #before>
<header>
<h2>VEUI</h2>
<section class="desc">
<a href="https://github.com/ecomfe/veui">
<img
alt="VEUI on GitHub"
src="https://img.shields.io/github/stars/ecomfe/veui?label=stars&logo=github"
height="20"
>
</a>
<nuxt-link
:class="{
'locale-swith': true,
disabled: altLocale.disabled,
}"
:to="altLocale.disabled ? '' : altLocale.to"
>
{{ altLocale.label }}
</nuxt-link>
</section>
<section class="search">
<one-search/>
</section>
<div
class="toggle"
@click="toggleMenu"
>
<veui-icon
class="expanded-icon"
name="chevron-left"
/>
<veui-icon
class="collapsed-icon"
name="hamburger"
/>
</div>
</header>
</template>
<template #item-label="{ label, sub }">
{{ label }}<small>{{ sub }}</small>
</template>
@ -147,31 +145,14 @@ export default {
top 0
bottom 0
left 0
display flex
flex-direction column
width 280px
z-index 1
.one-menu
width 100%
height 100%
background-color #fff
& >>> .veui-menu-tree-wrapper
display flex
flex-direction column
flex-grow 1
height 100%
overflow visible
.veui-menu-tree
overflow auto
& >>> .DocSearch
margin 0
border-radius 6px
font inherit
header
padding 32px 20px 20px
flex none
h2
display flex
@ -191,10 +172,6 @@ export default {
img
display block
small
margin-left 8px
opacity 0.7
.locale-swith
display block
margin-left 12px
@ -215,6 +192,21 @@ export default {
margin-right 12px
flex-shrink 0
.one-menu
flex 1 1 auto
width 100%
overflow auto
background-color #fff
& >>> .DocSearch
margin 0
border-radius 6px
font inherit
small
margin-left 8px
opacity 0.7
.toggle
display none

View File

@ -121,9 +121,6 @@ Icon.register({
flex 1 1 auto
height calc(100% - 48px)
& >>> .live-preview
padding 24px 36px
& >>> .VueLive-error
display none

View File

@ -4,10 +4,7 @@
Header
</veui-header>
<veui-layout>
<veui-sidebar
sticky
style="max-height: 320px;"
>
<veui-sidebar sticky>
<div class="center full">
Sidebar
</div>

20
package-lock.json generated
View File

@ -82,6 +82,7 @@
"veui-theme-dls": "^2.6.4",
"veui-theme-dls-icons": "^2.6.4",
"vue-awesome": "^4.5.0",
"vue-frag": "^1.4.0",
"vue-i18n": "^8.16.0",
"vue-live": "^1.17.2",
"vue-windows": "^0.2.4"
@ -22221,6 +22222,18 @@
"node": ">=6.0.0"
}
},
"node_modules/vue-frag": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vue-frag/-/vue-frag-1.4.0.tgz",
"integrity": "sha512-S3LfATqRwSMR6O8mf92wKF3y2E/Gs/3MFJXW4CozMOLUeOopNUlkcYuhVLagw7fkNmn/Pyb1zfRY5UCwU+X6Gw==",
"dev": true,
"funding": {
"url": "https://github.com/privatenumber/vue-frag?sponsor=1"
},
"peerDependencies": {
"vue": "^2.6.0"
}
},
"node_modules/vue-hot-reload-api": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@ -41338,6 +41351,13 @@
}
}
},
"vue-frag": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vue-frag/-/vue-frag-1.4.0.tgz",
"integrity": "sha512-S3LfATqRwSMR6O8mf92wKF3y2E/Gs/3MFJXW4CozMOLUeOopNUlkcYuhVLagw7fkNmn/Pyb1zfRY5UCwU+X6Gw==",
"dev": true,
"requires": {}
},
"vue-hot-reload-api": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",

View File

@ -91,6 +91,7 @@
"veui-theme-dls": "^2.6.4",
"veui-theme-dls-icons": "^2.6.4",
"vue-awesome": "^4.5.0",
"vue-frag": "^1.4.0",
"vue-i18n": "^8.16.0",
"vue-live": "^1.17.2",
"vue-windows": "^0.2.4"