/ src / components / CodeCopy.svelte
CodeCopy.svelte
 1  <script lang="ts">
 2  import { onMount } from "svelte";
 3  import CheckIcon from "~icons/ph/check";
 4  import CopyIcon from "~icons/ph/copy";
 5  
 6  onMount(() => {
 7  	const codeBlocks = document.querySelectorAll("pre");
 8  
 9  	codeBlocks.forEach((pre) => {
10  		const wrapper = document.createElement("div");
11  		wrapper.className = "relative group";
12  
13  		pre.parentNode?.insertBefore(wrapper, pre);
14  		wrapper.appendChild(pre);
15  
16  		const button = document.createElement("button");
17  		button.className =
18  			"absolute top-2 right-2 p-2 rounded bg-pink-950/80 text-pink-50 opacity-0 group-hover:opacity-100 transition-opacity duration-200 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-pink-400 hover:bg-pink-900 z-10";
19  		button.setAttribute("aria-label", "Copy code to clipboard");
20  		button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="M216 32H88a8 8 0 0 0-8 8v16H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-16h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-8 144H48V64h16v96a8 8 0 0 0 8 8h136Zm32-32h-24V48H96v-8h144Z"/></svg>`;
21  
22  		button.addEventListener("click", async () => {
23  			const code = pre.textContent || "";
24  			try {
25  				await navigator.clipboard.writeText(code);
26  				button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="m232.5 71.5l-128 128a12.1 12.1 0 0 1-17 0l-56-56a12 12 0 0 1 17-17L96 175L215.5 55.5a12 12 0 0 1 17 17Z"/></svg>`;
27  				button.classList.add("bg-green-700/80");
28  				button.classList.remove("bg-pink-950/80");
29  
30  				setTimeout(() => {
31  					button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path fill="currentColor" d="M216 32H88a8 8 0 0 0-8 8v16H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-16h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-8 144H48V64h16v96a8 8 0 0 0 8 8h136Zm32-32h-24V48H96v-8h144Z"/></svg>`;
32  					button.classList.remove("bg-green-700/80");
33  					button.classList.add("bg-pink-950/80");
34  				}, 2000);
35  			} catch (err) {
36  				console.error("Failed to copy:", err);
37  			}
38  		});
39  
40  		wrapper.appendChild(button);
41  	});
42  
43  	return () => {
44  		codeBlocks.forEach((pre) => {
45  			const wrapper = pre.parentElement;
46  			if (wrapper?.classList.contains("group")) {
47  				const button = wrapper.querySelector("button");
48  				if (button) button.remove();
49  				wrapper.parentNode?.insertBefore(pre, wrapper);
50  				wrapper.remove();
51  			}
52  		});
53  	};
54  });
55  </script>