Creación de un bloque Gutenberg

Creación de un bloque Gutenberg

Editor de Bloques WordPress. Creación de bloques.

Vamos a crear un nuevo bloque de contenido dinámico, implementándolo en JavaScript. En este ejemplo, el bloque será la imagen destacada de la entrada y un enlace al artículo de dicha entrada, pudiendo elegir el usuario la entrada a mostrar mediante una búsqueda o las 30 últimas entradas.

Para ello utilizaremos la función registerBlockType. Esta función es responsable de especificar el modelo de un bloque, describiendo los comportamientos necesarios para que el editor comprenda cómo aparece, cambia cuando se edita y, en última instancia, se guarda en el contenido de la publicación.

Los bloques deberán registrarse en el servidor para asegurarse de que el script se coloca en cola cuando se carga el editor. Registraremos los scripts y estilos usando wp_register_script y wp_register_style, luego asignaremos estos como identificadores asociados al bloque usando la configuración de registro de tipo de bloque script, style, editor_script y editor_style. Los identificadores con prefijo editor_ sólo se colocarán en cola en el contexto del editor, mientras que la secuencia de comandos y el estilo se colocarán tanto en el editor como en el proceso de visualización.

<?php
/*
 *************************************************************
 * Plugin Name: Doowebs Gutenberg blocks
 * Description: Gutenberg blocks.
 * Author: Doowebs
 * Version: 0.0
 *************************************************************
 */
function dw-guten_block_register_block() {
  wp_register_script(
        'dwguten-block',
        plugins_url('block.js',__FILE__),
        array('wp-blocks','wp-editor','wp-element','wp-components','wp-data','wp-i18n'),
        filemtime(plugin_dir_path(__FILE__).'block.js')
  );
}
add_action('init','dwguten_block_register_block');

Hay que tener en cuenta las dependencias del script, por ejemplo:

wp-blocks, incluye registro de tipo de bloque y funciones relacionadas.
wp-element, incluye la abstracción de elementos de WordPress para describir la estructura de los bloques.
wp-editor, incluye el componente RichText,

Registrar el bloque

Con el script en cola, veamos la implementación del bloque en sí (archivo block.js):

( function( blocks, editor, element, components, data, i18n ) {
    var el = element.createElement;
    var InspectorControls = editor.InspectorControls;
    var PanelBody = components.PanelBody;
    var PanelRow = components.PanelRow;
    var SelectControl = components.SelectControl;
    var TextControl = components.TextControl;
    var withSelect = data.withSelect;
    var Button = components.Button;

    blocks.registerBlockType( 'dwguten-block/post', {
        title: i18n.__('Article Link'),
        icon: 'pressthis',
        category: 'doowebs-category',

        attributes: { 
            post_id: {
                type: 'number',
                default: 0,
            },
            post_title: {
                type: 'string',
                default: '',
            },
            search_text: {
                type: 'string',
                default: '',
            },
            search_text_sel: {
                type: 'string',
                default: '',
            },
        },

        edit: withSelect(function(select,props) {
            var postToShow=30;
            var searchTextSel=props.attributes.search_text_sel;
            var query = {
                per_page: postToShow,
                search: searchTextSel,
            };
            return {
                posts:select('core').getEntityRecords('postType','post',query)
            };
        })(function(props) {
            var postId=props.attributes.post_id;
            var postTitle=props.attributes.post_title;
            if (!props.posts) {
                return i18n.__('Cargando ...');
            }
            var className=props.className;
 

            let options = [{value:0,label:i18n.__('Selecciona una entrada')}];
            var i=0;
            while (i<props.posts.length) {
                options.push({value:props.posts[i].id,label:props.posts[i].title.raw});
                i++;
            }
            i=0;
            while (i<props.posts.length&&props.posts[i].id!=props.attributes.post_id) {i++;}
            if (i<props.posts.length) {
                props.setAttributes({post_title:props.posts[i].title.raw});
            }
            return [ 
                el(InspectorControls,{},
                    el(PanelBody,
                        {
                        title: i18n.__("Selección de entrada"),
                        initialOpen: true,
                        },
                        el(PanelRow,{},
                            el(TextControl,{
                                type: 'text',
                                label: i18n.__('Buscar'),
                                onChange: function (content) {
                                    props.setAttributes({search_text:content});
                                },
                                value: props.attributes.search_text,
                            }),
                            el(Button,{
                                className: 'components-button editor-post-preview is-button is-default is-large',
                                type: 'button',
                                onClick: function () {
                                    props.setAttributes({search_text_sel:props.attributes.search_text});
                               },
                            },i18n.__('Buscar')),
                        ),
                        el(PanelRow,{},
                            el(SelectControl,{
                                label: i18n.__('Entrada'),
                                options: options,
                                onChange: function (value) {
                                    props.setAttributes({post_id:parseInt(value)});
                                },
                                value: props.attributes.post_id,
                            }),
                        ),
                    ),
                ),
                el('p',{className:className+' head'},i18n.__('Article link')),
                el('p',{className:className+' title'},postTitle),
            ];
        }),

        save: function(props) {
            return null;
        },
    } );
}(
    window.wp.blocks,
    window.wp.editor,
    window.wp.element,
    window.wp.components,
    window.wp.data,
    window.wp.i18n,
) );

Una vez que se registra un bloque, este está disponible como una opción en el cuadro de diálogo de inserción del editor, utilizando valores del título, icono y categoría para organizar su visualización. Puede elegir un icono de cualquiera de los incluidos en el conjunto de iconos Dashicons incorporado, o proporcionar un elemento svg personalizado.

El nombre de un bloque debe tener como prefijo un espacio de nombres específico para su complemento. Esto ayuda a evitar conflictos cuando más de un complemento registra un bloque con el mismo nombre. En este ejemplo, el espacio de nombres es dwguten.

Las funciones edit y save describen la estructura de su bloque en el contexto del editor y el contenido guardado para el front-end, respectivamente. Pudiendo definir la personalización del bloque en la parte del editor y en la parte del front-end.

Coloca en cola los estilos del bloque para el editor y el front-end

Al igual que los scripts, deben de ponerse en cola los estilos del bloque. Se utilizará el controlador editor_style para los estilos que sólo son relevantes en el editor, y el controlador de estilo para los estilos comunes aplicados tanto en el editor como en el front-end.
El código para los estilos el editor los pasamos al archivo editor.css:

.wp-block-dwguten-block-post.title {
  color:navy;
  background: paleturquoise;
  border: 1px solid #c99;
  padding: 3px;
  margin: 0;
  width: 100%;
  text-align: center;
  display: flex;
  justify-content: center;
  flex-direction: column;
  min-height: 100px;
}

y los estilos para el front-end en el archivo style.css, que en nuestro caso lo vamos a dejar vacío.

En el fichero del plugin (guten-plugin.php) registramos estos ficheros:

<?php
/*
 *************************************************************
 * Plugin Name: Doowebs dwguten blocks
 * Description: Gutenberg blocks.
 * Author: Doowebs
 * Version: 0.0
 *************************************************************
 */
function dwguten_block_register_block() {
  wp_register_style(
      'dwguten-block-editor',
      plugins_url( 'editor.css', __FILE__ ),
      array( 'wp-edit-blocks' ),
      filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
  );
  wp_register_style(
      'dwguten-block-style',
      plugins_url( 'style.css', __FILE__ ),
      array( ),
      filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
  );
}
add_action( 'init', 'dwguten_block_register_block' );

Atributos

Las funciones edit y save son responsables de describir la estructura de la apariencia del bloque en el editor y en front-end, respectivamente. Si el usuario cambia un bloque, esta estructura puede necesitar cambiarla. Para lograr esto, el estado de un bloque se mantiene durante toda la sesión de edición como un objeto JavaScript simple, y cuando ocurre una actualización, la función de edición se invoca nuevamente. Dicho de otra manera: la salida de un bloque es una función de sus atributos.

Un desafío de mantener la representación de un bloque como un objeto JavaScript es que debemos poder extraer este objeto nuevamente del contenido guardado de una publicación. Esto se logra con la propiedad de atributos del tipo de bloque:

       attributes: { 
            post_id: {
                type: 'number',
                default: 0,
            },
            post_title: {
                type: 'string',
                default: '',
            },
            search_text: {
                type: 'string',
                default: '',
            },
            search_text_sel: {
                type: 'string',
                default: '',
            },
        },

Al registrar un nuevo tipo de bloque, la propiedad de atributos describe la forma del objeto de atributos que le gustaría recibir en las funciones de edit y save. Cada valor es una función fuente para encontrar el valor deseado del bloque.

En el fragmento de código anterior, al cargar el editor, el valor del contenido se extraerá del HTML del elemento de párrafo en la función edit.

Controles del bloque: barra lateral

En el editor podemos establecer los componentes en el bloque o en la barra lateral (inspector).

La barra lateral de configuración se utiliza para mostrar configuraciones menos utilizadas o configuraciones que requieren más espacio en la pantalla. La barra lateral de configuración debe usarse solo para la configuración de nivel de bloque.

Si tiene configuraciones que afectan solo el contenido seleccionado dentro de un bloque. La barra lateral de configuración se muestra incluso cuando se edita un bloque en modo HTML, por lo que solo debe contener configuraciones de nivel de bloque.

La pestaña Bloque se muestra en lugar de la pestaña Documento cuando se selecciona un bloque.

De manera similar a la representación de una barra de herramientas, si incluye un elemento InspectorControls en el valor de retorno de la función de edición de su tipo de bloque, esos controles se mostrarán en la región de la barra lateral de configuración.

Bloques dinámicos

Los bloques dinámicos son bloques que construyen su estructura y contenido sobre la marcha cuando el bloque se representa en el front-end.
Los bloques dinámicos se pueden utilizar para la representación de entradas que pueden ser actualizadas o bloques en los que es necesario realizar actualización del código (HTML, CSS, JS). Este es el caso de agregación de clases o elementos HTML que cambien el diseño del front-end: esto se consigue con el usos de bloques dinámicos asegurando que esos se apliquen.

Para muchos bloques dinámicos, la función save debe devolverse como nula, lo que le indica al editor que guarde los atributos del bloque en la base de datos. Estos atributos luego se pasan a la representación del lado del servidor, para que pueda decidir cómo mostrar el bloque en el front-end del sitio.

Los atributos de bloque se pueden usar para cualquier contenido o configuración que desee guardar para ese bloque. Por ejemplo, los atributos se pueden usar para cada contenido que desee mostrar en el front-end, como el texto del encabezado, el texto del párrafo, una imagen, una URL, etc.

En el ejemplo de código anterior se mostraba un tipo de bloque dinámico para mostrar una entrada (block.js).

Debido a que es un bloque dinámico, es necesario un componente de servidor. El contenido en el front-end de su sitio depende de la función llamada por la propiedad render_callback de register_block_type.

<?php
/*
 *************************************************************
 * Plugin Name: Doowebs dwguten blocks
 * Description: Gutenberg blocks
 * Author: Doowebs
 * Version: 0.0
 *************************************************************
 */
 
function dwguten_block_register_block() {

  register_block_type( 'dwguten-block/post', array(
    'style' => 'dwguten-block-style',
    'editor_style' => 'dwguten-block-editor',
    'editor_script' => 'dwguten-block',
    'attributes' => array(
      'post_id'     => array('type'=>'number',),
      'post_title'  => array('type'=>'string',),
      'search_text' => array('type'=>'string',),
      'search_text_sel' => array('type'=>'string',),
    ),
    'render_callback' => 'dwguten_block_dynamic_render_callback_post',
  ));
}
add_action( 'init', 'dwguten_block_register_block' );

function dwguten_block_dynamic_render_callback_post($attributes) {
    $post_id=$attributes['post_id'];
    $post=get_post($post_id);
    $category=get_the_category($post_id);
    $video=false;
    if (!empty($category)) {
      $i=0;
      while ($i<sizeof($category)) {
        if ($category[$i]->cat_name=='Vídeos') {$video=true;}
        $i++;
      }
    }
    $videost='';
    if ($video) {$videost='<img src="\wp-content\themes\tema\images\ic_playCircle.svg" class="video-logo" alt="Video-logo" />';}
    $bc=
        '<section class="article-link">'.
            '<a class="article-title-link" href="'.esc_url(get_permalink($post_id)).'">'.
                '<h2 class="article-title">'.get_the_title($post_id).'</h2>'.
            '</a>'.
            '<a class="thumbnail-link" href="'.esc_url(get_permalink($post_id)).'">'.
                '<span class="photo-overlay"></span>'. 
                get_the_post_thumbnail($post_id).
                $videost.
            '</a>'.
            '<div class="article-excerpt">'.the_excerpt_m($post_id).'</div>'.
            '<a class="article-link-btn" href="'.esc_url(get_permalink($post_id)).'">'.
                esc_html__('Ir al artículo').
            '</a>'.
        '</section>';
    return $bc;
}
 
?>

Con la definición de los archivos guten-plugin.php, block.js, editor.css y style.css en una carpeta dentro de plugins de nuestro proyecto WordPress habremos definido un plugin, que una vez activado, dispondremos de un nuevo bloque en nuestro editor gutenberg, disponible para nuestras entradas. En este caso, se genera un bloque con la imagen destacada de la entrada, que en el caso de ser de la categoría Vídeos se mostrará con el icono play (tener en cuenta añadir el archivo de imagen ic_playCircle.svg en la ruta indicada en el código), y un enlace a la entrada (Ir al artículo).

Por último, habría que añadir el código css para nuestro front-end en el archivo style.css de nuestro tema WordPress, por ejemplo:

/* CSS Articulo link */
.article-link {
  width: 100%;
  margin: 24px 0 14px;
  background-color: #F9FBFC;
  padding: 10px 15px 50px;
}
.article-link .article-title {
  color: #25343D;
  font-family: Archia;
  font-size: 24px;
  font-weight: bold;
  line-height: 36px;
}
.article-link .article-title:hover {
  color: #71EB6C;
}
.article-link .thumbnail-link {
  display: block;
  width: 100%;
  position: relative;
}
.article-link .photo-overlay {
  background: rgba(0,0,0,0);
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;
  margin: 0;
  z-index: 5;
}
.article-link .photo-overlay:hover {
  background: rgba(0,0,0,0.4);
}
.article-excerpt {
  width: 100%;
  margin: 0;
  background-color: #F9FBFC;
  padding: 15px 0;
  color: #25343D; 
  font-size: 16px;  
  line-height: 24px;
}
.article-link-btn {
  color: #336BEF;
  font-size: 16px;
  font-weight: bold;
  line-height: 24px;
}