|
|
<script lang="ts"> |
|
|
import { onDestroy } from "svelte"; |
|
|
|
|
|
import IconCopy from "./icons/IconCopy.svelte"; |
|
|
import Tooltip from "./Tooltip.svelte"; |
|
|
|
|
|
interface Props { |
|
|
classNames?: string; |
|
|
value: string; |
|
|
children?: import("svelte").Snippet; |
|
|
onClick?: () => void; |
|
|
} |
|
|
|
|
|
let { classNames = "", value, children, onClick }: Props = $props(); |
|
|
|
|
|
let isSuccess = $state(false); |
|
|
let timeout: ReturnType<typeof setTimeout>; |
|
|
|
|
|
const unsecuredCopy = (text: string) => { |
|
|
|
|
|
|
|
|
const textArea = document.createElement("textarea"); |
|
|
textArea.value = text; |
|
|
document.body.appendChild(textArea); |
|
|
textArea.focus(); |
|
|
textArea.select(); |
|
|
document.execCommand("copy"); |
|
|
document.body.removeChild(textArea); |
|
|
|
|
|
return Promise.resolve(); |
|
|
}; |
|
|
|
|
|
const copy = async (text: string) => { |
|
|
if (window.isSecureContext && navigator.clipboard) { |
|
|
return navigator.clipboard.writeText(text); |
|
|
} |
|
|
return unsecuredCopy(text); |
|
|
}; |
|
|
|
|
|
const handleClick = async () => { |
|
|
try { |
|
|
await copy(value); |
|
|
|
|
|
isSuccess = true; |
|
|
if (timeout) { |
|
|
clearTimeout(timeout); |
|
|
} |
|
|
timeout = setTimeout(() => { |
|
|
isSuccess = false; |
|
|
}, 1000); |
|
|
} catch (err) { |
|
|
console.error(err); |
|
|
} |
|
|
}; |
|
|
|
|
|
onDestroy(() => { |
|
|
if (timeout) { |
|
|
clearTimeout(timeout); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
|
|
|
<button |
|
|
class={classNames} |
|
|
title={"Copy to clipboard"} |
|
|
type="button" |
|
|
onclick={() => { |
|
|
onClick?.(); |
|
|
handleClick(); |
|
|
}} |
|
|
> |
|
|
<div class="relative"> |
|
|
{#if children}{@render children()}{:else} |
|
|
<IconCopy classNames="h-[1.14em] w-[1.14em]" /> |
|
|
{/if} |
|
|
|
|
|
<Tooltip classNames={isSuccess ? "opacity-100" : "opacity-0"} /> |
|
|
</div> |
|
|
</button> |
|
|
|