Creați o experiență grozavă de editare WordPress și economisiți timp cu Gutenberg InnerBlocks

Publicat: 2023-02-12

Nu te repeta

Pentru dezvoltatori, lenea poate fi o virtute. O componentă bine făcută, reutilizabilă economisește timp și permite reutilizarea eficientă și consistența. Acest lucru se pretează bine pentru a crea experiențe de editare în WordPress, deoarece consecvența generează familiaritate și cunoștințe pentru cei care folosesc un site web în fiecare zi.

WordPress a făcut mult zgomot odată cu lansarea editorului de blocuri Gutenberg, cunoscut acum pur și simplu sub numele de „Editorul”. Pentru editarea conținutului, redactarea și publicarea, oferă o experiență excelentă, mult mai bună decât WYSIWYG-urile mai vechi. În calitate de dezvoltator, vreau să ofer cea mai bună experiență de editare pentru clienții mei, vreau să profit de funcționalitatea de bază și nu vreau să reinventez roata. Din fericire, WordPress ne permite să facem toate aceste lucruri cu blocuri personalizate.

Introduceți InnerBlocks

InnerBlocks este o caracteristică excelentă în editorul WordPress. Dezvoltatorii pot folosi blocuri de bază precum paragraful, titlurile și butoanele pentru a crea o experiență coerentă pentru un client. În loc să rescrie secțiuni de text și să redeclare câmpuri, clientul primește o experiență cu care se familiarizează; editarea și combinarea acelorași blocuri. Să aruncăm o privire la un bloc pe care ar putea fi necesar să îl construim pentru un client și să vedem cum InnerBlocks ne poate ajuta să-l realizăm.

Construirea unui bloc cu InnerBlocks

Un exemplu de bloc îndemn la acțiune cu linkuri pentru titlu, paragraf și butoane
Figura: un exemplu de bloc de apel la acțiune. Aceasta include un titlu, un paragraf și câteva link-uri pentru butoane.

Să examinăm acest bloc și să ne gândim cum l-am putea construi. Un instrument comun la care folosim atunci când facem blocuri este Advanced Custom Fields (ACF). ACF Pro adaugă suport pentru blocuri personalizate, care le permite dezvoltatorilor să scrie blocuri în PHP pentru editor și front-end. De asemenea, ne permite să folosim câmpuri personalizate - o paradigmă cu care mulți dezvoltatori WordPress sunt familiarizați. ACF acceptă InnerBlocks, ceea ce înseamnă că putem crea un bloc personalizat, dar nici măcar nu va trebui să scriem cod personalizat pentru acel titlu, paragraf sau butoane.

Cu blocurile ACF, putem folosi blocurile de bază pentru a crea o experiență de editare grozavă pentru tot textul pe care îl vedem în acest bloc. O altă opțiune potențială ar fi utilizarea modelelor de bloc, dar în calitate de dezvoltatori și curatori ai unei experiențe WordPress fantastice, crearea unui bloc personalizat atinge obiectivul dorit.

Blocați codul de înregistrare

Iată codul pentru înregistrarea blocului nostru personalizat ACF. Citiți mai multe despre utilizarea funcției acf_register_block_type aici.

 <?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. ), ) ); } );

Să descompun codul. Folosim funcția acf_register_block_type pentru a ne înregistra blocul și transmitem o serie de argumente care definesc blocul nostru și activează și dezactivează funcționalitatea.

  • Observați că urmăm procedura recomandată pentru a adăuga un nume unic pentru blocul nostru, precum și un titlu și o descriere care pot fi citite de om. Indicăm un șablon de randare, unde vom configura șablonul PHP pentru bloc mai târziu. Am adăugat, de asemenea, o categorie în care dorim ca blocul nostru să fie grupat în Block Inserter.
  • Avem cuvinte cheie care pot fi căutate pentru a face blocul mai ușor de căutat în insertor.
  • Adăugăm o matrice „suportă” pentru a activa funcționalități precum setarea unei ancori HTML și am dezactivat alinierea, deoarece acest bloc va sta întotdeauna în centrul paginii.
  • Care acceptă matrice este locul în care activăm magia InnerBlocks! Setarea „jsx” la true îi spune ACF că vom accepta redarea șablonului React jsx în interiorul blocului nostru.

Să revizuim conținutul de care avem nevoie în blocul nostru:

  • Titlu
  • Textul paragrafului
  • Butoane pentru a lua măsuri!

Acum, în mod normal, am atribui câmpuri blocului nostru, așa cum este subliniat aici. Dar, în acest caz, nu avem nevoie - putem folosi blocurile de bază pentru a realiza toate acestea. Dacă blocul are nevoie de un fundal de imagine, de exemplu, adăugarea unui câmp de imagine ACF la bloc ar putea fi o modalitate excelentă de a realiza acest lucru. Deocamdată, totuși, să rămânem la exemplul nostru InnerBlocks și să trecem la șablon.

Șablon de bloc

Iată șablonul pentru noul bloc. Asigurați-vă că îndreptați înregistrarea blocului către locația șablonului de bloc. O practică obișnuită este punerea acestora într-un folder parțial din temă.

 <?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>

Să defalcăm acest cod.

  • La început, avem un șablon de bloc generic pe care ACF îl oferă în ghidul lor de bloc, cum ar fi suport pentru o ancoră și un nume personalizat de clasă.
  • Componenta InnerBlocks poate primi proprietăți. Folosim trei în acest șablon.
    • Blocuri permise: Adăugăm o serie de blocuri permise pentru a fi curate. Doar aceste blocuri vor fi disponibile pentru selecție în spațiul InnerBlocks al blocului nostru personalizat.
    • Șablon: putem trece o serie de blocuri și putem transmite atribute de bloc pentru blocuri cu care să înceapă când se încarcă prima dată în editor.
      • Observați că putem transmite atribute de blocare pentru a configura utilizatorii noștri pentru succes. Titlul are deja setat nivelul 2, iar paragraful este centrat.
      • Notă: blocurile de bază vor fi întotdeauna denumite în JavaScript ca {plugin}/blockname. În cazul nostru, folosim blocuri de bază, dar dacă doriți să utilizați un bloc ACF personalizat, ați scrie „acf” în fața numelui blocului. Amintiți-vă, când folosiți InnerBlocks, transmitem această componentă Editorului WordPress, care utilizează React. De aceea ne gândim în JS aici.
      • O altă notă: observați că blocul de bază/butoane utilizează InnerBlocks în sine! De fapt, trecem o serie de două blocuri de bază/buton în interior!
    • Blocare șablon: templateLock este setat pe componenta InnerBlocks. Dacă îl setăm la „toate”, niciunul dintre blocurile din șablonul nostru furnizat către InnerBlocks nu ar putea fi mutat sau eliminat. Dacă îl setăm la „inserare”, blocurile din interior ar putea fi doar mutate, niciun bloc nu ar putea fi eliminat și niciun bloc nou nu ar putea fi adăugat. Dacă o setăm la „false”, instanța InnerBlocks va fi deblocată și va fi deblocată indiferent de orice blocare a șablonului părinte (mai multe despre aceasta mai jos). Citiți mai multe despre șabloanele de bloc în Manualul Editorului de blocuri WordPress. Vom vorbi mai mult despre blocarea blocurilor.

Avem un bloc nou! După toate acestea, și ceva stil, desigur, blocul exemplu arată astfel în editor:

Blocul îndemn la acțiune cu blocul de antet selectat.
Figura: Blocul Call to Action din editor. Blocul de titlu este selectat.

Nesting Deeper: Curăți experiența și mai mult cu Imbricate InnerBlocks

Avem un bloc grozav și nu a trebuit să setăm un WYSIWYG sau chiar să gestionăm câmpuri personalizate în șablonul nostru. Dar dacă am vrea să organizăm și să perfecționăm această experiență și mai mult pentru client? Să presupunem că primim o solicitare prin care ne asigurăm că titlul este întotdeauna prezent, deci există întotdeauna un bloc de titlu, dar conținutul de după este flexibil și poate include chiar o listă sau un alt tip de bloc. Scopul nostru este să atingem această flexibilitate, păstrând în același timp consistența.

Să luăm în considerare câteva reguli atunci când lucrați cu InnerBlocks:

  • Un singur bloc poate avea o singură instanță de InnerBlocks în interior.
  • Blocurile multiple din interiorul InnerBlocks pot folosi propriile lor componente InnerBlocks.
  • InnerBlocks pot fi blocate prin proprietatea templateLock, dar instanțe de InnerBlocks mai adânc într-o structură de bloc imbricate pot fi deblocate din nou setând templateLock la false.

O soluție este crearea unui bloc nou care să servească drept înveliș pentru InnerBlocks. Am include acel bloc în șablonul nostru de bloc părinte și l-am bloca, dar am debloca în mod explicit blocul nostru wrapper prin setarea „templateLock” la false. De asemenea, putem dori să ne asigurăm că acest bloc special de înveliș este disponibil numai în blocul părinte pe care îl alegem, setând un părinte pentru blocul nostru. Am putea permite mai multor tipuri de blocuri în spațiul respectiv să ofere liste de editori și multe altele, permițând totuși doar titlul, butoanele și blocul nostru de înveliș în blocul părinte Call to Action.

Blocare individuală a blocului

O caracteristică nouă în WordPress 5.9 este capacitatea de a bloca blocuri individuale într-un șablon. Aceasta este o altă soluție posibilă la problema noastră flexibilă, dar constantă.

Iată cum arată șablonul nostru cu unele blocuri individuale blocate:

 <?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>

Blocurile de titlu și butoane nu ar trebui să mai poată fi mutate sau îndepărtate. Asigurați-vă că reîmprospătați editorul, eliminați și apoi adăugați din nou blocul pentru a obține modificările de șablon.

Bonus: realizarea aceluiași bloc în mod nativ

Odată ce aveți un proces de construire în vigoare, crearea de blocuri WordPress native cu React este surprinzător de ușoară și nu prea teribil de diferită de a șablona un bloc în php. Configurarea unui mediu de dezvoltare bloc nu intră în domeniul de aplicare al acestui articol, dar există o serie de resurse pentru a începe, cum ar fi Manualul WordPress Block Editor. Odată ce puteți include blocuri personalizate, puteți construi rapid blocuri folosind InnerBlocks.

Iată un exemplu al blocului nostru de apel la acțiune index.js în React. Veți vedea aceeași strategie pe care am discutat mai sus cu ACF aplicată aici.

 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> ); }, });

Ieși și construiește!

Resurse aditionale

  • Articolul lui Bill Erickson despre InnerBlocks
  • Articolele lui Bill Erickson despre Block Editor
  • Ghidul blocurilor ACF
  • Documentație tip bloc de registru ACF
  • Manual WordPress Block Editor
  • Manual WordPress Block Editor: Creați un tutorial de bloc