demo-block.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <template>
  2. <div
  3. class="demo-block"
  4. :class="[blockClass, { 'hover': hovering }]"
  5. @mouseenter="hovering = true"
  6. @mouseleave="hovering = false">
  7. <div class="source">
  8. <slot name="source"></slot>
  9. </div>
  10. <div class="meta" ref="meta">
  11. <div class="description" v-if="$slots.default">
  12. <slot></slot>
  13. </div>
  14. <div class="highlight">
  15. <slot name="highlight"></slot>
  16. </div>
  17. </div>
  18. <div
  19. class="demo-block-control"
  20. ref="control"
  21. :class="{ 'is-fixed': fixedControl }"
  22. @click="isExpanded = !isExpanded">
  23. <transition name="arrow-slide">
  24. <i :class="[iconClass, { 'hovering': hovering }]"></i>
  25. </transition>
  26. <transition name="text-slide">
  27. <span v-show="hovering">{{ controlText }}</span>
  28. </transition>
  29. <el-tooltip effect="dark" :content="langConfig['tooltip-text']" placement="right">
  30. <transition name="text-slide">
  31. <el-button
  32. v-show="hovering || isExpanded"
  33. size="small"
  34. type="text"
  35. class="control-button"
  36. @click.stop="goCodepen">
  37. {{ langConfig['button-text'] }}
  38. </el-button>
  39. </transition>
  40. </el-tooltip>
  41. </div>
  42. </div>
  43. </template>
  44. <style lang="scss">
  45. .demo-block {
  46. border: solid 1px #ebebeb;
  47. border-radius: 3px;
  48. transition: .2s;
  49. &.hover {
  50. box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5);
  51. }
  52. code {
  53. font-family: Menlo, Monaco, Consolas, Courier, monospace;
  54. }
  55. .demo-button {
  56. float: right;
  57. }
  58. .source {
  59. padding: 24px;
  60. }
  61. .meta {
  62. background-color: #fafafa;
  63. border-top: solid 1px #eaeefb;
  64. overflow: hidden;
  65. height: 0;
  66. transition: height .2s;
  67. }
  68. .description {
  69. padding: 20px;
  70. box-sizing: border-box;
  71. border: solid 1px #ebebeb;
  72. border-radius: 3px;
  73. font-size: 14px;
  74. line-height: 22px;
  75. color: #666;
  76. word-break: break-word;
  77. margin: 10px;
  78. background-color: #fff;
  79. p {
  80. margin: 0;
  81. line-height: 26px;
  82. }
  83. code {
  84. color: #5e6d82;
  85. background-color: #e6effb;
  86. margin: 0 4px;
  87. display: inline-block;
  88. padding: 1px 5px;
  89. font-size: 12px;
  90. border-radius: 3px;
  91. height: 18px;
  92. line-height: 18px;
  93. }
  94. }
  95. .highlight {
  96. pre {
  97. margin: 0;
  98. }
  99. code.hljs {
  100. margin: 0;
  101. border: none;
  102. max-height: none;
  103. border-radius: 0;
  104. &::before {
  105. content: none;
  106. }
  107. }
  108. }
  109. .demo-block-control {
  110. border-top: solid 1px #eaeefb;
  111. height: 44px;
  112. box-sizing: border-box;
  113. background-color: #fff;
  114. border-bottom-left-radius: 4px;
  115. border-bottom-right-radius: 4px;
  116. text-align: center;
  117. margin-top: -1px;
  118. color: #d3dce6;
  119. cursor: pointer;
  120. position: relative;
  121. &.is-fixed {
  122. position: fixed;
  123. bottom: 0;
  124. width: 868px;
  125. }
  126. i {
  127. font-size: 16px;
  128. line-height: 44px;
  129. transition: .3s;
  130. &.hovering {
  131. transform: translateX(-40px);
  132. }
  133. }
  134. > span {
  135. position: absolute;
  136. transform: translateX(-30px);
  137. font-size: 14px;
  138. line-height: 44px;
  139. transition: .3s;
  140. display: inline-block;
  141. }
  142. &:hover {
  143. color: #409EFF;
  144. background-color: #f9fafc;
  145. }
  146. & .text-slide-enter,
  147. & .text-slide-leave-active {
  148. opacity: 0;
  149. transform: translateX(10px);
  150. }
  151. .control-button {
  152. line-height: 26px;
  153. position: absolute;
  154. top: 0;
  155. right: 0;
  156. font-size: 14px;
  157. padding-left: 5px;
  158. padding-right: 25px;
  159. }
  160. }
  161. }
  162. </style>
  163. <script type="text/babel">
  164. import compoLang from '../i18n/component.json';
  165. import Element from 'main/index.js';
  166. import { stripScript, stripStyle, stripTemplate } from '../util';
  167. const { version } = Element;
  168. export default {
  169. data() {
  170. return {
  171. codepen: {
  172. script: '',
  173. html: '',
  174. style: ''
  175. },
  176. hovering: false,
  177. isExpanded: false,
  178. fixedControl: false,
  179. scrollParent: null
  180. };
  181. },
  182. methods: {
  183. goCodepen() {
  184. // since 2.6.2 use code rather than jsfiddle https://blog.codepen.io/documentation/api/prefill/
  185. const { script, html, style } = this.codepen;
  186. const resourcesTpl = '<scr' + 'ipt src="//unpkg.com/vue/dist/vue.js"></scr' + 'ipt>' +
  187. '\n<scr' + `ipt src="//unpkg.com/element-ui@${ version }/lib/index.js"></scr` + 'ipt>';
  188. let jsTpl = (script || '').replace(/export default/, 'var Main =').trim();
  189. let htmlTpl = `${resourcesTpl}\n<div id="app">\n${html.trim()}\n</div>`;
  190. let cssTpl = `@import url("//unpkg.com/element-ui@${ version }/lib/theme-chalk/index.css");\n${(style || '').trim()}\n`;
  191. jsTpl = jsTpl
  192. ? jsTpl + '\nvar Ctor = Vue.extend(Main)\nnew Ctor().$mount(\'#app\')'
  193. : 'new Vue().$mount(\'#app\')';
  194. const data = {
  195. js: jsTpl,
  196. css: cssTpl,
  197. html: htmlTpl
  198. };
  199. const form = document.getElementById('fiddle-form') || document.createElement('form');
  200. while (form.firstChild) {
  201. form.removeChild(form.firstChild);
  202. }
  203. form.method = 'POST';
  204. form.action = 'https://codepen.io/pen/define/';
  205. form.target = '_blank';
  206. form.style.display = 'none';
  207. const input = document.createElement('input');
  208. input.setAttribute('name', 'data');
  209. input.setAttribute('type', 'hidden');
  210. input.setAttribute('value', JSON.stringify(data));
  211. form.appendChild(input);
  212. document.body.appendChild(form);
  213. form.submit();
  214. },
  215. scrollHandler() {
  216. const { top, bottom, left } = this.$refs.meta.getBoundingClientRect();
  217. this.fixedControl = bottom > document.documentElement.clientHeight &&
  218. top + 44 <= document.documentElement.clientHeight;
  219. this.$refs.control.style.left = this.fixedControl ? `${ left }px` : '0';
  220. },
  221. removeScrollHandler() {
  222. this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);
  223. }
  224. },
  225. computed: {
  226. lang() {
  227. return this.$route.path.split('/')[1];
  228. },
  229. langConfig() {
  230. return compoLang.filter(config => config.lang === this.lang)[0]['demo-block'];
  231. },
  232. blockClass() {
  233. return `demo-${ this.lang } demo-${ this.$router.currentRoute.path.split('/').pop() }`;
  234. },
  235. iconClass() {
  236. return this.isExpanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom';
  237. },
  238. controlText() {
  239. return this.isExpanded ? this.langConfig['hide-text'] : this.langConfig['show-text'];
  240. },
  241. codeArea() {
  242. return this.$el.getElementsByClassName('meta')[0];
  243. },
  244. codeAreaHeight() {
  245. if (this.$el.getElementsByClassName('description').length > 0) {
  246. return this.$el.getElementsByClassName('description')[0].clientHeight +
  247. this.$el.getElementsByClassName('highlight')[0].clientHeight + 20;
  248. }
  249. return this.$el.getElementsByClassName('highlight')[0].clientHeight;
  250. }
  251. },
  252. watch: {
  253. isExpanded(val) {
  254. this.codeArea.style.height = val ? `${ this.codeAreaHeight + 1 }px` : '0';
  255. if (!val) {
  256. this.fixedControl = false;
  257. this.$refs.control.style.left = '0';
  258. this.removeScrollHandler();
  259. return;
  260. }
  261. setTimeout(() => {
  262. this.scrollParent = document.querySelector('.page-component__scroll > .el-scrollbar__wrap');
  263. this.scrollParent && this.scrollParent.addEventListener('scroll', this.scrollHandler);
  264. this.scrollHandler();
  265. }, 200);
  266. }
  267. },
  268. created() {
  269. const highlight = this.$slots.highlight;
  270. if (highlight && highlight[0]) {
  271. let code = '';
  272. let cur = highlight[0];
  273. if (cur.tag === 'pre' && (cur.children && cur.children[0])) {
  274. cur = cur.children[0];
  275. if (cur.tag === 'code') {
  276. code = cur.children[0].text;
  277. }
  278. }
  279. if (code) {
  280. this.codepen.html = stripTemplate(code);
  281. this.codepen.script = stripScript(code);
  282. this.codepen.style = stripStyle(code);
  283. }
  284. }
  285. },
  286. mounted() {
  287. this.$nextTick(() => {
  288. let highlight = this.$el.getElementsByClassName('highlight')[0];
  289. if (this.$el.getElementsByClassName('description').length === 0) {
  290. highlight.style.width = '100%';
  291. highlight.borderRight = 'none';
  292. }
  293. });
  294. },
  295. beforeDestroy() {
  296. this.removeScrollHandler();
  297. }
  298. };
  299. </script>