type Options = { /** The time before the copied status is reset. */ delay: number; }; /** Use this hook to copy text to the clipboard and show a copied state. * * ## Usage * ```svelte * * * * ``` * */ export class UseClipboard { #copiedStatus = $state<'success' | 'failure'>(); private delay: number; private timeout: ReturnType | undefined = undefined; constructor({ delay = 500 }: Partial = {}) { this.delay = delay; } /** Copies the given text to the users clipboard. * * ## Usage * ```ts * clipboard.copy('Hello, World!'); * ``` * * @param text * @returns */ async copy(text: string) { if (this.timeout) { this.#copiedStatus = undefined; clearTimeout(this.timeout); } this.#copiedStatus = await copyText(text); this.timeout = setTimeout(() => { this.#copiedStatus = undefined; }, this.delay); return this.#copiedStatus; } /** true when the user has just copied to the clipboard. */ get copied() { return this.#copiedStatus === 'success'; } /** Indicates whether a copy has occurred * and gives a status of either `success` or `failure`. */ get status() { return this.#copiedStatus; } } export async function copyText(text: string): Promise<'success' | 'failure'> { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return 'success'; } // when navigator.clipboard is unavailable we fallback to this for wider browser compatibility const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.top = '0'; textArea.style.left = '0'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); const successful = document.execCommand('copy'); document.body.removeChild(textArea); return successful ? 'success' : 'failure'; } catch { return 'failure'; } }