1. Code
  2. WordPress
  3. Plugin Development

WordPress Gutenberg Block API: создание пользовательских блоков

Scroll to top
This post is part of a series called WordPress Gutenberg Block API.
WordPress Gutenberg Block API: Block Look and Feel
WordPress Gutenberg Block API: Extending Blocks

Russian (Pусский) translation by Anna Goorikova (you can also view the original English article)

Новый редактор WordPress (под кодовым названием Gutenberg) должен быть выпущен в версии 5.0. Сейчас самое время разобраться с этим, прежде чем он попадет в ядро WordPress. В этой серии я покажу вам, как работать с Block API и создавать свои собственные блоки контента, которые вы можете использовать для создания своих постов и страниц.

В предыдущем посте мы увидели, как использовать набор инструментов create-guten-block для создания плагина, содержащего образец блока, с которым мы можем работать. Мы можем легко расширить это по мере необходимости, но это хорошая идея, чтобы знать, как создать блок с нуля, чтобы полностью понять все аспекты разработки блока Гутенберга.

В этом посте мы создадим второй блок в нашем плагине my-custom-block для отображения случайного изображения из веб-службы PlaceIMG. Вы также сможете настроить блок, выбрав категорию изображения в раскрывающемся списке выбора.

Но сначала мы узнаем, как блочные сценарии и стили ставятся в очередь, прежде чем перейти к крайне важной функции registerBlockType(), которая является фундаментальной для создания блоков в Гутенберге.

Добавление в очередь блок-скриптов и стилей

Чтобы добавить JavaScript и CSS, необходимые для наших блоков, мы можем использовать два новых хука WordPress, предоставленных Гутенбергом:

  • enqueue_block_editor_assets
  • enqueue_block_assets

Они доступны только в том случае, если подключаемый модуль Gutenberg активен и работает аналогично стандартным хукам WordPress для постановки сценариев в очередь. Однако они предназначены специально для работы с блоками Гутенберга.

Хук enqueue_block_editor_assets добавляет скрипты и стили только в редактор администратора. Это делает его идеальным для постановки в очередь JavaScript для регистрации блоков и CSS для стилизации элементов пользовательского интерфейса для настроек блоков.

Тем не менее, для вывода блоков вам нужно, чтобы ваши блоки отображались в редакторе так же, как и во внешнем интерфейсе. Использование enqueue_block_assets делает это простым, поскольку стили автоматически добавляются как в редактор, так и в интерфейс. Вы также можете использовать этот хук для загрузки JavaScript, если это необходимо.

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

1
add_action( 'enqueue_block_assets', 'load_front_end scripts' );
2
function load_front_end scripts() {
3
    if( !is_admin() {
4
        // enqueue front end only scripts and styles here

5
    }
6
}

Чтобы поставить в очередь скрипты и стили с помощью этих двух новых хуков, вы можете использовать стандартные функции wp_enqueue_style() и wp_enqueue_scripts(), как обычно.

Однако вы должны убедиться, что вы используете правильные зависимости скрипта. Для постановки сценариев в редакторе вы можете использовать следующие зависимости:

  • скрипты: array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-components' )
  • стили: array( 'wp-edit-blocks' )

И когда вы ставите в очередь стили как для редактора, так и для внешнего интерфейса, вы можете использовать эту зависимость:

  • array( 'wp-blocks' )

Здесь стоит упомянуть, что настоящие файлы, поставленные в очередь нашим плагином my-custom-block, представляют собой скомпилированные версии JavaScript и CSS, а не файлы, содержащие исходный код JSX и Sass.

Об этом следует помнить, когда файлы ставятся в очередь вручную. Если вы попытаетесь поставить в очередь необработанный исходный код, включающий React, ES6+ или Sass, то вы столкнетесь с многочисленными ошибками.

Вот почему полезно использовать инструментарий, такой как create-guten-block, поскольку он обрабатывает и ставит в очередь скрипты для вас автоматически!

Регистрация блоков Гутенберга

Чтобы создать новый блок, нам нужно зарегистрировать его в Gutenberg, вызвав registerBlockType() и передав имя блока плюс объект конфигурации. Этот объект имеет довольно много свойств, которые требуют правильного объяснения.

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

Например:

1
registerBlockType(
2
    'block-namespace/block-name',
3
    {
4
        // configuration object

5
    }
6
);

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

Второй параметр registerBlockType() является объектом настроек и требует указания ряда свойств:

  • title (строка): отображается в редакторе как метка блока с возможностью поиска
  • description (необязательная строка): описывает назначение блока
  • icon (необязательный элемент Dashicon или JSX): значок, связанный с блоком
  • category (строка): где блок появляется в диалоговом окне Add blocks
  • keywords (необязательный массив): до трех ключевых слов, используемых при поиске блоков
  • attributes (необязательный объект): обрабатывает данные динамического блока
  • edit (функция): функция, которая возвращает разметку для отображения в редакторе
  • save (function): функция, которая возвращает разметку, которая будет отображаться на внешнем интерфейсе
  • useOnce (необязательный логический): запретить добавление блока более одного раза
  • support (необязательный объект): определяет поддерживаемые блоком функции

Предполагая, что мы используем JSX для разработки блоков, вот как может выглядеть пример вызова registerBlockType() для очень простого блока:

1
registerBlockType(
2
    'my-unique-namespace/ultimate-block',
3
    {
4
        title: __( 'The Best Block Ever', 'domain' ),
5
        icon: 'wordpress',
6
        category: 'common',
7
        keywords: [ __( 'sample', 'domain' ), __( 'Gutenberg', 'domain' ), __( 'block', 'domain' ) ],
8
        edit: () => <h2>Welcome to the Gutenberg Editor!</h2>,

9
        save: () => <h2>How am I looking on the front end?</h2>

10
    }
11
);

Обратите внимание, что мы не включили запись для description, attributes, useOnce и supports свойств. Любые поля, которые являются необязательными (и не относятся к вашему блоку), могут быть безопасно пропущены. Например, поскольку в этом блоке не было динамических данных, нам не нужно беспокоиться об указании каких-либо атрибутов.

Давайте теперь рассмотрим свойства объекта конфигурации registerBlockType() более подробно одно за другим.

Title и Description

При вставке или поиске блока в редакторе заголовок будет отображаться в списке доступных блоков.

Он также отображается в окне инспектора блоков вместе с описанием блока, если оно определено. Если инспектор блоков в данный момент не отображается, вы можете использовать значок шестеренки в верхнем правом углу редактора Гутенберга, чтобы переключать видимость.

Block title and descriptionBlock title and descriptionBlock title and description

Поля заголовка и описания в идеале должны быть заключены в функции i18n, чтобы обеспечить возможность перевода на другие языки.

Category

В настоящее время доступно пять категорий блоков:

  • common
  • formatting
  • layout
  • widgets
  • embed

Они определяют раздел категории, в котором ваш блок указан в окне Add block.

Block categoriesBlock categoriesBlock categories

Вкладка Blocks содержит категории Common Blocks, Formatting, Layout Elements и Widgets, а категория Embeds имеет собственную вкладку.

Категории в настоящее время фиксированы в Гутенберге, но это может измениться в будущем. Это было бы полезно, например, если вы разрабатывали несколько блоков в одном плагине и хотели, чтобы все они были перечислены в общей категории, чтобы пользователи могли легче их находить.

Icon

По умолчанию вашему блоку назначен Dashicon, но вы можете переопределить его, указав пользовательский Dashicon в поле icon.

DashiconsDashiconsDashicons

Каждый Dashicon имеет префикс dashicons-, за которым следует уникальная строка. Например, значок шестеренки имеет название dashicons-admin-generic. Чтобы использовать его как значок блока, просто удалите префикс dashicons-, чтобы он был распознан, например, icon: 'admin-generic'.

Тем не менее, вы не ограничены использованием Dashicons в качестве свойства icon. Функция также принимает элемент JSX, что означает, что вы можете использовать любое изображение или элемент SVG в качестве значка вашего блока.

Просто убедитесь, что размер значков соответствует значкам других блоков, которые по умолчанию составляют 20 x 20 пикселей.

Keywords

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

1
keywords: [
2
    __( 'search', 'domain' ),
3
    __( 'for', 'domain' ),
4
    __( 'me', 'domain' ),
5
]

Вы даже можете объявить название своей компании и/или плагина в качестве ключевых слов, чтобы при наличии нескольких блоков пользователи могли начать вводить название вашей компании, и все ваши блоки будут отображаться в результатах поиска.

Хотя добавление ключевых слов не является обязательным, это отличный способ облегчить пользователям поиск ваших блоков.

Attributes

Атрибуты помогают управлять динамическими данными в блоке. Это свойство является необязательным, поскольку оно не требуется для очень простых блоков, которые выводят статическое содержимое.

Однако, если ваш блок имеет дело с данными, которые могут измениться (например, с содержимым редактируемой текстовой области) или вам необходимо сохранить настройки блока, то использование атрибутов - это то, что вам нужно.

Атрибуты работают путем хранения данных динамического блока либо в содержимом поста, сохраненном в базе данных, либо в мета данных поста. В этом уроке мы будем использовать только атрибуты, которые хранят данные вместе с содержимым поста.

Чтобы извлечь данные атрибута, хранящиеся в содержимом поста, нам нужно указать, где он находится в разметке. Мы можем сделать это, указав селектор CSS, который указывает на данные атрибута.

Например, если наш блок содержал тег anchor, мы можем использовать его поле title в качестве нашего атрибута следующим образом:

1
attributes: {
2
    linktitle: {
3
        source: 'attribute',
4
        selector: 'a',
5
        attribute: 'title'
6
    }
7
}

Здесь имя атрибута - linktitle, который является произвольной строкой и может быть любым, что вам нравится.

Например, мы могли бы использовать этот атрибут, чтобы сохранить заголовок ссылки <a title="some title">, который был введен через текстовое поле в настройках блока. Это неожиданно делает поле заголовка динамическим и позволяет изменить его значение в редакторе.

Когда запись сохранена, значение атрибута вставляется в поле ссылки title. Аналогичным образом, когда загружается редактор, значение тега title извлекается из содержимого и вставляется в атрибут linktitle.

Есть и другие способы использования source для определения того, как содержимое блока хранится или извлекается с помощью атрибутов. Например, вы можете использовать источник 'text' для извлечения внутреннего текста из элемента абзаца.

1
attributes: {
2
    paragraph: {
3
    source: 'text',
4
    selector: 'p'
5
    }
6
}

Вы также можете использовать дочерний source, чтобы извлечь все дочерние узлы элемента в массив и сохранить его в атрибуте.

1
attributes: {
2
    editablecontent: {
3
        source: 'children',
4
        selector: '.parent'
5
    }
6
}

Это выбирает элемент с классом .parent и сохраняет все дочерние узлы в атрибуте editablecontent.

Если вы не укажете источник, тогда значение атрибута будет сохранено в HTML-комментариях как часть разметки поста при сохранении в базе данных. Эти комментарии удаляются до того, как сообщение будет отображено в интерфейсе.

Мы увидим конкретный пример этого типа атрибута, когда перейдем к реализации нашего блока случайных изображений позже в этом уроке.

Привыкание к атрибутам может занять какое-то время, поэтому не беспокойтесь, если вы не до конца их поняли. Я бы порекомендовал взглянуть на раздел атрибутов справочника Гутенберга для более подробной информации и примеров.

Edit

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

Обычной практикой является добавление className={props.className} к элементу основного блока, чтобы вывести класс CSS, специфичный для вашего блока. WordPress не добавляет это для вас в редакторе, поэтому его нужно добавлять вручную для каждого блока, если вы хотите включить его.

Использование props.className является стандартной практикой и рекомендуется, так как она предоставляет способ настройки стилей CSS для каждого отдельного блока. Формат сгенерированного класса CSS: .wp-block-{namespace}-{name}. Как видите, это основано на пространстве имен/именах блоков и делает его идеальным для использования в качестве уникального идентификатора блока.

Функция редактирования требует, чтобы вы возвращали действительную разметку с помощью метода render(), который затем используется для визуализации вашего блока в редакторе.

Save

Подобно функции edit, команда save отображает визуальное представление вашего блока, но на передней панели. Он также получает данные динамического блока (если определены) через props.

Одно из главных отличий заключается в том, что props.className недоступен внутри save, но это не проблема, поскольку он автоматически вставляется Гутенбергом.

Supports

Свойство supports принимает объект с логическими значениями, чтобы определить, поддерживает ли ваш блок определенные основные функции.

Доступны следующие свойства объекта:

  • anchor (по умолчанию false): позволяет напрямую связываться с конкретным блоком
  • customClassName (true): добавляет поле в инспекторе блоков, чтобы определить имя пользовательского класса className для блока
  • className (true): добавляет className к корневому элементу блока на основе имени блока
  • html (true): позволяет редактировать разметку блока напрямую

Свойство useOnce

Это полезное свойство, которое позволяет ограничить добавление блока на страницу более одного раза. Примером этого является встроенный блок More, который нельзя добавить на страницу, если он уже есть.

useOnce propertyuseOnce propertyuseOnce property

Как видите, после добавления блока More значок блока становится серым и не может быть выбран. По умолчанию для свойства useOnce установлено значение false.

Давайте проявим креативность!

Настало время использовать знания, которые мы получили до настоящего времени, для реализации надежного рабочего примера блока, который делает что-то более интересное, чем просто вывод статического содержимого.

Мы будем строить блок для вывода случайного изображения, полученного с помощью внешнего запроса на сайт PlaceIMG, который возвращает случайное изображение. Кроме того, вы сможете выбрать категорию возвращаемого изображения с помощью выпадающего списка.

Our random image blockOur random image blockOur random image block

Для удобства мы добавим наш новый блок в тот же плагин my-custom-block, а не создадим новый плагин. Код для блока по умолчанию находится в папке /src/block, поэтому добавьте еще одну папку в /src под названием random-image и добавьте три новых файла:

  • index.js: регистрирует наш новый блок
  • editor.scss: для стилей редактора
  • style.scss: стили для редактора и внешнего интерфейса

Кроме того, вы можете скопировать папку /src/block и переименовать ее, если вы не хотите печатать все вручную. Убедитесь, что переименовали block.js в index.js внутри новой папки блоков.

Мы используем index.js для имени файла основного блока, поскольку это позволяет нам упростить оператор импорта внутри blocks.js. Вместо того, чтобы включать путь и полное имя файла блока, мы можем просто указать путь к папке блока, и import автоматически найдет файл index.js.

Откройте файл /src/blocks.js и добавьте ссылку на наш новый блок в верхней части файла, прямо под существующим оператором импорта.

1
import './random-image';

Внутри /src/random-image/index.js добавьте следующий код, чтобы запустить наш новый блок.

1
//  Import CSS.

2
import './style.scss';
3
import './editor.scss';
4
5
const { __ } = wp.i18n;
6
const { registerBlockType, query } = wp.blocks;
7
8
registerBlockType( 'cgb/block-random-image', {
9
    title: __( 'Random Image' ),
10
    icon: 'format-image',
11
    category: 'common',
12
    keywords: [
13
        __( 'random' ),
14
        __( 'image' )
15
    ],
16
    edit: function( props ) {
17
        return (
18
            <div className={ props.className }>
19
                <h3>Random image block (editor)</h3>

20
            </div>

21
        );
22
    },
23
    save: function( props ) {
24
        return (
25
            <div>
26
                <h3>Random image block (front end)</h3>

27
            </div>

28
        );
29
    }
30
} );

Это устанавливает структуру нашего блока и он очень похож на стандартный блочный код, сгенерированный инструментарием create-guten-block.

Мы начнем с импорта редактора и внешних таблиц стилей, а затем извлечем некоторые часто используемые функции из wp.i18n и wp.blocks в локальные переменные.

Внутри registerBlockType() были введены значения для свойств title, icon, category и keywords, в то время как функции edit и save в настоящее время просто выводят статическое содержимое.

Добавьте блок случайных изображений на новую страницу, чтобы увидеть сгенерированный результат.

Bare bones block outputBare bones block outputBare bones block output

Убедитесь, что вы по-прежнему просматриваете файлы на предмет изменений, чтобы любой исходный код блока (JSX, ES6+, Sass и т.д.) транслировался в действительный JavaScript и CSS. Если вы не просматриваете файлы на предмет изменений, откройте окно командной строки в корневой папке плагина my-custom-block и введите npm start.

Вы можете быть удивлены, почему нам не нужно было добавлять PHP-код для постановки дополнительных блочных сценариев. Сценарии блоков для блока my-custom-block ставятся в очередь через init.php, но нам не нужно ставить сценарии в очередь специально для нашего нового блока, так как об этом позаботился наш create-guten-block.

Пока мы импортируем наш основной файл блока в src/blocks.js (как мы это делали выше), нам не нужно делать никакой дополнительной работы. Весь код JSX, ES6+ и Sass будет автоматически скомпилирован в следующие файлы:

  • dist/blocks.style.build.css: стили для редактора и внешнего интерфейса
  • dist/blocks.build.js: JavaScript только для редактора
  • dist/blocks.editor.build.css: стили только для редактора

Эти файлы содержат JavaScript и CSS для всех блоков, поэтому нужно ставить в очередь только один набор файлов, независимо от количества созданных блоков!

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

1
attributes: {
2
    category: {
3
        type: 'string',
4
        default: 'nature'
5
    }
6
}

Это создает атрибут под названием category, который хранит строку со значением по умолчанию 'nature'. Мы не указали место в разметке для хранения и получения значения атрибута, поэтому вместо него будут использоваться специальные комментарии HTML. Это поведение по умолчанию, если вы пропустите источник атрибута.

Нам нужен какой-то способ изменения категории изображений, который мы можем сделать с помощью выпадающего списка. Обновите функцию edit следующим образом:

1
edit: function( props ) {
2
    const { attributes: { category }, setAttributes } = props;
3
    function setCategory( event ) {
4
        const selected = event.target.querySelector( 'option:checked' );
5
        setAttributes( { category: selected.value } );
6
        event.preventDefault();
7
    }
8
    return (
9
        <div className={ props.className }>
10
            Current category is: {category}
11
            <form onSubmit={ setCategory }>
12
                <select value={ category } onChange={ setCategory }>
13
                    <option value="animals">Animals</option>

14
                    <option value="arch">Architecture</option>

15
                    <option value="nature">Nature</option>

16
                    <option value="people">People</option>

17
                    <option value="tech">Tech</option>

18
                </select>

19
            </form>

20
        </div>

21
    );
22
}

Вот как это будет выглядеть:

Adding a block select drop down controlAdding a block select drop down controlAdding a block select drop down control

Это сильно отличается от предыдущей статической функции edit, поэтому давайте рассмотрим изменения.

Сначала мы добавили раскрывающийся список выбора с несколькими вариантами, соответствующими категориям изображений, доступным на сайте PlaceIMG.

PlaceIMG image categoriesPlaceIMG image categoriesPlaceIMG image categories

Когда значение раскрывающегося списка изменяется, вызывается функция setCategory, которая находит текущую выбранную категорию, а затем, в свою очередь, вызывает функцию setAttributes. Это основная функция Гутенберга, которая корректно обновляет значение атрибута. Рекомендуется обновлять атрибут только с помощью этой функции.

Теперь, когда выбирается новая категория, значение атрибута обновляется и передается обратно в функцию edit, которая обновляет выдаваемое сообщение.

Image category updatedImage category updatedImage category updated

Мы должны выполнить последний шаг, чтобы получить случайное изображение для отображения. Нам нужно создать простой компонент, который будет принимать текущую категорию и выводить тег <img> со случайным изображением, полученным с сайта PlaceIMG.

Запрос, который мы должны сделать для PlaceIMG, имеет вид: https://placeimg.com/[width]/[height]/[category]

Мы пока сохраним ширину и высоту, поэтому нам нужно только добавить текущую категорию в конец URL-адреса запроса. Например, если категория 'nature', то полный URL-адрес запроса будет: https://placeimg.com/320/220/nature.

Добавьте компонент RandomImage перед registerBlockType():

1
function RandomImage( { category } ) {
2
    const src = 'https://placeimg.com/320/220/' + category;

3
    return <img src={ src } alt={ category } />;
4
}

Чтобы использовать его, просто добавьте его в функцию edit и удалите статическое выходное сообщение. Пока мы на этом, сделаем то же самое для функции save.

Полный файл index.js теперь должен выглядеть так:

1
//  Import CSS.

2
import './style.scss';
3
import './editor.scss';
4
5
const { __ } = wp.i18n;
6
const { registerBlockType, query } = wp.blocks;
7
8
function RandomImage( { category } ) {
9
    const src = 'https://placeimg.com/320/220/' + category;
10
    return <img src={ src } alt={ category } />;

11
}
12
13
registerBlockType( 'cgb/block-random-image', {
14
    title: __( 'Random Image' ),
15
    icon: 'format-image',
16
    category: 'common',
17
    keywords: [
18
        __( 'random' ),
19
        __( 'image' )
20
    ],
21
    attributes: {
22
        category: {
23
            type: 'string',
24
            default: 'nature'
25
        }
26
    },
27
    edit: function( props ) {
28
        const { attributes: { category }, setAttributes } = props;
29
        function setCategory( event ) {
30
            const selected = event.target.querySelector( 'option:checked' );
31
            setAttributes( { category: selected.value } );
32
            event.preventDefault();
33
        }
34
35
        return (
36
            <div className={ props.className }>
37
                <RandomImage category={ category } />

38
                <form onSubmit={ setCategory }>
39
                    <select value={ category } onChange={ setCategory }>
40
                        <option value="animals">Animals</option>

41
                        <option value="arch">Architecture</option>

42
                        <option value="nature">Nature</option>

43
                        <option value="people">People</option>

44
                        <option value="tech">Tech</option>

45
                    </select>

46
                </form>

47
            </div>

48
        );
49
    },
50
    save: function( props ) {
51
        const { attributes: { category } } = props;
52
        return (
53
            <div>
54
                <RandomImage category={ category } />

55
            </div>

56
        );
57
    }
58
} );

Наконец (пока) добавьте следующие стили в editor.scss, чтобы добавить цветную рамку вокруг изображения.

1
.wp-block-cgb-block-random-image  {
2
    select {
3
        padding: 2px;
4
        margin: 7px 0 5px 2px;
5
        border-radius: 3px;
6
    }
7
}

Вам также понадобятся некоторые стили в style.css.

1
 .wp-block-cgb-block-random-image  {
2
    background: #f3e88e;
3
    color: #fff;
4
    border: 2px solid #ead9a6;
5
    border-radius: 10px;
6
    padding: 5px;
7
    width: -webkit-fit-content;
8
    width: -moz-fit-content;
9
    width: fit-content;
10
11
    img {
12
        border-radius: inherit;
13
        border: 1px dotted #caac69;
14
        display: grid;
15
    }
16
}

Всякий раз, когда страница обновляется в редакторе или на внешнем интерфейсе, отображается новое случайное изображение.

Finished editor viewFinished editor viewFinished editor view
Final front end viewFinal front end viewFinal front end view

Если вы видите одно и то же изображение, отображаемое снова и снова, вы можете выполнить жесткое обновление, чтобы предотвратить отображение изображения из кэша браузера.

Заключение

В этом уроке мы рассмотрели довольно много вопросов. Если вы прошли через все это, то заслуживаете похвалы за терпение и труды! Разработка блоков Gutenberg может быть очень увлекательной, но непросто понять каждую концепцию с первого взгляда.

Попутно мы узнали, как ставить в очередь блочные сценарии и стили, и подробно рассмотрели функцию registerBlockType().

Вслед за этим мы применили теорию на практике и создали собственный блок с нуля для отображения случайного изображения из определенной категории с помощью веб-службы PlaceIMG.

В следующей и последней части этого урока мы добавим больше функций в наш блок случайных изображений через панель настроек в инспекторе блоков.

Если вы следовали этому руководству и просто хотите поэкспериментировать с кодом, не вводя ничего, вы сможете загрузить последний плагин my-custom-block в следующем руководстве.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.