Создайте отличный опыт редактирования WordPress и сэкономьте время с помощью Gutenberg InnerBlocks

Опубликовано: 2023-02-12

Не повторяйтесь

Для разработчиков лень может быть достоинством. Хорошо сделанный повторно используемый компонент экономит время и обеспечивает эффективное повторное использование и согласованность. Это хорошо подходит для создания опыта редактирования в WordPress, поскольку согласованность порождает знакомство и знания для тех, кто использует веб-сайт изо дня в день.

WordPress наделал много шума с выпуском блочного редактора Gutenberg, теперь известного просто как «Редактор». Для редактирования, составления и публикации контента он обеспечивает превосходный опыт, намного лучший, чем старые WYSIWYG. Как разработчик, я хочу предоставить своим клиентам лучший опыт редактирования, я хочу использовать основные функции и не хочу изобретать велосипед. К счастью, WordPress позволяет нам делать все это с помощью настраиваемых блоков.

Введите внутренние блоки

InnerBlocks — отличная функция в редакторе WordPress. Разработчики могут использовать основные блоки, такие как абзац, заголовки и кнопки, чтобы создать единый интерфейс для клиента. Вместо того, чтобы переписывать текстовые разделы и переопределять поля, клиент получает опыт, с которым он знаком; редактирование и объединение одинаковых блоков. Давайте посмотрим на блок, который нам может понадобиться построить для клиента, и посмотрим, как InnerBlocks может помочь нам в этом.

Создание блока с помощью InnerBlocks

Пример блока призыва к действию со ссылками на заголовок, абзац и кнопку
Рисунок: пример блока призыва к действию. Это включает в себя заголовок, абзац и некоторые кнопки-ссылки.

Давайте рассмотрим этот блок и подумаем, как мы можем его построить. Обычный инструмент, который мы используем при создании блоков, — это расширенные настраиваемые поля (ACF). ACF Pro добавляет поддержку пользовательских блоков, что позволяет разработчикам писать блоки на PHP для редактора и внешнего интерфейса. Это также позволяет нам использовать настраиваемые поля — парадигму, с которой знакомы многие разработчики WordPress. ACF поддерживает InnerBlocks, что означает, что мы можем создать собственный блок, но даже не нужно будет писать собственный код для этого заголовка, абзаца или кнопок.

С блоками ACF мы можем использовать базовые блоки, чтобы обеспечить отличный опыт редактирования всего текста, который мы видим в этом блоке. Другим потенциальным вариантом может быть использование блочных шаблонов, но разработчики и кураторы фантастического опыта WordPress создают настраиваемый блок для достижения желаемой цели.

Заблокировать регистрационный код

Вот код для регистрации нашего пользовательского блока ACF. Подробнее об использовании функции acf_register_block_type читайте здесь.

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

Давайте разберем код. Мы используем функцию acf_register_block_type для регистрации нашего блока и передаем массив аргументов, которые определяют наш блок и включают и выключают функциональность.

  • Обратите внимание, что мы следуем рекомендуемой процедуре, чтобы добавить уникальное имя для нашего блока, а также удобочитаемый заголовок и описание. Мы указываем на шаблон рендеринга, где позже мы настроим наш PHP-шаблон для блока. Мы также добавили категорию, в которой мы хотим, чтобы наш блок был сгруппирован в средстве вставки блоков.
  • У нас есть ключевые слова с возможностью поиска, чтобы упростить поиск блока в средстве вставки.
  • Мы добавляем массив «supports», чтобы включить такие функции, как установка привязки HTML, и мы отключили выравнивание, так как этот блок всегда будет находиться в центре страницы.
  • Это поддерживает массив, где мы включаем магию InnerBlocks! Установка для jsx значения true сообщает ACF, что мы будем поддерживать рендеринг шаблонов React jsx внутри нашего блока.

Давайте рассмотрим содержимое, которое нам нужно в нашем блоке:

  • Заголовок
  • Текст абзаца
  • Кнопки, чтобы действовать!

Теперь обычно мы назначаем поля нашему блоку, как показано здесь. Но в этом случае нам это не нужно — мы можем использовать базовые блоки для достижения всего этого. Например, если блоку требуется фоновое изображение, добавление поля изображения ACF к блоку может стать отличным способом добиться этого. А пока давайте остановимся на нашем примере InnerBlocks и перейдем к шаблону.

Шаблон блока

Вот шаблон для нового блока. Убедитесь, что регистрация блока указывает на местоположение шаблона блока. Обычной практикой является размещение их в папке partials в теме.

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

Давайте разберем этот код.

  • В начале у нас есть общий шаблон шаблона блока, который ACF предоставляет в своем руководстве по блоку, например, поддержка якоря и пользовательского имени класса.
  • Компонент InnerBlocks может получать свойства. Мы используем три в этом шаблоне.
    • Разрешенные блоки: мы добавляем массив разрешенных блоков для курирования. Только эти блоки будут доступны для выбора внутри пространства InnerBlocks нашего пользовательского блока.
    • Шаблон: мы можем передать массив блоков, и мы можем передать атрибуты блоков для начала, когда они впервые загружаются в редактор.
      • Обратите внимание, что мы можем передавать атрибуты блока, чтобы настроить наших пользователей на успех. Для заголовка уже установлен уровень 2, а абзац расположен по центру.
      • Примечание. Базовые блоки всегда будут упоминаться в JavaScript как {plugin}/blockname. В нашем случае мы используем основные блоки, но если вы хотите использовать пользовательский блок ACF, вы должны написать «acf» перед именем блока. Помните, что при использовании InnerBlocks мы передаем этот компонент в редактор WordPress, который использует React. Вот почему мы думаем в JS здесь.
      • Еще одно замечание: обратите внимание, что блок core/buttons сам использует InnerBlocks! На самом деле мы передаем массив из двух блоков ядра/кнопки внутри!
    • Блокировка шаблона: templateLock устанавливается для компонента InnerBlocks. Если мы установим значение «все», ни один из блоков в нашем шаблоне, предоставленном InnerBlocks, нельзя будет переместить или удалить. Если мы установим его на «вставить», блоки внутри можно будет только перемещать, никакие блоки нельзя будет удалить, и нельзя будет добавить новые блоки. Если мы установим значение «false», экземпляр InnerBlocks будет разблокирован, и он будет разблокирован независимо от любых блокировок родительского шаблона (подробнее об этом ниже). Узнайте больше о шаблонах блоков в Руководстве по редактору блоков WordPress. Мы еще поговорим о блочной блокировке.

У нас новый блок! После всего этого и, конечно же, некоторых стилей блок-пример выглядит в редакторе так:

Блок призыва к действию с выбранным блоком заголовка.
Рисунок: Блок призыва к действию в редакторе. Блок заголовка выбран.

Более глубокое вложение: еще больше курируйте опыт с помощью вложенных внутренних блоков

У нас есть отличный блок, и нам не пришлось настраивать WYSIWYG или даже обрабатывать настраиваемые поля в нашем шаблоне. Но что, если бы мы захотели еще больше улучшить и настроить этот опыт для клиента? Допустим, мы получаем запрос о том, что заголовок всегда присутствует, поэтому всегда есть блок заголовка, но содержимое после него гибкое и может даже включать список или блок другого типа. Наша цель — добиться такой гибкости, сохраняя при этом согласованность.

Рассмотрим некоторые правила при работе с InnerBlocks:

  • В одном блоке может быть только один экземпляр InnerBlocks.
  • Несколько блоков внутри InnerBlocks могут использовать свои собственные компоненты InnerBlocks.
  • InnerBlocks можно заблокировать с помощью свойства templateLock, но экземпляры InnerBlocks, расположенные глубже во вложенной блочной структуре, можно снова разблокировать, установив для templateLock значение false.

Одним из решений является создание нового блока, который служит оболочкой для InnerBlocks. Мы бы включили этот блок в наш шаблон родительского блока и заблокировали его, но явно разблокировали наш блок-оболочку внутри, установив для 'templateLock' значение false. Мы также можем захотеть убедиться, что этот специальный блок-оболочка доступен только в выбранном нами родительском блоке, установив родителя для нашего блока. Мы могли бы разрешить несколько типов блоков в этом пространстве, чтобы предлагать списки редакторов и многое другое, в то же время разрешая только заголовок, кнопки и наш блок-оболочку в родительском блоке призыва к действию.

Блокировка отдельных блоков

Новой функцией WordPress 5.9 является возможность блокировки отдельных блоков в шаблоне. Это еще одно возможное решение нашей гибкой, но последовательной проблемы.

Вот как выглядит наш шаблон с некоторыми заблокированными отдельными блоками:

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

Блоки заголовков и кнопок больше нельзя перемещать или удалять. Обязательно обновите редактор, удалите, а затем снова добавьте блок, чтобы получить изменения шаблона.

Бонус: создание одного и того же блока нативно

После того, как у вас есть процесс сборки, создание собственных блоков WordPress с помощью React становится на удивление простым и не слишком сильно отличается от шаблона блока в php. Настройка среды разработки блоков выходит за рамки этой статьи, но есть ряд ресурсов, которые помогут вам начать работу, например, Руководство по редактору блоков WordPress. Как только вы сможете включать пользовательские блоки, вы сможете быстро создавать блоки с помощью InnerBlocks.

Вот пример нашего блока призыва к действию index.js в React. Здесь вы увидите ту же стратегию, которую мы обсуждали выше, с применением ACF.

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

Иди и строй!

Дополнительные ресурсы

  • Статья Билла Эриксона о InnerBlocks
  • Статьи Билла Эриксона о редакторе блоков
  • Руководство по блокам ACF
  • Документация по типу блока регистра ACF
  • Справочник редактора блоков WordPress
  • Руководство по редактору блоков WordPress: учебник по созданию блока