demo-block.vue 8.0 KB

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