Adicionando botão para copiar code snippets no Gatsby (gambiarra)

Não quer criar um plugin para o Gatsby? Então este post é para você.

Escrito em 5 de maio de 2022 - 🕒 2 min. de leitura

Eu constantemente escrevo sobre programação e compartilho trechos de código nos meus posts, e copiar e colar código é uma habilidade essencial para qualquer programador, e como selecionar um texto e pressionar CTRL + C então CTRL + V é muito trabalhoso, eu decidi adicionar um botão de cópia a todos os meus códigos para facilitar a vida de todos os meus colegas programadores da internet.

Possíveis soluções

A maneira correta de fazer isso é criar um plugin para acessar o processo de geração de HTML do Gatsby e acessar o objeto que contém o código na estrutura de dados AST. Existe um tutorial muito bom feito pelo @thundermiracle sobre como fazer isso.

Claro, eu decidi fazer isso do jeito preguiçoso e com gambiarras, que é alterando a string HTML na função createPages no arquivo gatsby-node.js.

O código

Dentro do loop que cria as páginas dos posts, eu adicionei o seguinte código no arquivo gatsby-node.js:

exports.createPages = async function ({ actions, graphql }) {
    const { data } = await graphql(` /* some graphQL query */ `);
    data.allMarkdownRemark.edges.forEach((edge) => {
        const alternativeHtml = generateAlternativeHtml(edge.node.html);
        actions.createPage({
            component: require.resolve(`./src/templates/PostTemplate.jsx`),
            context: { alternativeHtml },
        })
    })
}

Para a função generateAlternativeHtml, usarei o pacote jsdom para analisar a string HTML e convertê-la em um objeto DOM e usar o método querySelectorAll para encontrar todos os nodes com código no post e adicione o botão de cópia a eles.

function generateAlternativeHtml(html) {
    const dom = new JSDOM(html);
    const { document } = dom.window;
    const codeSnippets = document.querySelectorAll('.gatsby-highlight');

    codeSnippets.forEach((codeSnippet) => {
        const wrapper = document.createElement('div');
        wrapper.classList.add('copy-code-block');

        // add your own css styles here, css classes, etc.
        const copyButton = document.createElement('button');
        copyButton.innerHTML = 'Copy';
        codeSnippet.insertAdjacentHTML(`afterend`, copyButton);
    });

    return document.body.innerHTML;
}

Agora todos os nodes de código terão um botão de cópia, mas o botão não faz nada. Para resolver isso, vou adicionar eventos onclick aos botões de cópia para quando o botão for clicado, copiar o código. Isso precisa ser feito na parte React do código com um useEffect no arquivo PostTemplate.jsx.

Para copiar o código usarei o pacote clipboard-copy.

import copy from 'clipboard-copy';

const BlogPostTemplate = ({ data, pageContext }) => {
    const { alternativeHtml } = pageContext;

    useEffect(() => {
        const codeSnippets = document.querySelectorAll('.copy-code-block button');
        codeSnippets.forEach((elementWrapper) => {
            codeSnippet.addEventListener('click', () => {
                const codeSnippet = elementWrapper.parentElement.querySelector('.gatsby-highlight');
                const code = codeSnippet.innerHTML;
                copy(code);
            });
        });
    }, []);

    return (
        <section>
            <h1>{data.markdownRemark.frontmatter.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: alternativeHtml }} />
        </section>
    );
};

Feito! Super gambiarrado, but hey, it works! :)

Conclusão

Essa solução adiciona um pouco de sobrecarga ao processo Static Site Generator e alguma manipulação de DOM no cliente, mas é uma boa solução por enquanto. Se você está procurando uma solução rápida e fácil, este é o melhor jeito de fazer.

É isso pessoal, espero que esse post tenha sido útil para você.

Tags:


Publicar um comentário

Comentários

Nenhum comentário.