|
|
@ -1,12 +1,16 @@ |
|
|
|
<template> |
|
|
|
<template> |
|
|
|
<div class="position-relative"> |
|
|
|
<div class="position-relative"> |
|
|
|
<div @click="copyCode" class="position-absolute" style="top: 10px; right: 10px;"> |
|
|
|
<div @click="copyCode" class="position-absolute" style="top: 10px; right: 10px;"> |
|
|
|
<template v-if="copied"> |
|
|
|
<button class="copy text-secondary" :class="copied ? 'd-none' : ''" data-bs-toggle="tooltip" |
|
|
|
<code>Copied!</code> |
|
|
|
data-bs-placement="top" data-bs-title="Copy to clipboard"> |
|
|
|
</template> |
|
|
|
|
|
|
|
<button class="copy text-secondary" title="Copy code"> |
|
|
|
|
|
|
|
<IFaClone /> |
|
|
|
<IFaClone /> |
|
|
|
</button> |
|
|
|
</button> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<button class="copy text-secondary" :class="!copied ? 'd-none' : ''" data-bs-toggle="tooltip" |
|
|
|
|
|
|
|
data-bs-placement="top" data-bs-title="Copied!" data-bs-trigger="manual" data-bs-show |
|
|
|
|
|
|
|
ref="stickyTooltipElement"> |
|
|
|
|
|
|
|
<IFaCheck class="text-success" /> |
|
|
|
|
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<pre :class="$props.class"><code class="language-{{ language }}"><slot /></code></pre> |
|
|
|
<pre :class="$props.class"><code class="language-{{ language }}"><slot /></code></pre> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -30,7 +34,9 @@ button.copy:hover { |
|
|
|
</style> |
|
|
|
</style> |
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref } from "vue" |
|
|
|
import { nextTick, ref, watch } from "vue" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const bootstrap = useNuxtApp().$bootstrap; |
|
|
|
|
|
|
|
|
|
|
|
defineProps({ |
|
|
|
defineProps({ |
|
|
|
code: { |
|
|
|
code: { |
|
|
@ -59,19 +65,48 @@ defineProps({ |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const copied = ref(false); |
|
|
|
const copied = ref<boolean>(false); |
|
|
|
|
|
|
|
|
|
|
|
const copyCode = async (e: Event) => { |
|
|
|
const copyCode = async (e: Event) => { |
|
|
|
try { |
|
|
|
try { |
|
|
|
const target = e.currentTarget as HTMLElement; |
|
|
|
const target = e.currentTarget as HTMLElement; |
|
|
|
const element = target.nextElementSibling as HTMLPreElement | null; |
|
|
|
const element = target.nextElementSibling as HTMLPreElement | null; |
|
|
|
const textToCopy = element ? element.textContent.trim() : ""; |
|
|
|
const textToCopy = element ? element.textContent!.trim() : ""; |
|
|
|
|
|
|
|
|
|
|
|
await navigator.clipboard.writeText(textToCopy); |
|
|
|
await navigator.clipboard.writeText(textToCopy); |
|
|
|
copied.value = true; |
|
|
|
copied.value = true; |
|
|
|
setTimeout(() => (copied.value = false), 2000); |
|
|
|
setTimeout(() => { copied.value = false; }, 2000); |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
console.error('Failed to copy:', err) |
|
|
|
console.error('Failed to copy:', err); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clicked tooltip |
|
|
|
|
|
|
|
const stickyTooltip = ref<any>(); |
|
|
|
|
|
|
|
const stickyTooltipElement = ref<HTMLInputElement | null>(null); |
|
|
|
|
|
|
|
watch(copied, (newValue, oldValue) => { |
|
|
|
|
|
|
|
if (newValue !== oldValue) { |
|
|
|
|
|
|
|
if (newValue) { |
|
|
|
|
|
|
|
// Ensure the DOM is updated before executing |
|
|
|
|
|
|
|
nextTick(() => { |
|
|
|
|
|
|
|
// @ts-ignore |
|
|
|
|
|
|
|
stickyTooltip.value = new bootstrap.Tooltip(stickyTooltipElement.value, { trigger: "manual" }); |
|
|
|
|
|
|
|
stickyTooltip.value.show(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
if (stickyTooltip.value) { |
|
|
|
|
|
|
|
stickyTooltip.value.dispose(); |
|
|
|
|
|
|
|
stickyTooltip.value = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => { |
|
|
|
|
|
|
|
if (stickyTooltip.value) { |
|
|
|
|
|
|
|
stickyTooltip.value.dispose(); |
|
|
|
|
|
|
|
stickyTooltip.value = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
</script> |
|
|
|
</script> |
|
|
|