2020-08-13 15:18:26 +08:00
< template >
< div
class = "demo-block"
: class = "[blockClass, { 'hover': hovering }]"
@ mouseenter = "hovering = true"
@ mouseleave = "hovering = false"
>
< div class = "source" >
< slot name = "source" > < / slot >
< / div >
< div ref = "meta" class = "meta" >
< div v-if ="$slots.default" class="description" >
< slot > < / slot >
< / div >
< div class = "highlight" >
< slot name = "highlight" > < / slot >
< / div >
< / div >
< div
ref = "control"
class = "demo-block-control"
: class = "{ 'is-fixed': fixedControl }"
@ click = "isExpanded = !isExpanded"
>
< transition name = "arrow-slide" >
< i : class = "[iconClass, { 'hovering': hovering }]" > < / i >
< / transition >
< transition name = "text-slide" >
< span v-show ="hovering" > {{ controlText }} < / span >
< / transition >
2021-05-31 14:31:01 +08:00
< div class = "control-button-container control-button-container-left" >
< el -button
v - show = "isExpanded && hasSetup"
size = "small"
type = "text"
class = "control-button"
@ click . stop = "onSwitchSyntax"
>
{ { showSetup ? langConfig [ 'switch-button-option-text' ] : langConfig [ 'switch-button-setup-text' ] } }
< / e l - b u t t o n >
< / div >
2021-01-31 18:42:29 +08:00
< div class = "control-button-container" >
< el -button
2021-05-31 14:31:01 +08:00
v - show = "isExpanded"
2021-01-31 18:42:29 +08:00
ref = "copyButton"
size = "small"
type = "text"
class = "control-button copy-button"
@ click . stop = "copy"
>
{ { langConfig [ 'copy-button-text' ] } }
< / e l - b u t t o n >
< el -tooltip effect = "dark" :content ="langConfig['tooltip-text']" placement = "right" >
< transition name = "text-slide" >
< el -button
2021-05-31 14:31:01 +08:00
v - show = "isExpanded"
2021-01-31 18:42:29 +08:00
size = "small"
type = "text"
class = "control-button run-online-button"
@ click . stop = "goCodepen"
>
{ { langConfig [ 'run-online-button-text' ] } }
< / e l - b u t t o n >
< / transition >
< / e l - t o o l t i p >
< / div >
2020-08-13 15:18:26 +08:00
< / div >
< / div >
< / template >
< script >
import { nextTick } from 'vue'
2020-09-28 16:09:10 +08:00
import hljs from 'highlight.js'
2021-01-31 18:42:29 +08:00
import clipboardCopy from 'clipboard-copy'
2020-08-13 15:18:26 +08:00
import compoLang from '../i18n/component.json'
2021-05-31 14:31:01 +08:00
import { stripScript , stripStyle , stripTemplate , stripSetup , removeSetup } from '../util'
2020-08-13 15:18:26 +08:00
const version = '1.0.0' // element version
2020-12-27 23:54:01 +08:00
const stripTemplateAndRemoveTemplate = code => {
2021-05-31 14:31:01 +08:00
const result = removeSetup ( stripTemplate ( code ) )
2020-12-27 23:54:01 +08:00
if ( result . indexOf ( '<template>' ) === 0 ) {
return result . replace ( /^<template>/ , '' ) . replace ( /<\/template>$/ , '' )
}
return result
}
2021-05-31 14:31:01 +08:00
const sanitizeHTML = str => {
const temp = document . createElement ( 'div' )
temp . textContent = str
return temp . innerHTML
}
2020-08-13 15:18:26 +08:00
export default {
data ( ) {
return {
codepen : {
script : '' ,
html : '' ,
style : '' ,
} ,
hovering : false ,
isExpanded : false ,
fixedControl : false ,
scrollParent : null ,
2021-05-31 14:31:01 +08:00
showSetup : false ,
hasSetup : false ,
2020-08-13 15:18:26 +08:00
}
} ,
computed : {
lang ( ) {
return this . $route . path . split ( '/' ) [ 1 ]
} ,
langConfig ( ) {
return compoLang . filter ( config => config . lang === this . lang ) [ 0 ] [ 'demo-block' ]
} ,
blockClass ( ) {
return ` demo- ${ this . lang } demo- ${ this . $router . currentRoute . value . path . split ( '/' ) . pop ( ) } `
} ,
iconClass ( ) {
return this . isExpanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom'
} ,
controlText ( ) {
return this . isExpanded ? this . langConfig [ 'hide-text' ] : this . langConfig [ 'show-text' ]
} ,
codeArea ( ) {
return this . $el . getElementsByClassName ( 'meta' ) [ 0 ]
} ,
2021-05-31 14:31:01 +08:00
displayDemoCode ( ) {
return this . showSetup ? this . codepen . setup : this . codepen . script
2020-08-13 15:18:26 +08:00
} ,
} ,
watch : {
isExpanded ( val ) {
2021-05-31 14:31:01 +08:00
this . setCodeAreaHeight ( )
2020-08-13 15:18:26 +08:00
if ( ! val ) {
this . fixedControl = false
this . $refs . control . style . left = '0'
this . removeScrollHandler ( )
return
}
setTimeout ( ( ) => {
this . scrollParent = document . querySelector ( '.page-component__scroll > .el-scrollbar__wrap' )
this . scrollParent && this . scrollParent . addEventListener ( 'scroll' , this . scrollHandler )
this . scrollHandler ( )
} , 200 )
} ,
} ,
created ( ) {
const highlight = this . $slots . highlight ( )
if ( highlight && highlight [ 0 ] ) {
let code = ''
let cur = highlight [ 0 ]
2020-12-04 11:55:06 +08:00
if ( cur . type === 'pre' && ( cur . children && cur . children [ 0 ] ) ) {
2020-08-13 15:18:26 +08:00
cur = cur . children [ 0 ]
2020-12-04 11:55:06 +08:00
if ( cur . type === 'code' ) {
code = cur . children
2020-08-13 15:18:26 +08:00
}
}
if ( code ) {
2020-12-27 23:54:01 +08:00
this . codepen . html = stripTemplateAndRemoveTemplate ( code )
2020-08-13 15:18:26 +08:00
this . codepen . script = stripScript ( code )
this . codepen . style = stripStyle ( code )
2021-05-31 14:31:01 +08:00
this . codepen . setup = stripSetup ( code )
if ( this . codepen . setup ) {
this . hasSetup = true
}
2020-08-13 15:18:26 +08:00
}
}
} ,
beforeUnmount ( ) {
this . removeScrollHandler ( )
} ,
2021-05-31 14:31:01 +08:00
mounted ( ) {
this . prettyCode ( )
} ,
2020-08-13 15:18:26 +08:00
methods : {
2021-05-31 14:31:01 +08:00
getCodeAreaHeight ( ) {
if ( this . $el . getElementsByClassName ( 'description' ) . length > 0 ) {
return this . $el . getElementsByClassName ( 'description' ) [ 0 ] . clientHeight +
this . $el . getElementsByClassName ( 'highlight' ) [ 0 ] . clientHeight + 20
}
return this . $el . getElementsByClassName ( 'highlight' ) [ 0 ] . clientHeight
} ,
setCodeAreaHeight ( ) {
this . codeArea . style . height = this . isExpanded ? ` ${ this . getCodeAreaHeight ( ) + 1 } px ` : '0'
} ,
prettyCode ( ) {
nextTick ( ( ) => {
const highlight = this . $el . querySelector ( '.highlight' )
const hlcode = highlight . querySelector ( 'pre code' )
hlcode . innerHTML = sanitizeHTML ( ` <template> ${ this . codepen . html } </template>
< script >
$ { this . displayDemoCode }
$ { '</sc' + 'ript>' }
` )
nextTick ( ( ) => {
if ( this . $el . getElementsByClassName ( 'description' ) . length === 0 ) {
highlight . style . width = '100%'
highlight . borderRight = 'none'
}
try {
hljs . highlightBlock ( hlcode )
} catch ( error ) {
console . log ( error )
}
} )
} )
} ,
onSwitchSyntax ( ) {
this . showSetup = ! this . showSetup
this . prettyCode ( )
this . $nextTick ( this . setCodeAreaHeight )
} ,
2021-01-31 18:42:29 +08:00
copy ( ) {
const res = clipboardCopy ( `
< template >
$ { this . codepen . html }
< / template >
< script >
$ { ' ' + this . codepen . script }
\ < \ / script >
< style >
$ { this . codepen . style }
< / style >
` )
res . then ( ( ) => {
this . $message ( {
showClose : true ,
message : this . langConfig [ 'copy-success' ] ,
type : 'success' ,
} )
} ) . catch ( ( ) => {
this . $message ( {
showClose : true ,
message : this . langConfig [ 'copy-error' ] ,
type : 'error' ,
} )
} )
} ,
2020-08-13 15:18:26 +08:00
goCodepen ( ) {
// since 2.6.2 use code rather than jsfiddle https://blog.codepen.io/documentation/api/prefill/
const { script , html , style } = this . codepen
2021-01-07 20:24:44 +08:00
const resourcesTpl = '<scr' + 'ipt src="//unpkg.com/vue@next"></scr' + 'ipt>' +
2020-12-04 11:55:06 +08:00
'\n<scr' + ` ipt src="//unpkg.com/element-plus/lib/index.full.js"></scr ` + 'ipt>'
2020-08-13 15:18:26 +08:00
let htmlTpl = ` ${ resourcesTpl } \ n<div id="app"> \ n ${ html . trim ( ) } \ n</div> `
2020-12-04 11:55:06 +08:00
let cssTpl = ` @import url("//unpkg.com/element-plus/lib/theme-chalk/index.css"); \ n ${ ( style || '' ) . trim ( ) } \ n `
2020-12-14 13:47:19 +08:00
let jsTpl = script ? script . replace ( /export default/ , 'var Main =' ) . trim ( ) . replace ( /import ({.*}) from 'vue'/g , ( s , s1 ) => ` const ${ s1 } = Vue ` ) . replace ( /import ({.*}) from 'element-plus'/g , ( s , s1 ) => ` const ${ s1 } = ElementPlus ` ) : 'var Main = {}'
2020-12-04 11:55:06 +08:00
jsTpl += '\n;const app = Vue.createApp(Main);\napp.use(ElementPlus);\napp.mount("#app")'
2020-08-13 15:18:26 +08:00
const data = {
js : jsTpl ,
css : cssTpl ,
html : htmlTpl ,
}
const form = document . getElementById ( 'fiddle-form' ) || document . createElement ( 'form' )
while ( form . firstChild ) {
form . removeChild ( form . firstChild )
}
form . method = 'POST'
form . action = 'https://codepen.io/pen/define/'
form . target = '_blank'
form . style . display = 'none'
const input = document . createElement ( 'input' )
input . setAttribute ( 'name' , 'data' )
input . setAttribute ( 'type' , 'hidden' )
input . setAttribute ( 'value' , JSON . stringify ( data ) )
form . appendChild ( input )
document . body . appendChild ( form )
form . submit ( )
2021-02-26 20:37:34 +08:00
document . body . removeChild ( form )
2020-08-13 15:18:26 +08:00
} ,
scrollHandler ( ) {
const { top , bottom , left } = this . $refs . meta . getBoundingClientRect ( )
2021-02-01 15:14:07 +08:00
const controlBarHeight = 44
this . fixedControl = bottom + controlBarHeight > document . documentElement . clientHeight &&
top <= document . documentElement . clientHeight
2020-08-13 15:18:26 +08:00
this . $refs . control . style . left = this . fixedControl ? ` ${ left } px ` : '0'
} ,
removeScrollHandler ( ) {
this . scrollParent && this . scrollParent . removeEventListener ( 'scroll' , this . scrollHandler )
} ,
} ,
}
< / script >
< style lang = "scss" scoped >
. demo - block {
border : solid 1 px # ebebeb ;
border - radius : 3 px ;
transition : .2 s ;
& . hover {
box - shadow : 0 0 8 px 0 rgba ( 232 , 237 , 250 , .6 ) , 0 2 px 4 px 0 rgba ( 232 , 237 , 250 , .5 ) ;
}
code {
font - family : Menlo , Monaco , Consolas , Courier , monospace ;
}
. demo - button {
float : right ;
}
. source {
padding : 24 px ;
}
. meta {
background - color : # fafafa ;
border - top : solid 1 px # eaeefb ;
overflow : hidden ;
height : 0 ;
transition : height .2 s ;
}
. description {
padding : 20 px ;
box - sizing : border - box ;
border : solid 1 px # ebebeb ;
border - radius : 3 px ;
font - size : 14 px ;
line - height : 22 px ;
color : # 666 ;
word - break : break - word ;
margin : 10 px ;
background - color : # fff ;
p {
margin : 0 ;
line - height : 26 px ;
}
code {
color : # 5 e6d82 ;
background - color : # e6effb ;
margin : 0 4 px ;
display : inline - block ;
padding : 1 px 5 px ;
font - size : 12 px ;
border - radius : 3 px ;
height : 18 px ;
line - height : 18 px ;
}
}
. highlight {
pre {
margin : 0 ;
}
code . hljs {
margin : 0 ;
border : none ;
max - height : none ;
border - radius : 0 ;
& : : before {
content : none ;
}
}
}
. demo - block - control {
border - top : solid 1 px # eaeefb ;
height : 44 px ;
box - sizing : border - box ;
background - color : # fff ;
border - bottom - left - radius : 4 px ;
border - bottom - right - radius : 4 px ;
text - align : center ;
margin - top : - 1 px ;
color : # d3dce6 ;
cursor : pointer ;
position : relative ;
& . is - fixed {
2021-02-01 15:14:07 +08:00
position : sticky ;
2020-08-13 15:18:26 +08:00
bottom : 0 ;
}
i {
font - size : 16 px ;
line - height : 44 px ;
transition : .3 s ;
& . hovering {
transform : translateX ( - 40 px ) ;
}
}
> span {
position : absolute ;
transform : translateX ( - 30 px ) ;
font - size : 14 px ;
line - height : 44 px ;
transition : .3 s ;
display : inline - block ;
}
& : hover {
color : # 409 EFF ;
background - color : # f9fafc ;
}
& . text - slide - enter ,
& . text - slide - leave - active {
opacity : 0 ;
transform : translateX ( 10 px ) ;
}
2021-01-31 18:42:29 +08:00
. control - button - container {
line - height : 40 px ;
2020-08-13 15:18:26 +08:00
position : absolute ;
top : 0 ;
right : 0 ;
padding - left : 5 px ;
padding - right : 25 px ;
}
2021-01-31 18:42:29 +08:00
2021-05-31 14:31:01 +08:00
. control - button - container - left {
left : 0 ;
width : 100 px ;
padding - left : 14 px ; // 14 + 10 = 24px .hljs code padding left 24
}
2021-01-31 18:42:29 +08:00
. control - button {
font - size : 14 px ;
margin : 0 10 px ;
}
2020-08-13 15:18:26 +08:00
}
}
< / style >