Crie uma ótima experiência de edição no WordPress e economize tempo com Gutenberg InnerBlocks
Publicados: 2023-02-12Não se repita
Para os desenvolvedores, a preguiça pode ser uma virtude. Um componente reutilizável e bem feito economiza tempo e permite reutilização e consistência eficientes. Isso se presta bem à criação de experiências de edição no WordPress, pois a consistência gera familiaridade e conhecimento para aqueles que usam um site no dia a dia.
O WordPress fez muito barulho com o lançamento do editor de blocos Gutenberg, agora conhecido simplesmente como “O Editor”. Para edição, rascunho e publicação de conteúdo, ele oferece uma experiência excelente, muito melhor do que os WYSIWYGs mais antigos. Como desenvolvedor, quero fornecer a melhor experiência de edição para meus clientes, quero aproveitar a funcionalidade principal e não quero reinventar a roda. Felizmente, o WordPress nos permite fazer todas essas coisas com blocos personalizados.
Inserir Blocos Internos
InnerBlocks é um ótimo recurso no editor do WordPress. Os desenvolvedores podem usar blocos principais como parágrafo, títulos e botões para criar uma experiência consistente para um cliente. Em vez de reescrever seções de texto e redeclarar campos, o cliente obtém uma experiência com a qual se familiarizou; editar e combinar os mesmos blocos. Vamos dar uma olhada em um bloco que podemos precisar construir para um cliente e ver como InnerBlocks pode nos ajudar a alcançá-lo.
Construindo um bloco com InnerBlocks
Vamos examinar este bloco e considerar como podemos construí-lo. Uma ferramenta comum que usamos ao criar blocos é o Advanced Custom Fields (ACF). O ACF Pro adiciona suporte a blocos personalizados, que permite aos desenvolvedores escrever blocos em PHP para o editor e o front-end. Ele também nos permite usar campos personalizados - um paradigma com o qual muitos desenvolvedores do WordPress estão familiarizados. O ACF oferece suporte a InnerBlocks, o que significa que podemos criar um bloco personalizado, mas nem precisamos escrever um código personalizado para esse título, parágrafo ou botões.
Com o ACF Blocks, podemos aproveitar os blocos principais para criar uma ótima experiência de edição para todo o texto que vemos neste bloco. Outra opção potencial seria usar padrões de blocos, mas como desenvolvedores e curadores de uma fantástica experiência WordPress, criar um bloco personalizado atinge o objetivo desejado.
Bloquear código de registro
Aqui está o código para registrar nosso bloco ACF personalizado. Leia mais sobre como usar a função acf_register_block_type aqui.
<?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. ), ) ); } );
Vamos decompor o código. Estamos usando a função acf_register_block_type para registrar nosso bloco e estamos passando uma matriz de argumentos que definem nosso bloco e ativam e desativam a funcionalidade.
- Observe que estamos seguindo o procedimento recomendado para adicionar um nome exclusivo para nosso bloco, bem como um título legível e uma descrição. Apontamos para um modelo de renderização, onde configuraremos nosso modelo PHP para o bloco posteriormente. Também adicionamos uma categoria na qual queremos que nosso bloco seja agrupado no Insertor de bloco.
- Temos palavras-chave pesquisáveis para facilitar a pesquisa do bloco no insersor.
- Adicionamos uma matriz de 'suportes' para habilitar funcionalidades como definir uma âncora HTML e desativamos o alinhamento, pois esse bloco sempre ficará no centro da página.
- Essa matriz de suporte é onde ativamos a mágica InnerBlocks! Definir 'jsx' como true informa ao ACF que daremos suporte à renderização do modelo React jsx dentro do nosso bloco.
Vamos revisar o conteúdo que precisamos em nosso bloco:
- Cabeçalho
- Texto do parágrafo
- Botões para agir!
Agora, normalmente atribuiríamos campos ao nosso bloco, conforme descrito aqui. Mas, neste caso, não precisamos - podemos aproveitar os blocos principais para conseguir tudo isso. Se o bloco precisar de uma imagem de fundo, por exemplo, adicionar um campo de imagem ACF ao bloco pode ser uma ótima maneira de conseguir isso. Por enquanto, porém, vamos nos ater ao nosso exemplo InnerBlocks e pular para o modelo.
Modelo de bloco
Aqui está o modelo para o novo bloco. Certifique-se de apontar o registro do bloco para o local do modelo de bloco. Uma prática comum é colocá-los em uma pasta de parciais no 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>
Vamos decompor esse código.
- No início, temos um clichê de modelo de bloco genérico que o ACF fornece em sua orientação de bloco, como suporte para uma âncora e nome de classe personalizado.
- O componente InnerBlocks pode receber propriedades. Estamos usando três neste modelo.
- Blocos permitidos: adicionamos uma matriz de blocos permitidos para curadoria. Somente esses blocos estarão disponíveis para seleção dentro do espaço InnerBlocks do nosso bloco personalizado.
- Modelo: podemos passar uma matriz de blocos e podemos passar atributos de bloco para os blocos começarem quando forem carregados no editor.
- Observe que podemos passar atributos de bloco para configurar nossos usuários para o sucesso. O título já tem o nível 2 definido e o parágrafo está centralizado.
- Observação: os blocos principais sempre serão referidos em JavaScript como {plugin}/blockname. Em nosso caso, estamos usando blocos principais, mas se você quiser usar um bloco ACF personalizado, escreva 'acf' na frente do nome do bloco. Lembre-se, ao usar InnerBlocks, estamos passando este componente para o Editor WordPress, que usa React. É por isso que estamos pensando em JS aqui.
- Outra observação: observe que o bloco principal/botões usa o próprio InnerBlocks! Na verdade, estamos passando uma matriz de dois blocos de núcleo/botão dentro!
- Bloqueio de modelo: templateLock é definido no componente InnerBlocks. Se definirmos como 'all', nenhum dos blocos em nosso modelo fornecido para InnerBlocks poderá ser movido ou removido. Se definirmos como 'inserir', os blocos internos só poderão ser movidos, nenhum bloco poderá ser removido e nenhum novo bloco poderá ser adicionado. Se definirmos como 'falso', a instância InnerBlocks será desbloqueada e será desbloqueada independentemente de quaisquer bloqueios de modelo pai (mais sobre isso mais abaixo). Leia mais sobre Modelos de Blocos no Manual do Editor de Blocos do WordPress. Falaremos mais sobre bloqueio de blocos.
Temos um novo bloco! Depois de tudo isso, e alguns estilos, é claro, o bloco de exemplo fica assim no editor:
Aninhamento mais profundo: curadoria da experiência ainda mais com blocos internos aninhados
Temos um ótimo bloco e não precisamos configurar um WYSIWYG ou mesmo manipular campos personalizados em nosso modelo. Mas e se quiséssemos selecionar e refinar ainda mais essa experiência para o cliente? Digamos que recebemos uma solicitação para garantir que o cabeçalho esteja sempre presente, de modo que sempre haja um bloco de cabeçalho, mas o conteúdo posterior é flexível e pode até incluir uma lista ou outro tipo de bloco. Nosso objetivo é alcançar essa flexibilidade, ao mesmo tempo em que preservamos a consistência.
Vamos considerar algumas regras ao trabalhar com InnerBlocks:
- Um único bloco pode ter apenas uma instância de InnerBlocks dentro.
- Vários blocos dentro de InnerBlocks podem usar seus próprios componentes InnerBlocks.
- InnerBlocks podem ser bloqueados por meio da propriedade templateLock, mas instâncias de InnerBlocks mais profundas dentro de uma estrutura de bloco aninhada podem ser desbloqueadas novamente definindo templateLock como false.
Uma solução é criar um novo bloco que sirva como wrapper para InnerBlocks. Incluiríamos esse bloco em nosso modelo de bloco pai e o bloquearíamos, mas desbloquearíamos explicitamente nosso bloco wrapper definindo 'templateLock' como falso. Também podemos querer garantir que esse bloco wrapper especial esteja disponível apenas no bloco pai que escolhermos, definindo um pai para nosso bloco. Poderíamos permitir vários tipos de bloco dentro desse espaço para oferecer listas de editores e muito mais, enquanto ainda permitíamos apenas o cabeçalho, botões e nosso bloco wrapper no bloco pai de chamada para ação.
Bloqueio de bloco individual
Um novo recurso no WordPress 5.9 é a capacidade de bloquear blocos individuais em um modelo. Essa é outra solução possível para nosso problema flexível, mas consistente.
Aqui está a aparência do nosso modelo com alguns blocos individuais 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>
O cabeçalho e os blocos de botões não devem mais ser movidos ou removidos. Certifique-se de atualizar o editor, remover e adicionar o bloco novamente para obter as alterações de modelo.
Bônus: Fazendo o mesmo bloco nativamente
Uma vez que você tenha um processo de construção em vigor, fazer blocos nativos do WordPress com React é surpreendentemente fácil e não muito diferente de modelar um bloco em php. A configuração de um ambiente de desenvolvimento de blocos está fora do escopo deste artigo, mas há vários recursos para você começar, como o Manual do editor de blocos do WordPress. Depois de incluir blocos personalizados, você pode criar blocos rapidamente usando o InnerBlocks.
Aqui está um exemplo do nosso bloco de chamada para ação index.js em React. Você verá a mesma estratégia que discutimos acima com ACF aplicada aqui.
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> ); }, });
Vá em frente e construa!
Recursos adicionais
- Artigo de Bill Erickson sobre InnerBlocks
- Artigos de Bill Erickson no Block Editor
- Guia de Blocos ACF
- Documentação do tipo de bloco de registro ACF
- Manual do Editor de Blocos do WordPress
- Manual do editor de blocos do WordPress: criar um tutorial de bloco