vp-demo.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <script setup lang="ts">
  2. import { computed, getCurrentInstance, toRef } from 'vue'
  3. import { isClient, useClipboard, useToggle } from '@vueuse/core'
  4. import { CaretTop } from '@element-plus/icons-vue'
  5. import { useLang } from '../composables/lang'
  6. import { useSourceCode } from '../composables/source-code'
  7. import { usePlayground } from '../composables/use-playground'
  8. import demoBlockLocale from '../../i18n/component/demo-block.json'
  9. import Example from './demo/vp-example.vue'
  10. import SourceCode from './demo/vp-source-code.vue'
  11. const props = defineProps<{
  12. demos: object
  13. source: string
  14. path: string
  15. rawSource: string
  16. description?: string
  17. }>()
  18. const vm = getCurrentInstance()!
  19. const { copy, isSupported } = useClipboard({
  20. source: decodeURIComponent(props.rawSource),
  21. read: false,
  22. })
  23. const [sourceVisible, toggleSourceVisible] = useToggle()
  24. const lang = useLang()
  25. const demoSourceUrl = useSourceCode(toRef(props, 'path'))
  26. const formatPathDemos = computed(() => {
  27. const demos = {}
  28. Object.keys(props.demos).forEach(key => {
  29. demos[key.replace('../../examples/', '').replace('.vue', '')] = props.demos[key].default
  30. })
  31. return demos
  32. })
  33. const locale = computed(() => demoBlockLocale[lang.value])
  34. const decodedDescription = computed(() => decodeURIComponent(props.description!))
  35. const onPlaygroundClick = () => {
  36. const { link } = usePlayground(props.rawSource)
  37. if (!isClient) return
  38. window.open(link)
  39. }
  40. const copyCode = async () => {
  41. const { $message } = vm.appContext.config.globalProperties
  42. if (!isSupported) {
  43. $message.error(locale.value['copy-error'])
  44. }
  45. try {
  46. await copy()
  47. $message.success(locale.value['copy-success'])
  48. } catch (e: any) {
  49. $message.error(e.message)
  50. }
  51. }
  52. </script>
  53. <template>
  54. <ClientOnly>
  55. <!-- danger here DO NOT USE INLINE SCRIPT TAG -->
  56. <p text="sm" v-html="decodedDescription" />
  57. <div class="example">
  58. <Example :file="path" :demo="formatPathDemos[path]" />
  59. <ElDivider class="m-0" />
  60. <div class="op-btns">
  61. <ElTooltip :content="locale['edit-in-editor']" :show-arrow="false">
  62. <ElIcon :size="16" class="op-btn">
  63. <i-ri-flask-line @click="onPlaygroundClick" />
  64. </ElIcon>
  65. </ElTooltip>
  66. <ElTooltip :content="locale['edit-on-github']" :show-arrow="false">
  67. <ElIcon :size="16" class="op-btn github" style="color: var(--text-color-light)">
  68. <a :href="demoSourceUrl" rel="noreferrer noopener" target="_blank">
  69. <i-ri-github-line />
  70. </a>
  71. </ElIcon>
  72. </ElTooltip>
  73. <ElTooltip :content="locale['copy-code']" :show-arrow="false">
  74. <ElIcon :size="16" class="op-btn" @click="copyCode">
  75. <i-ri-file-copy-line />
  76. </ElIcon>
  77. </ElTooltip>
  78. <ElTooltip :content="locale['view-source']" :show-arrow="false">
  79. <ElIcon :size="16" class="op-btn" @click="toggleSourceVisible()">
  80. <i-ri-code-line />
  81. </ElIcon>
  82. </ElTooltip>
  83. </div>
  84. <ElCollapseTransition>
  85. <SourceCode v-show="sourceVisible" :source="source" />
  86. </ElCollapseTransition>
  87. <Transition name="el-fade-in-linear">
  88. <div v-show="sourceVisible" class="example-float-control" @click="toggleSourceVisible(false)">
  89. <ElIcon :size="16">
  90. <CaretTop />
  91. </ElIcon>
  92. <span>{{ locale['hide-source'] }}</span>
  93. </div>
  94. </Transition>
  95. </div>
  96. </ClientOnly>
  97. </template>
  98. <style scoped lang="scss">
  99. .example {
  100. border: 1px solid var(--border-color);
  101. border-radius: var(--el-border-radius-base);
  102. .op-btns {
  103. padding: 0.5rem;
  104. display: flex;
  105. align-items: center;
  106. justify-content: flex-end;
  107. height: 2.5rem;
  108. .el-icon {
  109. &:hover {
  110. color: var(--text-color);
  111. }
  112. }
  113. .op-btn {
  114. margin: 0 0.5rem;
  115. cursor: pointer;
  116. color: var(--text-color-lighter);
  117. transition: 0.2s;
  118. &.github a {
  119. transition: 0.2s;
  120. color: var(--text-color-lighter);
  121. &:hover {
  122. color: var(--text-color);
  123. }
  124. }
  125. }
  126. }
  127. &-float-control {
  128. display: flex;
  129. align-items: center;
  130. justify-content: center;
  131. border-top: 1px solid var(--border-color);
  132. height: 44px;
  133. box-sizing: border-box;
  134. background-color: var(--bg-color, #fff);
  135. border-bottom-left-radius: 4px;
  136. border-bottom-right-radius: 4px;
  137. margin-top: -1px;
  138. color: var(--el-text-color-secondary);
  139. cursor: pointer;
  140. position: sticky;
  141. left: 0;
  142. right: 0;
  143. bottom: 0;
  144. z-index: 10;
  145. span {
  146. font-size: 14px;
  147. margin-left: 10px;
  148. }
  149. &:hover {
  150. color: var(--el-color-primary);
  151. }
  152. }
  153. }
  154. </style>