Créez une excellente expérience d'édition WordPress et gagnez du temps avec Gutenberg InnerBlocks
Publié: 2023-02-12Ne vous répétez pas
Pour les développeurs, la paresse peut être une vertu. Un composant bien conçu et réutilisable fait gagner du temps et permet une réutilisation efficace et cohérente. Cela se prête bien à la création d'expériences d'édition dans WordPress, car la cohérence engendre la familiarité et les connaissances pour ceux qui utilisent un site Web au quotidien.
WordPress a fait beaucoup de bruit avec la sortie de l'éditeur de blocs Gutenberg, maintenant connu simplement sous le nom de "The Editor". Pour l'édition, la rédaction et la publication de contenu, il offre une excellente expérience, bien meilleure que les anciens WYSIWYG. En tant que développeur, je veux offrir la meilleure expérience d'édition à mes clients, je veux tirer parti des fonctionnalités de base et je ne veux pas réinventer la roue. Heureusement, WordPress nous permet de faire toutes ces choses avec des blocs personnalisés.
Entrez InnerBlocks
InnerBlocks est une fonctionnalité intéressante de l'éditeur WordPress. Les développeurs peuvent utiliser des blocs de base comme le paragraphe, les titres et les boutons pour créer une expérience cohérente pour un client. Au lieu de réécrire des sections de texte et de redéclarer des champs, le client obtient une expérience avec laquelle il se familiarise ; éditer et combiner les mêmes blocs. Examinons un bloc que nous pourrions avoir besoin de créer pour un client et voyons comment InnerBlocks peut nous aider à y parvenir.
Construire un bloc avec InnerBlocks
Examinons ce bloc et considérons comment nous pourrions le construire. Un outil commun que nous utilisons lors de la création de blocs est les champs personnalisés avancés (ACF). ACF Pro ajoute la prise en charge des blocs personnalisés, ce qui permet aux développeurs d'écrire des blocs en PHP pour l'éditeur et le front-end. Cela nous permet également d'utiliser des champs personnalisés, un paradigme avec lequel de nombreux développeurs WordPress sont familiers. ACF prend en charge InnerBlocks, ce qui signifie que nous pouvons créer un bloc personnalisé, mais nous n'aurons même pas besoin d'écrire de code personnalisé pour ce titre, ce paragraphe ou ces boutons.
Avec ACF Blocks, nous pouvons tirer parti des blocs de base pour créer une excellente expérience d'édition pour tout le texte que nous voyons dans ce bloc. Une autre option potentielle serait d'utiliser des modèles de blocs, mais en tant que développeurs et conservateurs d'une expérience WordPress fantastique, la création d'un bloc personnalisé atteint l'objectif souhaité.
Bloquer le code d'enregistrement
Voici le code pour enregistrer notre bloc ACF personnalisé. En savoir plus sur l'utilisation de la fonction acf_register_block_type ici.
<?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. ), ) ); } );
Décomposons le code. Nous utilisons la fonction acf_register_block_type pour enregistrer notre bloc, et nous passons un tableau d'arguments qui définissent notre bloc et activent et désactivent la fonctionnalité.
- Notez que nous suivons la procédure recommandée pour ajouter un nom unique à notre bloc, ainsi qu'un titre et une description lisibles par l'homme. Nous pointons vers un modèle de rendu, où nous configurerons notre modèle PHP pour le bloc plus tard. Nous avons également ajouté une catégorie dans laquelle nous voulons que notre bloc soit regroupé dans le Block Inserter.
- Nous avons des mots-clés interrogeables pour faciliter la recherche du bloc dans l'inséreuse.
- Nous ajoutons un tableau "supports" pour activer des fonctionnalités telles que la définition d'une ancre HTML et nous avons désactivé l'alignement car ce bloc sera toujours placé au centre de la page.
- C'est là que nous activons la magie InnerBlocks ! Définir 'jsx' sur true indique à ACF que nous prendrons en charge le rendu des modèles React jsx à l'intérieur de notre bloc.
Passons en revue le contenu dont nous avons besoin dans notre bloc :
- Titre
- Texte du paragraphe
- Des boutons pour passer à l'action !
Maintenant, normalement, nous assignerions des champs à notre bloc, comme indiqué ici. Mais dans ce cas, nous n'en avons pas besoin - nous pouvons tirer parti des blocs de base pour réaliser tout cela. Si le bloc a besoin d'un arrière-plan d'image, par exemple, l'ajout d'un champ d'image ACF au bloc pourrait être un excellent moyen d'y parvenir. Pour l'instant, cependant, restons-en à notre exemple InnerBlocks et passons au modèle.
Modèle de bloc
Voici le modèle du nouveau bloc. Assurez-vous de faire pointer l'enregistrement de bloc vers l'emplacement du modèle de bloc. Une pratique courante consiste à les placer dans un dossier partiels du thème.
<?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>
Décomposons ce code.
- Au début, nous avons un passe-partout de modèle de bloc générique qu'ACF fournit dans ses instructions de bloc, comme la prise en charge d'une ancre et d'un nom de classe personnalisé.
- Le composant InnerBlocks peut recevoir des propriétés. Nous en utilisons trois dans ce modèle.
- Blocs autorisés : nous ajoutons un tableau de blocs autorisés à gérer. Seuls ces blocs seront disponibles pour la sélection dans l'espace InnerBlocks de notre bloc personnalisé.
- Modèle : nous pouvons transmettre un tableau de blocs et nous pouvons transmettre des attributs de bloc pour les blocs de départ lors de leur premier chargement dans l'éditeur.
- Notez que nous pouvons transmettre des attributs de bloc pour préparer nos utilisateurs au succès. Le titre a déjà défini le niveau 2 et le paragraphe est centré.
- Remarque : Les blocs de base seront toujours référencés en JavaScript sous le nom de {plugin}/blockname. Dans notre cas, nous utilisons des blocs de base, mais si vous vouliez utiliser un bloc ACF personnalisé, vous écririez "acf" devant le nom du bloc. N'oubliez pas que lorsque vous utilisez InnerBlocks, nous transmettons ce composant à l'éditeur WordPress, qui utilise React. C'est pourquoi nous pensons en JS ici.
- Autre remarque : notez que le bloc principal/boutons utilise lui-même InnerBlocks ! Nous passons en fait un tableau de deux blocs noyau/bouton à l'intérieur !
- Verrouillage de modèle : templateLock est défini sur le composant InnerBlocks. Si nous le définissons sur "tous", aucun des blocs de notre modèle fourni à InnerBlocks ne pourra être déplacé ou supprimé. Si nous le réglons sur "insérer", les blocs à l'intérieur ne peuvent être déplacés, aucun bloc ne peut être supprimé et aucun nouveau bloc ne peut être ajouté. Si nous le définissons sur "false", l'instance InnerBlocks sera déverrouillée et elle sera déverrouillée indépendamment de tout verrou de modèle parent (plus à ce sujet plus bas). En savoir plus sur les modèles de blocs dans le manuel de l'éditeur de blocs WordPress. Nous parlerons davantage du blocage des blocs.
Nous avons un nouveau bloc ! Après tout cela, et un peu de style, bien sûr, le bloc d'exemple ressemble à ceci dans l'éditeur :
Nesting Deeper : organiser encore plus l'expérience avec Nested InnerBlocks
Nous avons un excellent bloc et nous n'avons pas eu à configurer de WYSIWYG ni même à gérer des champs personnalisés dans notre modèle. Mais que se passerait-il si nous voulions organiser et affiner encore plus cette expérience pour le client ? Disons que nous recevons une demande pour nous assurer que l'en-tête est toujours présent, donc il y a toujours un bloc d'en-tête, mais le contenu après est flexible et pourrait même inclure une liste ou un autre type de bloc. Notre objectif est d'atteindre cette flexibilité, tout en préservant la cohérence.
Considérons quelques règles lorsque nous travaillons avec InnerBlocks :
- Un seul bloc ne peut contenir qu'une seule instance d'InnerBlocks.
- Plusieurs blocs à l'intérieur de InnerBlocks peuvent utiliser leurs propres composants InnerBlocks.
- Les InnerBlocks peuvent être verrouillés via la propriété templateLock, mais les instances d'InnerBlocks plus profondes dans une structure de bloc imbriquée peuvent être déverrouillées à nouveau en définissant templateLock sur false.
Une solution consiste à créer un nouveau bloc qui sert de wrapper pour InnerBlocks. Nous inclurions ce bloc dans notre modèle de bloc parent et le verrouillerions, mais nous déverrouillerions explicitement notre bloc wrapper en définissant 'templateLock' sur false. Nous pouvons également vouloir nous assurer que ce bloc wrapper spécial n'est disponible que dans le bloc parent que nous choisissons, en définissant un parent pour notre bloc. Nous pourrions autoriser plusieurs types de blocs dans cet espace pour proposer des listes d'éditeurs et plus encore, tout en n'autorisant que l'en-tête, les boutons et notre bloc wrapper dans le bloc parent Call to Action.
Verrouillage de bloc individuel
Une nouvelle fonctionnalité de WordPress 5.9 est la possibilité de verrouiller des blocs individuels dans un modèle. C'est une autre solution possible à notre problème flexible mais cohérent.
Voici à quoi ressemble notre modèle avec certains blocs individuels verrouillés :
<?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>
L'en-tête et les blocs de boutons ne devraient plus pouvoir être déplacés ou supprimés. Assurez-vous d'actualiser l'éditeur, de supprimer, puis d'ajouter à nouveau le bloc pour obtenir les modifications de modèle.
Bonus : Faire le même bloc nativement
Une fois que vous avez mis en place un processus de construction, créer des blocs WordPress natifs avec React est étonnamment facile et pas trop différent de la création d'un bloc en php. La configuration d'un environnement de développement de blocs sort du cadre de cet article, mais il existe un certain nombre de ressources pour vous aider à démarrer, telles que le manuel de l'éditeur de blocs WordPress. Une fois que vous êtes en mesure d'inclure des blocs personnalisés, vous pouvez rapidement créer des blocs à l'aide d'InnerBlocks.
Voici un exemple de notre bloc Call to Action index.js dans React. Vous verrez la même stratégie dont nous avons discuté ci-dessus avec ACF appliquée ici.
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> ); }, });
Allez-y et construisez !
Ressources additionnelles
- Article de Bill Erickson sur InnerBlocks
- Les articles de Bill Erickson sur l'éditeur de blocs
- Guide des blocs ACF
- Documentation sur le type de bloc de registre ACF
- Manuel de l'éditeur de blocs WordPress
- Manuel de l'éditeur de blocs WordPress : créer un didacticiel de blocs