Un botón de copia para Highlight.js (Español (Spanish))

Un botón de copia para Highlight.js

Comments

NOTE: Apart from English (and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.

Saturday, 28 September 2024

//

5 minute read

Introducción

En este sitio utilizo Hightlight.js para renderizar fragmentos de código del lado del cliente. Me gusta esto ya que mantiene mi código del lado del servidor limpio y simple. Sin embargo, quería añadir un botón de copia a cada fragmento de código para que los usuarios pudieran copiar fácilmente el código a su portapapeles. Esta es una tarea sencilla, pero pensé que lo documentaría aquí para cualquier otra persona que quisiera hacer lo mismo.

Oh y esto es todo lo que me está esperando para añadir la funcionalidad del boletín de noticias para realmente aparecer en el sitio. Tan pronto como tenga la energía para hacerlo, añadiré esto.

El punto final es que tenemos un botón de copia como este en el sitio: Botón de copia

NOTA: Todo el crédito para este artículo va a Faraz Patankar ¿Quién es el artículo que usé para crear este? Sólo quería documentar los cambios que hice aquí para mi propia referencia y compartir con otros.

Las opciones

Hay un cou;le de maneras de hacer esto, por ejemplo hay un Complemento del botón de copia para Higlight.js pero decidí que quería más control sobre el botón y el estilo. Así que me encontré este artículo para añadir un botón de copia.

Los problemas

Aunque ese artículo es un gran enfoque, tuvo un par de problemas que dejaron de ser perfecto para mí:

  1. Utiliza una fuente que no uso en mi sitio (pero luego manualmente tiene el SVG también, no estoy seguro de por qué).
  // Lucide copy icon
    copyButton.innerHTML = `<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;

Mientras que esto funciona, ya uso BoxIcons en este sitio que tiene un copiar icono en allí ya.

  1. Usa una biblioteca de tostadas que no tengo en este sitio.
  // Notify user that the content has been copied
      toast.success("Copied to clipboard", {
        description: "The code block content has been copied to the clipboard.",
      });
  1. Noquea el flujo de y en el bloque de código y pone el icono en la parte inferior que no quería. Así que el mío está arriba a la derecha.

Mis adaptaciones

La función principal

Este plugin se engancha en el after:highlightElement evento y añade un botón al bloque de código.

Así que primero copié el código de Faraz y luego hice los siguientes cambios:

  1. En lugar de añadirlo al final del bloque de código lo preparo al principio.
  2. En lugar del SVG usé la versión BoxIcons simplemente añadiendo esas clases al botón insertado y configurando el tamaño de texto text-xl.
  3. Quité la notificación de tostadas y la reemplacé por una simple showToast función que tengo en mi sitio (ver más adelante)
  4. He añadido un aria-label y title al botón para la accesibilidad (y para dar un agradable efecto de suspensión).
hljs.addPlugin({
    "after:highlightElement": ({ el, text }) => {
        const wrapper = el.parentElement;
        if (wrapper == null) {
            return;
        }

        /**
         * Make the parent relative so we can absolutely
         * position the copy button
         */
        wrapper.classList.add("relative");
        const copyButton = document.createElement("button");
        copyButton.classList.add(
            "absolute",
            "top-2",
            "right-1",
            "p-2",
            "text-gray-500",
            "hover:text-gray-700",
            "bx",
            "bx-copy",
            "text-xl",
            "cursor-pointer"
        );
        copyButton.setAttribute("aria-label", "Copy code to clipboard");
        copyButton.setAttribute("title", "Copy code to clipboard");

        copyButton.onclick = () => {
            navigator.clipboard.writeText(text);

            // Notify user that the content has been copied
            showToast("The code block content has been copied to the clipboard.", 3000, "success");

        };
        // Append the copy button to the wrapper
        wrapper.prepend(copyButton);
    },
});

showToast Función

Esto depende de una navaja parcial que añadí a mi proyecto. Este parcial utiliza la DaisyUI Componente de tostada para mostrar un mensaje al usuario. Me gusta este enfoque ya que mantiene el Javascript limpio y sencillo y me permite peinar el mensaje de brindis de la misma manera que el resto del sitio.

<div id="toast" class="toast toast-bottom fixed z-50 hidden overflow-y-hidden">
    <div id="toast-message" class="alert">
        <div>
            <span id="toast-text">Notification message</span>
        </div>
    </div>
    <p class="hidden right-1 bx-copy  cursor-pointer alert-success alert-warning alert-error alert-info"></p>
</div>

Usted notará que esto también tiene un extraño oculto p etiqueta en la parte inferior, esto es sólo para Tailwind para analizar estas clases cuando se construye CSS del sitio.

La función Javascript es simple, sólo muestra el mensaje de brindis para un tiempo establecido y luego lo oculta de nuevo.

window.showToast = function(message, duration = 3000, type = 'success') {
    const toast = document.getElementById('toast');
    const toastText = document.getElementById('toast-text');
    const toastMessage = document.getElementById('toast-message');

    // Set message and type
    toastText.innerText = message;
    toastMessage.className = `alert alert-${type}`; // Change alert type (success, warning, error)

    // Show the toast
    toast.classList.remove('hidden');

    // Hide the toast after specified duration
    setTimeout(() => {
        toast.classList.add('hidden');
    }, duration);
}

Entonces podemos llamar a esto usando el showToast función en la copyButton.onclick evento.

showToast("The code block content has been copied to the clipboard.", 3000, "success");

He añadido esta derecha parcial en la parte superior de mi _Layout.cshtml archivo por lo que está disponible en cada página.

<partial name="_Toast"  />``

Ahora, cuando mostramos los posts del blog los bloques de código tienen: Botón de copia

Conclusión

Así que eso es todo, un simple cambio en el código de Faraz para que funcione para mí. Espero que esto ayude a alguien más.

logo

©2024 Scott Galloway