> Manuales > Manual de ProseMirror para la creación de editores de texto con Javascript

Cómo hacer un botón en la página para ejecutar un comando en un editor de texto creado con el framework Prosemirror. Ejemplo de botón para introducir una negrita en el documento con la marca bold.

Hacer un botón para negrita en un editor ProseMirror

En este artículo vamos a crear nuestro primer botón que ejecuta un comando, de una manera sencilla, para adquirir un conocimiento más sólido de los comandos y cómo ejecutarlos en un editor creado con ProseMirror.

Recuerda que ya hemos abordado anteriormente el tema de los comandos, en un artículo previamente publicado del Manual de ProseMirror, por lo que no empezamos justamente desde cero. Por favor, si no leíste ese artículo, tómate el tiempo necesario para repasarlo en: Comandos en ProseMirror.

Qué son marks en ProseMirror

Antes de comenzar a ver cómo realizar un comando para poner en negrita, vamos a explicar alguna cosa más sobre el objeto que modeliza el documento editable en ProseMirror.

Como dijimos anteriormente, este documento editable no es justamente un HTML, por diversos motivos que facilita el desarrollo de la funcionalidad del editor y la manera con la que se generará código a partir de él.

En lugar de HTML, ProseMirror usa un modelo de datos para poder definir la estructura de un documento, que tiene forma de árbol, como el propio DOM de la página en el navegador. Sin embargo, a diferencia del DOM, no todas las etiquetas generan un nodo del árbol. Hay algunas etiquetas "inline" como la negrita o la cursiva que simplemente son "marcas" dentro de un nodo.

Estas marcas, que en la documentación se llaman "marks" pueden aplicarse a un nodo de manera múltiple. Por ejemplo podemos tener un texto que tiene una "mark" de negrita (bold), pero puede haber otro nodo que tiene dos "mark" a la vez, como negrita y cursiva.

Comando para poner y quitar una mark

Ya sabiendo que un elemento como una negrita se asocia mediante una marca, vamos a ver el comando que nos permitiría asociar negrita a un pedazo de texto del editor, o quitarle la negrita si ya la tenía anteriormente.

El comando que nos interesa en este caso se llama toggleMark y permite poner y quitar una marca en el editor. Funciona de manera que, si tiene la marca, entonces la quita y si no tiene la marca, entonces la pone.

En la documentación la tienen especificada de con la siguiente cabecera de función.

toggleMark(markType: MarkType, attrs⁠?: Attrs = null) 

Pero ojo, esta función toggleMark no es un comando en sí, sino que devuelve un comando!! Puedes entonces pensar en ella como un generador de comandos, donde al crear ese comando debemos indicar qué tipo de marca queremos configurar (poner o quitar) cuando se ejecute el comando generado.

El motivo de tener un generador de comandos es porque así tienes un comando toggleMark para cualquier tipo de marca que quieras colocar, siempre que esté en tu schema.

Como puedes ver, toggleMark() recibe dos parámetros:

Así que, si quiero generar un comando para hacer una negrita, escribo este código:

import { schema } from "prosemirror-schema-basic";
import { toggleMark } from "prosemirror-commands";

const boldCommand = toggleMark(schema.marks.strong);

Ejecutando un comando ProseMirror con combinación de teclas

En el artículo pasado acerca de comandos en Prosemirror ejecutamos los comandos por medio de combinaciones de teclas configuradas en el editor. Vamos a refrescar ese conocimiento para ver cómo podríamos poner y quitar una negrita usando las teclas Ctrl + b (Cmd + b si estás en MacOS).

Esto se hacía con el plugin keymap, al que le pasábamos un objeto con las combinaciones de teclas que queremos crear y los comandos que se deben ejecutar.

keymap({
   "Mod-b": boldCommand, 
})

A su vez, los plugins configurados en ProseMirror se indican al crear el objeto de estado del editor, con un código como este.

let state = EditorState.create({
  schema,
  plugins: [
    keymap({
      "Mod-b": boldCommand, 
    })
  ],
});

Estoy ahorrándome colocar los correspondientes imports. Todo esto ya lo hemos visto anteriormente en el manual, por lo que espero que no haya problemas. Luego, en este mismo artículo, verás todo el código junto de este ejemplo por si hubiera dudas.

Ejecutando un comando como respuesta a la pulsación de un botón

En el ejemplo anterior, el paso de ejecutar el comando se realiza digamos que automáticamente por el plugin keymap, cuando se produce la pulsación de teclas. Sin embargo, el comando en sí requiere algunas acciones extra si lo queremos ejecutar por nosotros mismos.

Ahora vamos a ver cómo cambiaría nuestra aplicación cuando el comando lo queremos lanzar mediante nuestro propio código. Pero antes debemos saber algo más sobre los comandos de ProseMirror.

Un comando ProrseMirror es una función

Básicamente debemos saber que un comando en ProseMirror es una función. Cuando ejecutamos esa función se produce el efecto configurado en el comando. Por tanto, debemos de conocer un poco mejor cómo son las funciones de los comandos, porque ellas tienen algunas particularidades importantes para que se puedan ejecutar bien.

Lo que debemos saber es que todo comando requiere enviar parámetros sobre el contexto donde se debe ejecutar. Esos parámetros son básicamente los siguientes:

Ambos parámetros los podemos obtener del objeto view generado al instanciar el EditorView.

Por tanto, nuestro código para poder ejecutar un comando será algo parecido a esto:

let view = new EditorView(document.getElementById('editor'), {
  state,
});
boldCommand(view.state, view.dispatch);

El comando además devuelve un boleano para indicar si se pudo realizar ese comando correctamente.

Crear un botón para la poner o quitar una mark de negrita

Ya solamente nos queda poner un botón en cualquier lugar de la página. Puedes hacerlo perfectamente en tu HTML.

<button id="button-bold">Bold</button>

Ahora asociar un manejador de eventos para este botón, que se encargue de la ejecución del comando sobre la vista del editor.

document.getElementById('button-bold').addEventListener('click', function() {
  boldCommand(view.state, view.dispatch);
  view.focus();
});

El comando se ejecuta tal cual hemos explicado anteriormente. Pero además hemos agregado la invocación a un método de la vista nuevo para nosotros: view.focus().

El método focus() sobre la vista sirve, como te imaginarás, para poner el foco de la aplicación de nuevo sobre el editor. Esto es necesario porque, si no lo colocas, al pulsar el botón el foco de la aplicación se quedaría sobre el botón y lo suyo es que se vuelva al editor, de modo que el usuario pueda seguir escribiendo cosas en él.

Conclusión y código del ejemplo completo

Hemos visto cómo ejecutar comandos cuando se produzcan acciones por parte del usuario, lo que nos permitirá avanzar bastante y aportar funcionalidades al editor de diversos tipos. Esta es la base que nos facilitará la creación de toolbars para nuestros editores. Claro que estamos todavía bastante lejos de construir un toolbar y necesitamos aprender varias cosas antes, pero iremos viendo todas estas cosas en el manual.

Para finalizar este artículo vamos a colocar el código del ejemplo completo de nuestro editor con el comando para colocar negritas.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Crear un botón de bold en Prosemirror</title>
  <style>
    #editor {
      border: 1px solid blue;
    }

    .ProseMirror {      
      /* Poner aquí todos los estilos de ProseMirror que vimos en artículos anteriores */
    }

  </style>
</head>

<body>
  <h1>Crear un botón de bold en Prosemirror</h1>

  <div id="editor"></div>

  <button id="button-bold">Bold</button>

  <hr>
  <a href="../index.html">Volver</a>

  <script type="module">
    import { schema } from "prosemirror-schema-basic";
    import { EditorState } from "prosemirror-state";
    import { EditorView } from "prosemirror-view";
    import { keymap } from "prosemirror-keymap";
    import {
      baseKeymap,
      toggleMark
    } from "prosemirror-commands";

    const boldCommand = toggleMark(schema.marks.strong);

    let state = EditorState.create({
      schema,
      plugins: [
        keymap(baseKeymap),
        keymap({
          "Mod-b": boldCommand, 
        })
      ],
    });
    let view = new EditorView(document.getElementById('editor'), {
      state,
    });

    document.getElementById('button-bold').addEventListener('click', function() {
      boldCommand(view.state, view.dispatch);
      view.focus();
    })
  </script>
</body>

</html>

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual