Creando botones propios para Pulse Editor

Educación online efectiva

Aprende con expertos de la industria

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

Una de las características de Pulse Editor es que podemos configurar cuáles botones de todos los que nos provee el editor queremos usar. Pero no solo podemos usar esos botones, también podemos crear botones propios.

Si ya vieron como implementar Pulse Editor pueden luego crear botones propios. Vamos a ver cómo hacer esto.

Botón base

El editor nos provee de un editor base que podemos usar. Este es un simple wrapper de la etiqueta <button /> para colocar siempre ciertos atributos. Pueden acceder a este componente simplemente con un import.

import { Base } from 'pulse-editor/buttons`

Luego ya pueden hacer render y pasarle lo que quieran, al final del día es un botón, lo únicos props que deben pasar sí o sí son:

  • children => los componentes hijos, pueden ser lo que quieran.
  • name => el nombre del botón, en el componente <Bold /> este es bold, así que pueden darse una idea.
  • onClick => la función que se va a ejecutar cuando el usuario haga click sobre el botón.

El botón además define un className por defecto llamado PulseEditor-button (que pueden cambiar si pasan su propio className), define disabled como false (también sobre escribible) y fuerza que el type sea button. Porque por defecto las etiquetas <button /> son de tipo submit. Así evitamos que cualquier botón del editor envíe un posible formulario que envuelva al editor.

Creando el botón

Vamos a crear un botón para la aplicación de escritorio Pulse. Este botón va a encargarse de crear un nuevo archivo en el editor. Como Pulse usa Next.js no necesitamos importar React, pero como vamos a crear un componente de clase entonces sí vamos a importar Component.

import { Component } from 'react';

Luego importamos el botón base como vimos antes y vamos a iniciar a crear nuestro componente.

import { Component } from 'react'
import { Base } from 'pulse-editor/buttons'

export default class NewButton extends Component {
	render = () => (
		<Base onClick={this.handleClick} name='new'>
			<span title='New file [CMD+N]'>
				New
			</span>
		</Base>
	)
}

Con eso nuestro botón ya hace render de un botón normal de nuestro editor y definimos dos cosas extras sobre este: la primera es que tenemos que crear un handleClick y la segunda es que vamos a usar el atajo de teclado cmd+n en Mac o ctrl+n en Windows y Linux.

Definiendo el atajo de teclado

Vamos primero a definir nuestro atajo de teclado. Para eso necesitamos acceder a dos funciones desde el contexto de React a los cuales vamos a tener acceso siempre que nuestro botón se renderice dentro del componente <Editor />.

import { Component } from 'react'
import { Base } from 'pulse-editor/buttons'
import { func } from 'prop-types' // importamos el prop-type func para definir funciones

export default class NewButton extends Component {
	static contextTypes = {
		setShortcut: func.isRequired, // esta función nos permite definir un shortcut
		removeShortcut: func.isRequired, // esta función nos permite quitar un shortcut
	}

	render = () => (
		<Base onClick={this.handleClick} name='new'>
			<span title='New file [CMD+N]'>
				New
			</span>
		</Base>
	)
}

Con eso tenemos acceso a la función. Ahora vamos a usar estas funciones, la primera se usa en componentDidMount para agregar nuestro atajo de teclado y la segunda se usa en componentWillUnmount cuando el componente se vaya a desmontar.

import { Component } from 'react'
import { Base } from 'pulse-editor/buttons'
import { func } from 'prop-types'
import isMac from 'pulse-editor/built/utils/is-mac' // importamos un util interno de pulse-editor para saber si estamos en Mac

export default class NewButton extends Component {
	static contextTypes = {
		setShortcut: func.isRequired,
		removeShortcut: func.isRequired,
	}

	componentDidMount() {
		this.context.setShortcut({
			ctrlKey: !isMac(), // si no estamos en Mac usamos la tecla control
			metaKey: isMac(), // si estamos en Mac usamos la tecla meta (command)
			altKey: false, // no vamos a usar la tecla alt
			shiftKey: false, // no vamos a usar la tecla shift
			keyName: 'n', // vamos a usar la tecla N (cmd+n o ctrl+n)
			updater: selected => selected, // nuestra función updater (más abajo vemos que es)
			handler: event => event.selection, // nuestro función handler (más abajo vemos que es)
		})
	}

	componentWillUnmount() {
		// eliminamos el atajo de teclado que usa la tecla N al desmontar el componente
		this.context.removeShortcut({ keyName: 'n' })
	}

	render = () => (
		<Base onClick={this.handleClick} name='new'>
			<span title='New file [CMD+N]'>
				New
			</span>
		</Base>
	)
}

Así configuramos atajos de teclados. Como vemos al momento de definir uno indicamos que atajo se va a usar, si vamos a usar las teclas ctrl, meta, alt y shift y otra tecla. Le tecla meta puede significar cmd (command) en Mac o win (windows) en teclados no-mac en Windows y Linux.

Luego definimos dos funciones, updater y handler. Esta es probablemente la parte más complicada. La función updater recibe los siguientes datos:

  • selected => el texto seleccionado por el usuario al momento de ejecutarse updater
  • value => el valor completo actual del editor
  • selection => un objeto que nos indica la posición de selected dentro de value

Esta función updater devuelve un nuevo string que sobreescribe selected dentro de value.

Por ejemplo si tenemos el texto hola como value y ol como selected entonces el objeto selection va a venir con { start: 1, end: 3 }. Si updater devuelve **ol** entonces el valor completo nuevo va a ser h**ol**a.

La función handler se ejecuta luego de aplicar updater y actualizar el contenido del editor. Esto nos permite modificar la posición del cursor del usuario (para cambiar el texto seleccionado) o hacer cualquier cosa, por ejemplo mandar eventos de analytics.

Esta función debe devolver un objeto similar a selection, con las propiedades start y end y recibe un objeto event con las siguientes propiedades.

  • value => el valor completo actualizado
  • field => el elemento del DOM del textarea
  • updated => el valor generado por la función updater
  • selected => el texto seleccionado original
  • selection => la posición del selected dentro del valor original
  • Todas las propiedades de un evento nativo del DOM

Luego de convertir ol a **ol** podemos decidir entre mantener seleccionado ol o **ol**, para el primero caso handler debe devolver { start: 3, end: 5 } y para el segundo { start: 1, end: 7 }.

Programando la funcionalidad

Vamos a programar la funcionalidad propia del botón, ya que hasta ahora el botón no hace nada de verdad.

import { Component } from 'react'
import { Base } from 'pulse-editor/buttons'
import { ipcRenderer } from 'electron' // módulo para de Electron para comunicarse con el proceso principal
import { func } from 'prop-types'
import isMac from 'pulse-editor/built/utils/is-mac'
import Icon from 'react-icons/lib/fa/file-o' // componente de ícono que renderiza un SVG directo

export default class NewButton extends Component {
	static contextTypes = {
		setShortcut: func.isRequired,
		setFileName: func.isRequired, // función propia de la aplicación de escritorio Pulse para definir el nombre del archivo abierto actualmente
		removeShortcut: func.isRequired,
		writeValue: func.isRequired // función de Pulse Editor para sobreescribir el valor actual del editor
	}

	componentDidMount() {
		 // escuchamos el evento `new-file` que llega desde el proceso main
		ipcRenderer.on('new-file', this.createFile)
		this.context.setShortcut({
			ctrlKey: !isMac(),
			metaKey: isMac(),
			altKey: false,
			shiftKey: false,
			keyName: 'n',
			updater: selected => selected,
			handler: event => event.selection,
		})
	}

	componentWillUnmount() {
		// dejamos de escuchar el evento `new-file` que llega desde el proceso main
		ipcRenderer.removeListener('new-file', this.createFile)
		this.context.removeShortcut({ keyName: 'n' })
	}

	createFile = () => {
		// cuando creamos un nuevo archivo
		// limpiamos el nombre del archivo actual de la aplicación de escritorio
		this.context.setFileName(undefined)
		// cambiamos el valor actual del editor a un string vacío
		this.context.writeValue({ target: { value: '' } })
	}

	handleClick = () => this.createFile() // cuando se haga click ejecutamos la función `createFile`

	render = () => (
		<Base onClick={this.handleClick} name='new'>
			<span title='New file [CMD+N]'>
				New
			</span>
		</Base>
	)
}

Ese es el botón completo, la funcionalidad principal del editor está definida en createFile, en la que limpiamos el nombre de archivo y el valor actual del editor. La razón de limpiar el nombre de archivo es porque en nuestra aplicación de escritorio si abrimos un archivo o guardamos uno mantenemos la ruta completa del archivo para poder volver a guardarlo en el mismo lugar. Y vaciamos el valor del editor para limpiarlo.

Luego tenemos una conexión con el proceso main de Electron para enterarnos cuando este nos pida crear un nuevo archivo.

Implementando nuestro botón

Con lo que hicimos ya tenemos nuestro botón programado. Ahora para usarlo es tan simple como renderizarlo dentro del componente <Editor />.

import { Component } from 'react'
import { Editor, ButtonBar, ButtonGroup, Field, Preview, EmojiBar } from 'pulse-editor'
import { Bold, Italic, Underline } from 'pulse-editor/buttons'
import Head from 'next/head'

// importamos nuestro botón
import New from '../components/buttons/new-button.js'

export default () => (
	<Editor>
		<Head>
			<link
				rel='stylesheet'
				href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'
			/>
			<link
				rel='stylesheet'
				href='https://raw.githubusercontent.com/PlatziDev/pulse-editor/master/examples/full-usage/static/styles.css'
			/>
		</Head>
		<ButtonBar>
			<ButtonGroup>
				<Bold><i className="fa fa-bold" /></Bold>
				<Italic><i className="fa fa-italic" /></Italic>
				<Underline><i className="fa fa-underline" /></Underline>
			</ButtonGroup>
			<ButtonGroup>
				<New /> {/* renderizamos nuestro botón */}
			</ButtonGroup>
		</ButtonBar>
		<div className="PulseEditor-content">
			<Field>
			<Preview />
		</div>
		<EmojiBar />
	</Editor>
)

Como se ve, usar un botón personalizado es tan simple como cualquier otro botón, con eso pueden empezar a crear botones propios para cualquier funcionalidad extra que deseen incluir.

Palabras finales

Crear un botón puede parecer complicado pero en general depende de que tantas cosas queremos hacer con nuestro botón, un botón de bold es super simple, un botón como este que se conecta con Electron es claramente mucho más complejo y así y todo no es tanto código al final del día.

Educación online efectiva

Aprende con expertos de la industria

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

0 Comentarios

para escribir tu comentario

Artículos relacionados