Files
erahn-v2-fe/src/components/docs/PreviewCode.vue
T
2026-10-27 09:36:11 +08:00

89 lines
2.8 KiB
Vue

<script lang="ts" setup>
import { twMerge } from "tailwind-merge";
import { FileScan, Clipboard, ArrowDown, Check } from "@lucide/vue";
import { ref, onMounted, onUnmounted } from "vue";
const { title = "" } = defineProps<{
title?: string;
}>();
const expand = ref(false);
const copied = ref(false);
const codeRef = ref<HTMLDivElement | null>(null);
let copyTimeout: ReturnType<typeof setTimeout> | null = null;
onMounted(() => {
if (codeRef.value) {
if (
codeRef.value.offsetHeight < 300 &&
!(codeRef.value.offsetParent === null)
) {
expand.value = true;
}
}
});
onUnmounted(() => {
if (copyTimeout) clearTimeout(copyTimeout);
});
function handleCopy() {
const codeElement = codeRef.value?.querySelector("code");
if (!codeElement) return;
const text = codeElement.textContent || "";
navigator.clipboard.writeText(text).then(() => {
copied.value = true;
if (copyTimeout) clearTimeout(copyTimeout);
copyTimeout = setTimeout(() => {
copied.value = false;
}, 3000);
});
}
</script>
<template>
<div class="mt-8 mb-10 border border-foreground/20 bg-background/50 relative">
<div class="flex items-center py-2 px-5 gap-5">
<FileScan v-if="title" class="stroke-1 size-4 fill-foreground/10" />
<div class="flex items-center w-full gap-2">
<div>{{ title }}</div>
<div @click="expand = !expand" :class="twMerge([
'ms-auto mr-2 cursor-pointer px-3.5 py-1 relative after:w-px after:h-5 after:inset-y-0 after:my-auto after:bg-foreground/20 after:right-0 after:absolute',
codeRef &&
codeRef.offsetHeight < 300 &&
!(codeRef.offsetParent === null) &&
'opacity-0',
])
">
{{ expand ? "Collapse" : "Expand" }}
</div>
<Check v-if="copied" :class="twMerge([
'cursor-pointer size-4 stroke-1 fill-foreground/10',
!title && 'mt-1',
])
" />
<Clipboard v-else @click="handleCopy" :class="twMerge([
'cursor-pointer size-4 stroke-1 fill-foreground/10',
!title && 'mt-1',
])
" />
</div>
</div>
<div :class="twMerge([
'text-xs px-5 border-t border-foreground/20 overflow-x-auto overflow-y-hidden before:h-3/4 before:absolute before:bg-linear-to-b before:from-transparent before:to-background/80 before:inset-x-0 before:bottom-0',
expand && 'max-h-auto before:hidden',
!expand && 'max-h-100',
!title && '-mt-13 border-0',
])
" ref="codeRef">
<pre><code><slot /></code></pre>
<div v-if="!expand" @click="expand = true"
class="absolute bottom-0 inset-x-0 mx-auto w-26 mb-8 py-1 cursor-pointer border border-foreground/50 bg-foreground/15 flex items-center justify-center gap-2 backdrop-blur">
<ArrowDown class="size-4" /> Expand
</div>
</div>
</div>
</template>