Cocoon のコードブロックにコピーボタンを追加(2)

前回の記事で、Cocoon のコードブロックにコピーボタンを追加してみたものの、表示がやっぱり質素で、もう少しかっこいい表示にしたい。

前回の記事:

そこで、フリーの SVG アイコン素材を用いて、コピーボタンとなるように修正してみた。

完成後の表示イメージ:

大筋のコードは前回と概ね同じで、ボタンの部分を SVG 画像に差し替え。
push down でエフェクトが付くように、mousedown, mouseup, mouseleave に addEventListener を追加した。

JavaScript:

document.addEventListener("DOMContentLoaded", () => {
    svgImage = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="24" height="24">
<!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> 
<path d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zM362 464H54a6 6 0 0 1 -6-6V150a6 6 0 0 1 6-6h42v224c0 26.5 21.5 48 48 48h224v42a6 6 0 0 1 -6 6zm96-96H150a6 6 0 0 1 -6-6V54a6 6 0 0 1 6-6h308a6 6 0 0 1 6 6v308a6 6 0 0 1 -6 6z"/>
</svg>`;

    function createSvgElement(htmlText) {
        element = new DOMParser().parseFromString(htmlText, "text/html").body.firstElementChild;
        element.style.fill = "rgb(200, 200, 200)";
        element.style.opacity = "0.8";
        return element;
    }
    svgElement = createSvgElement(svgImage);

    // Get all <pre> elements
    const preElements = document.querySelectorAll("pre");

    preElements.forEach(pre => {
        const code = pre.querySelector("code");

        if (code) {
            // Create a copy button
            const copyButton = document.createElement("button");
            copyButton.style.position = "absolute";
            copyButton.style.top = "5px";
            copyButton.style.right = "5px";
            copyButton.style.width = "35px";
            copyButton.style.height = "35px";
            copyButton.style.border = "none";
            copyButton.style.background = "transparent";
            copyButton.style.cursor = "pointer";

            // Add SVG icon
            copyButton.appendChild(svgElement.cloneNode(true));

            // Add tooltip
            copyButton.title = "Copy to clipboard";

            // Add push effect
            copyButton.addEventListener("mousedown", () => {
                copyButton.style.transform = "translateY(2px)";
            });

            copyButton.addEventListener("mouseup", () => {
                copyButton.style.transform = "translateY(0)";
            });

            copyButton.addEventListener("mouseleave", () => {
                copyButton.style.transform = "translateY(0)";
            });

            // Add click action
            copyButton.addEventListener("click", () => {
                const codeText = code.textContent;
                navigator.clipboard.writeText(codeText).then(() => {
                    // Show copied message
                    const copiedMessage = document.createElement("div");
                    copiedMessage.textContent = "Copied!";
                    copiedMessage.style.position = "absolute";
                    copiedMessage.style.top = "-30px";
                    copiedMessage.style.right = "5px";
                    copiedMessage.style.backgroundColor = "black";
                    copiedMessage.style.color = "white";
                    copiedMessage.style.padding = "5px 10px";
                    copiedMessage.style.borderRadius = "3px";
                    copiedMessage.style.fontSize = "12px";
                    copiedMessage.style.zIndex = "10";
                    copiedMessage.style.opacity = "1";
                    copiedMessage.style.transition = "opacity 0.5s";

                    pre.parentNode.appendChild(copiedMessage);

                    setTimeout(() => {
                        copiedMessage.style.opacity = "0";
                        copiedMessage.addEventListener("transitionend", () => {
                            copiedMessage.remove();
                        });
                    }, 1500);
                }).catch(err => {
                    console.error("Failed to copy", err);
                });
            });

            // To maintain the position of the copy button, wrap the <pre> element in a div element
            const container = document.createElement("div");
            container.style.position = "relative";
            container.style.width = "100%";

            pre.parentNode.insertBefore(container, pre);
            container.appendChild(pre);
            container.appendChild(copyButton);
        }
    });
});

JavaScript は、「外観」→「テーマファイルエディター」→「javascript.js」から追加


コピーボタンのデザインは、Zenn を参考にさせていただきました。
https://zenn.dev/