Ghost CMS: Як додати кнопку копіювання до блоків коду

Хочете додати кнопку копіювання до фрагментів коду в Ghost CMS? За замовчуванням Ghost цього не підтримує, але є швидке рішення! Додаємо простий скрипт у Code Injection, змінюємо стилі та отримуємо зручну кнопку копіювання. Дізнайтеся, як це зробити за кілька хвилин!

Ghost CMS: Як додати кнопку копіювання до блоків коду
Photo by Karsten Winegeart on Unsplash

Вступ

Привіт. Шукаєш як покращити функціональність свого блогу?

За замовчуванням Ghost CMS не надає жодних можливостей для швидкого копіювання коду із фрагментів. Але для користувачів цієї опції дуже не вистачає, бо всі вже дуже давно звикли мати її у правому куті фрагментів коду.

Що тут казати про кнопку копіювання, коли Ghost CMS навать не надавав підтримку підсвітки синтаксису для різних типів та мов програмування.

Тему з підсвічування синтаксису ми вже проходили у іншому дописі.

Підсвічування коду - Ghost
Швидкий спосіб додати підсвідку коду за допомогою Prism до блогу Ghost і не тільки. Підготуємо файли та підключимо їх через панель адміністратора

Сьогодні ж ми дуже швидко додамо кнопку копіювання до фрагментів коду вашого блогу.

Скрипт

Я людина лінива і тому теж спочатку провів пошук, де дуже швидко знайшов гілку на форумі Ghost та відповіль RyanF з якої стало ясно, що потрібно лише додати ще один скрипт в розділі Code Injection мого блогу.

Також Ryan зазначив, що для Prism є якись плагін. Я його знайшов - Copy to Clipboard Button - Prism Plugin, проте всеодно використав код від Ryan (бо швидко)

Ось цей код потрібно додати у Site footer. Доречі раджу одразу ж і скористатись кнопкою копіювання у правому куті. Просто натисніть на слово html.

<script>
function initCodeCopy() {
    const codeBlocks = document.querySelectorAll('code[class*="language-"]');

    codeBlocks.forEach((block) => {
        const lang = parseLanguage(block);
        const referenceEl = block.parentElement;
        const parent = block.parentElement.parentElement;
        
        const wrapper = document.createElement('div');
        wrapper.className = 'code-wrapper';
        parent.insertBefore(wrapper, referenceEl);
        wrapper.append(block.parentElement);

        const copyBtn = document.createElement('button');
        copyBtn.setAttribute('class', 'copy-button');
        copyBtn.setAttribute('data-lang', lang);
        copyBtn.innerHTML = `${lang} <svg viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.007-1H7zM5.003 8L5 20h10V8H5.003zM9 6h8v10h2V4H9v2z" fill="currentColor"/></svg>`;

        wrapper.insertAdjacentElement('beforeend', copyBtn);
    });

    function parseLanguage(block) {
        const className = block.className;
        if (className.startsWith('language')) {
            const [prefix, lang] = className.split('-');
            return lang;
        }
    }

    function copy(e) {
        const btn = e.currentTarget;
        const lang = btn.dataset.lang;

        const text = e.currentTarget.previousSibling.children[0].textContent;

        navigator.clipboard.writeText(text).then(
            () => {
                btn.innerHTML = `copied! <svg viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.007-1H7zm2 0h8v10h2V4H9v2z" fill="currentColor"/></svg>`;
                btn.setAttribute('style', 'opacity: 1');
                
            },
            () => alert('failed to copy'),
        );

        setTimeout(() => {
            btn.removeAttribute('style');
            btn.innerHTML = `${lang} <svg viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.007-1H7zM5.003 8L5 20h10V8H5.003zM9 6h8v10h2V4H9v2z" fill="currentColor"/></svg>`;
        }, 3000);
    }

    const copyButtons = document.querySelectorAll('.copy-button');

    copyButtons.forEach((btn) => {
        btn.addEventListener('click', copy);
    });
}

document.addEventListener("DOMContentLoaded", initCodeCopy)
</script>

Стилі

Зверніть увагу, що ви можете з легкістю змінити кольори або інші параметри відображення кнопки копіювання.

Стилі також додайте після скрипта та змініть потрібні параметри за бажанням.

<style>
/* Custom styles for theme */
.code-wrapper {
    position: relative;
    overflow: hidden;
    border-radius: 8px;
}

.code-wrapper > pre[class*="language-"] {
    margin-top: 0;
}
.copy-button {
    position: absolute;
    top: 5px;
    right: 5px;
    display: flex;
    align-items: center;
    color: var(--ghost-accent-color);
    cursor: pointer;
    background: transparent;
    background-color: #2d2d2d;
    border: none;
}

.copy-button svg {
    width: 1em;
    margin-left: 0.25em;
    opacity: 0.5;
    transition: opacity 0.3s;
}

.copy-button:hover svg {
    opacity: 1;
}
</style>

Цими стилями я намагався максимально наблизити вигляд кнопки до тієї, що ви можете побачити в статті A complete guide to code snippets

Після збереження внесених змін перезавантажте сторінку і користуйтесь кнопкою!

Дякую RyanF за швидке вирішення моєї проблеми.