<script setup lang="ts"> import { computed, getCurrentInstance, toRef } from 'vue' import { isClient, useClipboard, useToggle } from '@vueuse/core' import { CaretTop } from '@element-plus/icons-vue' import { useLang } from '../composables/lang' import { useSourceCode } from '../composables/source-code' import { usePlayground } from '../composables/use-playground' import demoBlockLocale from '../../i18n/component/demo-block.json' import Example from './demo/vp-example.vue' import SourceCode from './demo/vp-source-code.vue' const props = defineProps<{ demos: object source: string path: string rawSource: string description?: string }>() const vm = getCurrentInstance()! const { copy, isSupported } = useClipboard({ source: decodeURIComponent(props.rawSource), read: false, }) const [sourceVisible, toggleSourceVisible] = useToggle() const lang = useLang() const demoSourceUrl = useSourceCode(toRef(props, 'path')) const formatPathDemos = computed(() => { const demos = {} Object.keys(props.demos).forEach((key) => { demos[key.replace('../../examples/', '').replace('.vue', '')] = props.demos[key].default }) return demos }) const locale = computed(() => demoBlockLocale[lang.value]) const decodedDescription = computed(() => decodeURIComponent(props.description!) ) const onPlaygroundClick = () => { const { link } = usePlayground(props.rawSource) if (!isClient) return window.open(link) } const copyCode = async () => { const { $message } = vm.appContext.config.globalProperties if (!isSupported) { $message.error(locale.value['copy-error']) } try { await copy() $message.success(locale.value['copy-success']) } catch (e: any) { $message.error(e.message) } } </script> <template> <ClientOnly> <!-- danger here DO NOT USE INLINE SCRIPT TAG --> <p text="sm" v-html="decodedDescription" /> <div class="example"> <Example :file="path" :demo="formatPathDemos[path]" /> <ElDivider class="m-0" /> <div class="op-btns"> <ElTooltip :content="locale['edit-in-editor']" :show-arrow="false"> <ElIcon :size="16" class="op-btn"> <i-ri-flask-line @click="onPlaygroundClick" /> </ElIcon> </ElTooltip> <ElTooltip :content="locale['edit-on-github']" :show-arrow="false"> <ElIcon :size="16" class="op-btn github" style="color: var(--text-color-light)" > <a :href="demoSourceUrl" rel="noreferrer noopener" target="_blank"> <i-ri-github-line /> </a> </ElIcon> </ElTooltip> <ElTooltip :content="locale['copy-code']" :show-arrow="false"> <ElIcon :size="16" class="op-btn" @click="copyCode"> <i-ri-file-copy-line /> </ElIcon> </ElTooltip> <ElTooltip :content="locale['view-source']" :show-arrow="false"> <ElIcon :size="16" class="op-btn" @click="toggleSourceVisible()"> <i-ri-code-line /> </ElIcon> </ElTooltip> </div> <ElCollapseTransition> <SourceCode v-show="sourceVisible" :source="source" /> </ElCollapseTransition> <Transition name="el-fade-in-linear"> <div v-show="sourceVisible" class="example-float-control" @click="toggleSourceVisible(false)" > <ElIcon :size="16"> <CaretTop /> </ElIcon> <span>{{ locale['hide-source'] }}</span> </div> </Transition> </div> </ClientOnly> </template> <style scoped lang="scss"> .example { border: 1px solid var(--border-color); border-radius: var(--el-border-radius-base); .op-btns { padding: 0.3rem; display: flex; align-items: center; justify-content: flex-end; height: 2rem; .el-icon { &:hover { color: var(--text-color); } } .op-btn { margin: 0 0.5rem; cursor: pointer; color: var(--text-color-lighter); transition: 0.2s; &.github a { transition: 0.2s; color: var(--text-color-lighter); &:hover { color: var(--text-color); } } } } &-float-control { display: flex; align-items: center; justify-content: center; border-top: 1px solid var(--border-color); height: 44px; box-sizing: border-box; background-color: var(--bg-color, #fff); border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; margin-top: -1px; color: var(--el-text-color-secondary); cursor: pointer; position: sticky; left: 0; right: 0; bottom: 0; z-index: 10; span { font-size: 14px; margin-left: 10px; } &:hover { color: var(--el-color-primary); } } } </style>