Cree una gran experiencia de edición de WordPress y ahorre tiempo con Gutenberg InnerBlocks
Publicado: 2023-02-12No te repitas
Para los desarrolladores, la pereza puede ser una virtud. Un componente reutilizable bien hecho ahorra tiempo y permite una reutilización eficiente y consistente. Esto se presta bien para crear experiencias de edición en WordPress, ya que la consistencia genera familiaridad y conocimiento para aquellos que usan un sitio web en el día a día.
WordPress hizo mucho ruido con el lanzamiento del editor de bloques de Gutenberg, ahora conocido simplemente como "El Editor". Para la edición, redacción y publicación de contenido, proporciona una experiencia excelente, mucho mejor que las antiguas WYSIWYG. Como desarrollador, quiero brindar la mejor experiencia de edición a mis clientes, quiero aprovechar la funcionalidad principal y no quiero reinventar la rueda. Afortunadamente, WordPress nos permite hacer todas esas cosas con bloques personalizados.
Introduzca InnerBlocks
InnerBlocks es una gran característica en el editor de WordPress. Los desarrolladores pueden usar bloques centrales como el párrafo, los encabezados y los botones para crear una experiencia consistente para un cliente. En lugar de reescribir secciones de texto y volver a declarar campos, el cliente obtiene una experiencia con la que se familiariza; editar y combinar los mismos bloques. Echemos un vistazo a un bloque que podríamos necesitar construir para un cliente y veamos cómo InnerBlocks puede ayudarnos a lograrlo.
Construyendo un bloque con InnerBlocks
Examinemos este bloque y consideremos cómo podríamos construirlo. Una herramienta común a la que recurrimos cuando creamos bloques son los campos personalizados avanzados (ACF). ACF Pro agrega compatibilidad con bloques personalizados, lo que permite a los desarrolladores escribir bloques en PHP para el editor y la interfaz. También nos permite usar campos personalizados, un paradigma con el que muchos desarrolladores de WordPress están familiarizados. ACF es compatible con InnerBlocks, lo que significa que podemos crear un bloque personalizado, pero ni siquiera necesitaremos escribir código personalizado para ese encabezado, párrafo o botones.
Con ACF Blocks, podemos aprovechar los bloques centrales para crear una excelente experiencia de edición para todo el texto que vemos en este bloque. Otra opción potencial sería usar patrones de bloques, pero como desarrolladores y curadores de una fantástica experiencia de WordPress, crear un bloque personalizado logra el objetivo deseado.
Código de registro de bloque
Aquí está el código para registrar nuestro bloque ACF personalizado. Lea más sobre el uso de la función acf_register_block_type aquí.
<?php /** * Template for registering an ACF powered custom block. * * More info: https://www.advancedcustomfields.com/resources/blocks/ */ /** * Register the block. */ add_action( 'acf/init', function() { /** * ACF block registration options here: https://www.advancedcustomfields.com/resources/acf_register_block_type/ */ acf_register_block_type( array( 'name' => 'call-to-action-demo', // JS will register as: acf/{block-name}. 'title' => __( 'Call to Action ACF Demo', 'locale' ), 'description' => __( 'A custom ACF Call to Action block.', 'locale' ), 'render_template' => 'partials/blocks/call-to-action-demo.php', // Change to block template. 'category' => 'design', // Category in the block inserter. 'keywords' => array( 'action', 'buttons', 'cta' ), // Searchable keywords. 'supports' => array( 'align' => false, // Disable support for align. 'anchor' => true, // Enable support for anchor. 'jsx' => true, // Enable support for JSX. 'mode' => false, // Disable ACF block edit/preview mode switching as we are only using InnerBlocks for editable content. ), ) ); } );
Desglosemos el código. Usamos la función acf_register_block_type para registrar nuestro bloque y pasamos una serie de argumentos que definen nuestro bloque y activan y desactivan la funcionalidad.
- Tenga en cuenta que estamos siguiendo el procedimiento recomendado para agregar un nombre único para nuestro bloque, así como un título y una descripción legibles por humanos. Apuntamos a una plantilla de representación, donde configuraremos nuestra plantilla de PHP para el bloque más tarde. También hemos agregado una categoría donde queremos que nuestro bloque se agrupe en el Insertador de bloques.
- Tenemos palabras clave que se pueden buscar para que el bloque sea más fácil de buscar en el insertador.
- Agregamos una matriz de 'soportes' para habilitar funcionalidades como establecer un ancla HTML y hemos desactivado la alineación ya que este bloque siempre se ubicará en el centro de la página.
- ¡Esa matriz de soporte es donde activamos la magia de InnerBlocks! Establecer 'jsx' en verdadero le dice a ACF que admitiremos la representación de plantillas React jsx dentro de nuestro bloque.
Repasemos el contenido que necesitamos en nuestro bloque:
- Título
- Texto de párrafo
- ¡Botones para pasar a la acción!
Ahora, normalmente asignaríamos campos a nuestro bloque, como se describe aquí. Pero en este caso, no es necesario, podemos aprovechar los bloques centrales para lograr todo esto. Si el bloque necesita una imagen de fondo, por ejemplo, agregar un campo de imagen ACF al bloque podría ser una excelente manera de lograrlo. Por ahora, sin embargo, sigamos nuestro ejemplo de InnerBlocks y saltemos a la plantilla.
Plantilla de bloque
Aquí está la plantilla para el nuevo bloque. Asegúrese de apuntar el registro del bloque a la ubicación de la plantilla del bloque. Una práctica común es colocarlos en una carpeta de parciales en el tema.
<?php /** * ACF Call to Action example block template. * * More info: https://www.advancedcustomfields.com/resources/acf_register_block_type/ * * @param array $block The block settings and attributes. * @param string $content The block inner HTML (empty). * @param bool $is_preview True during AJAX preview. * @param (int|string) $post_id The post ID this block is saved to. * */ // Create id attribute allowing for custom "anchor" value. $block_ . $block['id']; if ( ! empty( $block['anchor'] ) ) { $block_id = $block['anchor']; } // Create class attribute allowing for custom "className" and "align" values. $class_name = 'acf-call-to-action-demo'; if ( ! empty( $block['className'] ) ) { $class_name .= ' ' . $block['className']; } if ( ! empty( $block['align'] ) ) { $class_name .= ' align' . $block['align']; } ?> <div class="<?php echo esc_html( $class_name ); ?>"> <div class="cta__inner"> <?php // Set up innerBlocks and provide a template. // Restrict InnerBlocks to allowed block list. $allowed_blocks = array( 'core/heading', 'core/paragraph', 'core/buttons' ); // Start InnerBlocks with a template. $template = array( array( 'core/heading', array( 'placeholder' => __( 'CTA Heading', 'locale' ), 'align' => 'center', 'level' => '2', ), ), array( 'core/paragraph', array( 'placeholder' => __( 'Add CTA text here', 'locale' ), 'align' => 'center', ), ), array( 'core/buttons', array( 'placeholder' => __( 'Add CTA buttons here', 'locale' ), 'align' => 'center', ), array( array( 'core/button', array( 'text' => __( 'Take action', 'locale' ), ), ), array( 'core/button', array( 'text' => __( 'Learn more', 'locale' ), ), ), ), ), ); // Echo out our JSX InnerBlocks compoennt for the editor. echo '<InnerBlocks allowedBlocks="' . esc_attr( wp_json_encode( $allowed_blocks ) ) . '" template="' . esc_attr( wp_json_encode( $template ) ) . '" templateLock="false" />'; ?> </div> </div>
Desglosemos este código.
- Al principio, tenemos una plantilla de bloque genérica repetitiva que ACF proporciona en su guía de bloque, como la compatibilidad con un ancla y un nombre de clase personalizado.
- El componente InnerBlocks puede recibir propiedades. Estamos usando tres en esta plantilla.
- Bloques permitidos: agregamos una serie de bloques permitidos para curar. Solo estos bloques estarán disponibles para su selección dentro del espacio InnerBlocks de nuestro bloque personalizado.
- Plantilla: podemos pasar una matriz de bloques y podemos pasar atributos de bloque para que los bloques comiencen cuando se cargan por primera vez en el editor.
- Tenga en cuenta que podemos pasar atributos de bloque para configurar a nuestros usuarios para el éxito. El título ya tiene establecido el nivel 2 y el párrafo está centrado.
- Nota: En JavaScript siempre se hará referencia a los bloques principales como {plugin}/blockname. En nuestro caso, usamos bloques principales, pero si quisiera usar un bloque ACF personalizado, escribiría 'acf' delante del nombre del bloque. Recuerda, cuando usamos InnerBlocks, estamos pasando este componente al Editor de WordPress, que usa React. Es por eso que estamos pensando en JS aquí.
- Otra nota: ¡observa que el bloque core/buttons usa InnerBlocks en sí mismo! ¡En realidad estamos pasando una matriz de dos bloques de botones/núcleo dentro!
- Bloqueo de plantilla: templateLock se establece en el componente InnerBlocks. Si lo configuramos en 'todos', ninguno de los bloques en nuestra plantilla proporcionada a InnerBlocks podría moverse o eliminarse. Si lo configuramos en 'insertar', los bloques dentro solo se pueden mover, no se pueden eliminar bloques y no se pueden agregar nuevos bloques. Si lo establecemos en 'falso', la instancia de InnerBlocks se desbloqueará y se desbloqueará independientemente de los bloqueos de la plantilla principal (más sobre esto más adelante). Lea más sobre las plantillas de bloques en el manual del editor de bloques de WordPress. Hablaremos más sobre el bloqueo de bloques.
¡Tenemos un nuevo bloque! Después de todo eso, y algo de estilo, por supuesto, el bloque de ejemplo se ve así en el editor:
Anidamiento más profundo: Curación de la experiencia aún más con InnerBlocks anidados
Tenemos un gran bloque, y no tuvimos que configurar WYSIWYG o incluso manejar campos personalizados en nuestra plantilla. Pero, ¿y si quisiéramos curar y afinar aún más esta experiencia para el cliente? Digamos que recibimos una solicitud de que nos aseguremos de que el encabezado esté siempre presente, por lo que siempre hay un bloque de encabezado, pero el contenido posterior es flexible e incluso podría incluir una lista u otro tipo de bloque. Nuestro objetivo es lograr esa flexibilidad, al tiempo que preservamos la consistencia.
Consideremos algunas reglas al trabajar con InnerBlocks:
- Un solo bloque solo puede tener una instancia de InnerBlocks dentro.
- Múltiples bloques dentro de InnerBlocks pueden usar sus propios componentes de InnerBlocks.
- Los InnerBlocks se pueden bloquear a través de la propiedad templateLock, pero las instancias de InnerBlocks más profundas dentro de una estructura de bloques anidados se pueden desbloquear nuevamente configurando templateLock en falso.
Una solución es crear un nuevo bloque que sirva como contenedor para InnerBlocks. Incluiríamos ese bloque en nuestra plantilla de bloque principal y lo bloquearíamos, pero desbloquearíamos explícitamente nuestro bloque contenedor al establecer 'templateLock' en falso. También es posible que queramos asegurarnos de que este bloque contenedor especial solo esté disponible dentro del bloque principal que elegimos, configurando un bloque principal para nuestro bloque. Podríamos permitir múltiples tipos de bloques dentro de ese espacio para ofrecer listas de editores y más, y al mismo tiempo solo permitir el encabezado, los botones y nuestro bloque contenedor en el bloque principal de Llamado a la acción.
Bloqueo de bloque individual
Una nueva característica de WordPress 5.9 es la capacidad de bloquear bloques individuales en una plantilla. Esta es otra posible solución a nuestro problema flexible pero consistente.
Así es como se ve nuestra plantilla con algunos bloques individuales bloqueados:
<?php /** * ACF Call to Action example block template. * * More info: https://www.advancedcustomfields.com/resources/acf_register_block_type/ * * @param array $block The block settings and attributes. * @param string $content The block inner HTML (empty). * @param bool $is_preview True during AJAX preview. * @param (int|string) $post_id The post ID this block is saved to. * */ // Create id attribute allowing for custom "anchor" value. $block_ . $block['id']; if ( ! empty( $block['anchor'] ) ) { $block_id = $block['anchor']; } // Create class attribute allowing for custom "className" and "align" values. $class_name = 'acf-call-to-action-demo'; if ( ! empty( $block['className'] ) ) { $class_name .= ' ' . $block['className']; } if ( ! empty( $block['align'] ) ) { $class_name .= ' align' . $block['align']; } ?> <div class="<?php echo esc_html( $class_name ); ?>"> <div class="cta__inner"> <?php // Set up innerBlocks and provide a template. // Restrict InnerBlocks to allowed block list. $allowed_blocks = array( 'core/heading', 'core/paragraph', 'core/buttons' ); // Start InnerBlocks with a template. $template = array( array( 'core/heading', array( 'placeholder' => __( 'Heading', 'locale' ), 'align' => 'center', 'level' => '2', 'lock' => array( 'move' => true, // Block may nto be moved. 'remove' => true, // Block may not be removed. ), ), ), array( 'core/paragraph', array( 'placeholder' => __( 'Add CTA text here', 'locale' ), 'align' => 'center', ), ), array( 'core/buttons', array( 'placeholder' => __( 'Add CTA buttons here', 'locale' ), 'align' => 'center', 'lock' => array( 'move' => true, // Block may not be moved. 'remove' => true, // Block may not be removed. ), ), array( array( 'core/button', array( 'text' => __( 'Take action', 'locale' ), ), ), array( 'core/button', array( 'text' => __( 'Learn more', 'locale' ), ), ), ), ), ); // Echo out our JSX InnerBlocks compoennt for the editor. echo '<InnerBlocks allowedBlocks="' . esc_attr( wp_json_encode( $allowed_blocks ) ) . '" template="' . esc_attr( wp_json_encode( $template ) ) . '" />'; ?> </div> </div>
Los bloques de encabezado y botones ya no deberían poder moverse ni eliminarse. Asegúrese de actualizar el editor, eliminar y luego agregar el bloque nuevamente para obtener los cambios de plantilla.
Bonificación: hacer el mismo bloque de forma nativa
Una vez que haya implementado un proceso de compilación, crear bloques nativos de WordPress con React es sorprendentemente fácil y no demasiado diferente de crear una plantilla de un bloque en php. La configuración de un entorno de desarrollo de bloques está fuera del alcance de este artículo, pero hay una serie de recursos para comenzar, como el Manual del editor de bloques de WordPress. Una vez que pueda incluir bloques personalizados, puede construir bloques rápidamente usando InnerBlocks.
Aquí hay un ejemplo de nuestro bloque Call to Action index.js en React. Verá la misma estrategia que discutimos anteriormente con ACF aplicada aquí.
import { __ } from '@wordpress/i18n'; import { registerBlockType } from '@wordpress/blocks'; import { InnerBlocks } from '@wordpress/block-editor'; import { useBlockProps } from '@wordpress/block-editor'; /** * Block Name. * Create an example Call to Action Block * Uses InnerBlocks for editable content within. */ export const blockName = 'call-to-action'; /** * Block Config. * Set basic params for controlling the editor. */ export const BLOCK_CONFIG = { // Set up the block template. CTA_TEMPLATE: [ [ 'core/heading', { placeholder: __('CTA Headline', 'locale'), align: 'center', level: 2, lock: { move: true, remove: true, }, }, ], [ 'core/paragraph', { placeholder: 'Optional CTA text', align: 'center', lock: { move: true, }, }, ], [ 'core/buttons', { lock: { move: true, remove: true, }, className: 'is-content-justification-center', align: 'center', // __experimentalLayout - https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/buttons/block.json layout: { type: 'flex', justifyContent: 'center', }, }, [ [ 'core/button', { text: 'Apply now', lock: { move: true, remove: true, }, }, ], ['core/button', { text: 'Learn more' }], ], ], ], // Set up the allowed blocks. ALLOWED_BLOCKS: ['core/paragraph', 'core/heading', 'core/buttons'], }; // Register the block via WP func. Change 'myplugin' to your plugin or theme. registerBlockType(`myplugin/${blockName}`, { title: __('Call to Action', 'locale'), // Change 'locale' to your locale for internationalization. description: __( 'Call to action block with headline and buttons', 'locale' ), keywords: [__('call'), __('action'), __('cta')], category: 'design', supports: { anchor: true, defaultStylePicker: false, html: false, align: false, }, attributes: { anchor: { type: 'string', default: '', }, }, transforms: {}, variations: [], edit: (props) => { const blockProps = useBlockProps({ className: `wp-block-myplugin-${blockName}`, }); return ( <div {...blockProps}> <div className="cta__inner"> <div className="cta__inner-blocks-wrapper"> <InnerBlocks template={BLOCK_CONFIG.CTA_TEMPLATE} allowedBlocks={BLOCK_CONFIG.ALLOWED_BLOCKS} renderAppender={false} /> </div> </div> </div> ); }, save: () => { return ( <div> <div className="cta__inner"> <InnerBlocks.Content /> </div> </div> ); }, });
¡Ve y construye!
Recursos adicionales
- Artículo de Bill Erickson sobre InnerBlocks
- Artículos de Bill Erickson sobre el editor de bloques
- Guía de bloques ACF
- Documentación de tipo de bloque de registro ACF
- Manual del editor de bloques de WordPress
- Manual del editor de bloques de WordPress: Tutorial para crear un bloque