Con esta guía pretendemos mostrar cómo crear bloques para Gutenberg, recordemos que el proyecto gutenberg cambiara el editor TinyMCE de WordPress por un nuevo editor que le dara al usuario final la posibilidad de maquetar sus páginas visualmente.
Todos los contenidos se podrán cargar desde bloques, hablamos de un párrafo, una imagen, un botón, video , audio, todo dependerá de la creatividad de los desarrolladores. Los bloques serán la nueva unidad de información que le permitirá al webmaster maquetar su página web.
Los bloques para el nuevo editor de WordPress se desarrollan principalmente en los lenguajes de JavaScript ES5 y ESNext y esto podrìa intimidar a cualquier desarrollador WordPress. En este momento no hay mucha documentación para construir bloques personalizados, pero recopilando distintos articulos que mencionaremos al final logramos construir un bloque personalizado para un perfil de Usuario. Aprenderemos mucho en el proceso.
El Bloque de perfil presentara una imagen, un nombre, un título, la biografía y enlaces de redes sociales. Este bloque podrá ser «Editable» por el usuario, con controles personalizados y opciones de inspector. Esencialmente, utiliza todos los aspectos de los bloques.
El plugin de “Bloque de perfil” está disponible en Github y en el directorio de plugins de WordPress. Este Bloque de perfil requiere el complemento Gutenberg activo en este momento.Esto hasta que el nuevo editor de WordPress se integre oficialmente.
Construir bloques para gutenberg usando ES5 ( JavaScript) tiene almenos 2 ventajas. Primero, es lo más fácil de entender para los desarrolladores WordPress que no tienen mucha experiencia con javascript. En segundo lugar, ES5 es compatible con navegadores modernos. Entonces, los programadores WordPress pueden desarrollarse localmente sin necesidad de un compilador como Babel.
Antes de profundizar en el código, es importante observar la estructura general de archivos que tendra nuestro plugin. Por si no lo sabian, debemos crear un plugin que habilite un nuevo bloque personalizado en gutenberg. Esto nos dará una mejor comprensión de los componentes necesarios que componen un bloque.
Comenzaremos por poner en cola nuestros scripts y estilos en el archivo index.php. Este es el grueso de PHP con el que trabajaremos.
Primero, encolaremos nuestro archivo personalizado block.js y los estilos para ver el bloque dentro del editor:
function organic_profile_block_editor_assets() { // Scripts. wp_enqueue_script( 'organic-profile-block', // Handle. plugins_url( 'block.js', __FILE__ ), // Block.js: We register the block here. array( 'wp-blocks', 'wp-i18n', 'wp-element' ), // Dependencies, defined above. filemtime( plugin_dir_path( __FILE__ ) . 'block.js' ) // filemtime — Gets file modification time. ); // Styles. wp_enqueue_style( 'organic-profile-block-editor', // Handle. plugins_url( 'editor.css', __FILE__ ), // Block editor CSS. array( 'wp-edit-blocks' ), // Dependency to include the CSS after it. filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' ) // filemtime — Gets file modification time. ); } // End function organic_profile_block_editor_assets(). // Hook: Editor assets. add_action( 'enqueue_block_editor_assets', 'organic_profile_block_editor_assets' );
A continuación, encolaremos los estilos para el front-end:
function organic_profile_block_block_assets() { // Styles. wp_enqueue_style( 'organic-profile-block-frontend', // Handle. plugins_url( 'style.css', __FILE__ ), // Block frontend CSS. array( 'wp-blocks' ), // Dependency to include the CSS after it. filemtime( plugin_dir_path( __FILE__ ) . 'style.css' ) // filemtime — Gets file modification time. ); wp_enqueue_style( 'organic-profile-block-fontawesome', // Handle. plugins_url( 'font-awesome.css', __FILE__ ) // Font Awesome for social media icons. ); } // End function organic_profile_block_block_assets(). // Hook: Frontend assets. add_action( 'enqueue_block_assets', 'organic_profile_block_block_assets' );
Dentro del archivo block.js, comenzaremos envolviendo nuestro bloque en una función con sus dependencias. Las dependencias deben coincidir con lo que se definió previamente en nuestro archivo index.php:
function( blocks, i18n, element ) { // Our custom block code. } )( window.wp.blocks, window.wp.i18n, window.wp.element, );
A continuación, importaremos los componentes necesarios para nuestro bloque. Hay muchos componentes disponibles. Lamentablemente, no encontramos una biblioteca que enumere todos los componentes con descripciones. Entonces, encontrar y usar los componentes correctos ha sido un proceso de prueba y error.
( function( blocks, i18n, element ) { var el = element.createElement; var children = blocks.source.children; var BlockControls = wp.blocks.BlockControls; var AlignmentToolbar = wp.blocks.AlignmentToolbar; var MediaUploadButton = wp.blocks.MediaUploadButton; var InspectorControls = wp.blocks.InspectorControls; var TextControl = wp.blocks.InspectorControls.TextControl; } )( window.wp.blocks, window.wp.i18n, window.wp.element, );
blocks.registerBlockType( 'organic/profile-block', { // The name of our block. Must be a string with prefix. Example: my-plugin/my-custom-block. title: i18n.__( 'Profile' ), // The title of our block. icon: 'businessman', // Dashicon icon for our block. Custom icons can be added using inline SVGs. category: 'common', // The category of the block. attributes: { // Necessary for saving block content. title: { type: 'array', source: 'children', selector: 'h3', }, subtitle: { type: 'array', source: 'children', selector: 'h5', }, bio: { type: 'array', source: 'children', selector: 'p', }, mediaID: { type: 'number', }, mediaURL: { type: 'string', source: 'attribute', selector: 'img', attribute: 'src', }, href: { type: 'url', }, alignment: { type: 'string', default: 'center', }, facebookURL: { type: 'url', }, twitterURL: { type: 'url', }, instagramURL: { type: 'url', }, linkedURL: { type: 'url', }, emailAddress: { type: 'string', } }, } );
Ahora que hemos registrado nuestro bloque personalizado, necesitamos construir la interfaz. La interfaz de bloque se compone de 2 partes. La función de edición es la interfaz para el bloque dentro del editor. La función de guardar guarda la salida del bloque para el front-end. Si eres como yo, y JavaScript no es tu fortaleza, puede ser más fácil pensar en definir los elementos de la misma forma que lo harías con las etiquetas para HTML. Por ejemplo, tomemos este elemento de bloque simple:
el( 'div', { className: 'components-block-description' }, el( 'p', {}, i18n.__( 'Add links to your social media profiles.' ) ), ),
Es esencialmente lo mismo que este HTML:
<div class="components-block-description"> <p>Add links to your social media profiles.</p> </div>
Voy a dividir la función de edición en 4 partes: declarar variables, controles de bloque, controles de inspector y los elementos de nuestro bloque.
Primero, configuraremos nuestra función de edición y declararemos las variables:
edit: function( props ) { var focus = props.focus; var focusedEditable = props.focus ? props.focus.editable || 'title' : null; var alignment = props.attributes.alignment; var attributes = props.attributes; var facebookURL = props.attributes.facebookURL; var twitterURL = props.attributes.twitterURL; var instagramURL = props.attributes.instagramURL; var linkedURL = props.attributes.linkedURL; var emailAddress = props.attributes.emailAddress; var onSelectImage = ( media ) => { props.setAttributes( { mediaURL: media.url, mediaID: media.id, } ); }; var onSetHref = ( value ) => { props.setAttributes( { href: value, } ); }; function onChangeAlignment( newAlignment ) { props.setAttributes( { alignment: newAlignment } ); } return [ // We build the editor interface of our block here. ]; },
A continuación, configuremos nuestros controles de bloque. Esta sección define los controles disponibles dentro del editor TinyMCE para el bloque. En este caso, estamos agregando controles de alineación de contenido y un botón de carga multimedia para agregar y cambiar nuestra imagen de perfil:
focus && el( // Display controls when the block is clicked on. blocks.BlockControls, { key: 'controls' }, el( blocks.AlignmentToolbar, { value: alignment, onChange: onChangeAlignment, } ), el( 'div', { className: 'components-toolbar' }, el( blocks.MediaUploadButton, { buttonProps: { className: 'components-icon-button components-toolbar__control', 'aria-label': i18n.__( 'Edit image' ), }, onSelect: onSelectImage, onChange: onSetHref, type: 'image', url: attributes.href, }, el( 'svg', { className: 'dashicon dashicons-edit', width: '20', height: '20' }, el( 'path', { d: "M2.25 1h15.5c.69 0 1.25.56 1.25 1.25v15.5c0 .69-.56 1.25-1.25 1.25H2.25C1.56 19 1 18.44 1 17.75V2.25C1 1.56 1.56 1 2.25 1zM17 17V3H3v14h14zM10 6c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm3 5s0-6 3-6v10c0 .55-.45 1-1 1H5c-.55 0-1-.45-1-1V8c2 0 3 4 3 4s1-3 3-3 3 2 3 2z" } ) ) ) ) ),
A continuación, agregaremos nuestros controles de inspector. Estas son las opciones de bloque adicionales que se pueden configurar haciendo clic en la pestaña «Bloque» dentro del editor. Para el Bloque de perfil, los enlaces de las redes sociales se agregan dentro de los controles del inspector:
focus && el( blocks.InspectorControls, { key: 'inspector' }, el( 'div', { className: 'components-block-description' }, // A brief description of our block in the inspector. el( 'p', {}, i18n.__( 'Add links to your social media profiles.' ) ), ), el( 'h2', {}, i18n.__( 'Social Media Links' ) ), // A title for our social media link options. el( TextControl, { type: 'url', label: i18n.__( 'Facebook URL' ), value: facebookURL, onChange: function( newFacebook ) { props.setAttributes( { facebookURL: newFacebook } ); }, } ), el( TextControl, { type: 'url', label: i18n.__( 'Twitter URL' ), value: twitterURL, onChange: function( newTwitter ) { props.setAttributes( { twitterURL: newTwitter } ); }, } ), el( TextControl, { type: 'url', label: i18n.__( 'Instagram URL' ), value: instagramURL, onChange: function( newInstagram ) { props.setAttributes( { instagramURL: newInstagram } ); }, } ), el( TextControl, { type: 'url', label: i18n.__( 'LinkedIn URL' ), value: linkedURL, onChange: function( newLinkedIn ) { props.setAttributes( { linkedURL: newLinkedIn } ); }, } ), el( TextControl, { type: 'url', label: i18n.__( 'Email Address' ), value: emailAddress, onChange: function( newEmail ) { props.setAttributes( { emailAddress: newEmail } ); }, } ), ),
Ahora, construiremos los elementos de bloque dentro del editor:
el( 'div', { className: props.className }, el( 'div', { className: attributes.mediaID ? 'organic-profile-image image-active' : 'organic-profile-image image-inactive', style: attributes.mediaID ? { backgroundImage: 'url('+attributes.mediaURL+')' } : {} }, el( blocks.MediaUploadButton, { buttonProps: { className: attributes.mediaID ? 'image-button' : 'components-button button button-large', }, onSelect: onSelectImage, type: 'image', value: attributes.mediaID, }, attributes.mediaID ? el( 'img', { src: attributes.mediaURL } ) : 'Upload Image' ), ), el( 'div', { className: 'organic-profile-content', style: { textAlign: alignment } }, el( blocks.Editable, { tagName: 'h3', inline: false, placeholder: i18n.__( 'Profile Name' ), value: attributes.title, onChange: function( newTitle ) { props.setAttributes( { title: newTitle } ); }, focus: focusedEditable === 'title' ? focus : null, onFocus: function( focus ) { props.setFocus( _.extend( {}, focus, { editable: 'title' } ) ); }, } ), el( blocks.Editable, { tagName: 'h5', inline: false, placeholder: i18n.__( 'Subtitle' ), value: attributes.subtitle, onChange: function( newSubtitle ) { props.setAttributes( { subtitle: newSubtitle } ); }, focus: focusedEditable === 'subtitle' ? focus : null, onFocus: function( focus ) { props.setFocus( _.extend( {}, focus, { editable: 'subtitle' } ) ); }, } ), el( blocks.Editable, { tagName: 'p', inline: true, placeholder: i18n.__( 'Write a brief bio...' ), value: attributes.bio, onChange: function( newBio ) { props.setAttributes( { bio: newBio } ); }, focus: focusedEditable === 'bio' ? focus : null, onFocus: function( focus ) { props.setFocus( _.extend( {}, focus, { editable: 'bio' } ) ); }, } ), el( 'div', { className: 'organic-profile-social' }, attributes.facebookURL && el( 'a', { className: 'social-link', href: attributes.facebookURL, target: '_blank', }, el( 'i', { className: 'fa fa-facebook', } ), ), attributes.twitterURL && el( 'a', { className: 'social-link', href: attributes.twitterURL, target: '_blank', }, el( 'i', { className: 'fa fa-twitter', } ), ), attributes.instagramURL && el( 'a', { className: 'social-link', href: attributes.instagramURL, target: '_blank', }, el( 'i', { className: 'fa fa-instagram', } ), ), attributes.linkedURL && el( 'a', { className: 'social-link', href: attributes.linkedURL, target: '_blank', }, el( 'i', { className: 'fa fa-linkedin', } ), ), attributes.emailAddress && el( 'a', { className: 'social-link', href: 'mailto:' + attributes.emailAddress, target: '_blank', }, el( 'i', { className: 'fa fa-envelope', } ), ), ), ), )
Ahora, tenemos que guardar el bloque. Esta función define la salida del bloque en el front-end:
save: function( props ) { var attributes = props.attributes; var alignment = props.attributes.alignment; return ( el( 'div', { className: props.className }, attributes.mediaURL && el( 'div', { className: 'organic-profile-image', style: { backgroundImage: 'url('+attributes.mediaURL+')' } }, el( 'img', { src: attributes.mediaURL } ), ), el( 'div', { className: 'organic-profile-content', style: { textAlign: attributes.alignment } }, el( 'h3', {}, attributes.title ), attributes.subtitle && el( 'h5', {}, attributes.subtitle ), attributes.bio && el( 'p', {}, attributes.bio ), el( 'div', { className: 'organic-profile-social' }, attributes.facebookURL && el( 'a', { className: 'social-link', href: attributes.facebookURL, target: '_blank', }, el( 'i', { className: 'fa fa-facebook', } ), ), attributes.twitterURL && el( 'a', { className: 'social-link', href: attributes.twitterURL, target: '_blank', }, el( 'i', { className: 'fa fa-twitter', } ), ), attributes.instagramURL && el( 'a', { className: 'social-link', href: attributes.instagramURL, target: '_blank', }, el( 'i', { className: 'fa fa-instagram', } ), ), attributes.linkedURL && el( 'a', { className: 'social-link', href: attributes.linkedURL, target: '_blank', }, el( 'i', { className: 'fa fa-linkedin', } ), ), attributes.emailAddress && el( 'a', { className: 'social-link', href: 'mailto:' + attributes.emailAddress, target: '_blank', }, el( 'i', { className: 'fa fa-envelope', } ), ) ) ) ) ); },
¡Ahí tienes! Esa es la mayor parte de la creación de un bloque personalizado para el próximo editor de Gutenberg.
¡Solo agregue estilos y tendrá un bloque personalizado que funciona completamente!.
Este árticulo fue creado por los chicos de organic theme, puedes pasar y mirar su web. Disponen de themes interesantes!!.