Изучаем Gutenberg — 7 часть — создаем блок карточки

У нас есть базовые знания, мы поиграли с React, и теперь у нас есть настроенные инструменты проекта. Давайте приступим к созданию нашего пользовательского блока.

 Что мы создаем

Мы собираемся создать пользовательский блок карточки с изображением, заголовком и подписью. Это распространенный шаблон проектирования в Интернете, и он также позволяет нам взглянуть на некоторые основные компоненты Gutenberg, а также основные элементы WordPress, такие как Медиа библиотека. Мы также поиграем с некоторой логикой отображения с JSX для внешней разметки.

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

Начало работы

Первое, что мы собираемся сделать, это открыть block.jsфайл, который мы создали в предыдущем разделе. В активной папке плагинов он находится по адресу blocks/src/block/block.js.Если вы работаете с файловой структурой, созданной с помощью create-guten-block, вы можете начать с удаления всего содержимого block.jsи написания кода с нуля.

Справа вверху этого файла добавьте следующее:

const { RichText, MediaUpload, PlainText } = wp.editor;
const { registerBlockType } = wp.blocks;
const { Button } = wp.components;

Мы рассмотрели деструктуризацию в части 4.

Давайте импортируем наши файлы Sass.

import './style.scss';
import './editor.scss';

Теперь давайте начнем писать компонент для нашего блока. Прямо под этими строками добавьте следующее:

registerBlockType('card-block/main', {   
  title: 'Card',
  icon: 'heart',
  category: 'common'
});

Этот код говорит Gutenberg: «У меня есть блок, который вы можете добавить в свою коллекцию. Он называется« Карточка », у него есть значок« сердце », и он должен находиться в категории« общие »». Это базовое определение нашего компонента, поэтому давайте добавим еще немного кода.

Вы также можете узнать функцию registerBlockType из функции PHP register_block_type, которую мы использовали в прошлой статье. В то время как эта функция регистрирует блок на стороне сервера WordPress, эта функция регистрирует наш блок в экосистеме React на стороне клиента. И та, и другая необходимы, чтобы иметь блок, использующий React, и их зарегистрированные имена card-block/mainдолжны совпадать.

Добавьте следующий код, но убедитесь , что вы поставить запятую после 'common', так это выглядит следующим образом : 'common',.

Вот код:

attributes: {
  title: {
    source: 'text',
    selector: '.card__title'
  },
  body: {
    type: 'array',
    source: 'children',
    selector: '.card__body'
  },
  imageAlt: {
    attribute: 'alt',
    selector: '.card__image'
  },
  imageUrl: {
    attribute: 'src',
    selector: '.card__image'
  }
}

Здесь мы определяем редактируемые атрибуты нашего блока и селектор DOM, которым они назначены. Этот объект attribute работает очень похоже на объект state React . У него даже есть очень похожий метод обновления setAttributes

Об атрибутах и ​​State

Он может выглядеть как простой объект JavaScript, но этот кусок attributesвводит целый ряд новых концепций в темы WordPress, не в последнюю очередь это State

В Gutenberg атрибуты отслеживают текущее состояние данных в блоке. Атрибуты являются ближайшей параллелью, которую мы можем провести к пользовательским полям в Gutenberg, но они существуют только в контексте Gutenberg и JavaScript. Давайте возьмем атрибут title, например:

title: {
  source: 'text',
  selector: 'card__title'
},

Когда Gutenberg запускается, он говорит: «Мне нужно найти какой-то текст внутри вызываемого селектора.card__title и заполнить значение тем title, что я найду».

Атрибуты в Gutenberg не связаны напрямую с базой данных, как с пользовательскими полями post_meta. Записи source и selector это инструкции для Gutenberg, чтобы заполнить состояние каждого блока. Когда мы загружаем редактор, он следует этим инструкциям и присваивает значениеtitleна основе разметки, сохраненной в базе данных между комментариями HTML, которые указывают блок этого типа. Мы не видим значения titleв attributes, который мы регистрируем, но если бы мы получили доступ к props.attributes.title, мы бы получили любой text, который есть в .card__title.

Добавим функцию редактирования

Добавьте следующее после закрытия } объекта attributes. Как и раньше, обязательно добавьте запятую, чтобы она выглядела следующим образом },.

Добавьте следующий код после этого:

edit({ attributes, className, setAttributes }) {
  return (
  );
}

Итак, мы используем назначение деструктурирования, чтобы выбрать переданные параметры для функции редактирования. Двумя наиболее важными являются attributesи setAttributes.
Параметр  attributes такой же, как блок attributes, но в текущем, реактивном состоянии. Это означает, что если функция setAttributes обновляет одно из значений attributes, она будет автоматически обновляться в любом месте, которое ссылается на него, что аналогично нашему компоненту React из части 4.

В этой функции есть return. Мы собираемся вставить туда немного JSX. Добавьте в скобки return следующее:

<div className="container">
  <MediaUpload
    onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }
    type="image"
    value={ attributes.imageID }
    render={ ({ open }) => getImageButton(open) }
  />
  <PlainText
    onChange={ content => setAttributes({ title: content }) }
    value={ attributes.title }
    placeholder="Your card title"
    className="heading"
  />
  <RichText
    onChange={ content => setAttributes({ body: content }) }
    value={ attributes.body }
    multiline="p"
    placeholder="Your card text"
  />
</div>

Хорошо, здесь много чего происходит, но это все, что мы рассмотрели в предыдущих частях этой серии. У нас есть контейнер с тремя существующими компонентами Gutenberg. Для каждого мы устанавливаем релевантное attributeзначение: его значение, соответствующий заполнитель и onChange/onSelectобработчик. Мы также <MediaUpload />передаем пользовательское средство рендеринга , о котором мы вскоре расскажем.

Каждый обработчик onChange представляет собой маленькое выражение, которое проходит новое содержание, которое инициировало onChangeв функции setAttributes, где мы устанавливаем , какие attributes обновлять. Это обновление затем распространяется на любую ссылку на этот атрибут, где содержимое будет обновляться как по волшебству. Элемент <MediaUpload /> предвтсавляет событие onSelect, которое вызывается, когда пользователь выбирает или загружает элемент в медиа — библиотеке.

Есть собственный атрибут render, который ссылается на функцию getImageButton. Давайте напишем это. Над returnв функции edit добавьте следующее:

const getImageButton = (openEvent) => {
  if(attributes.imageUrl) {
    return (
      <img 
        src={ attributes.imageUrl }
        onClick={ openEvent }
        className="image"
      />
    );
  }
  else {
    return (
      <div className="button-container">
        <Button 
          onClick={ openEvent }
          className="button button-large"
        >
          Pick an image
        </Button>
      </div>
    );
  }
};

Что эта функция делает это обнаруживает, есть ли imageUrlв объекте attributes. Если он есть, он отобразит этот тег <img /> и позволит пользователю кликнуть по нему, чтобы выбрать другой. Если изображения нет, он будет отображать WordPress <Button />который предлагает пользователю выбрать изображение. Она вызывает то же самое openEvent, что было передано в функцию.

Добавим функцию сохранения

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

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

save({ attributes }) {

  const cardImage = (src, alt) => {
    if(!src) return null;

    if(alt) {
      return (
        <img 
          className="card__image" 
          src={ src }
          alt={ alt }
        /> 
      );
    }
    
    // No alt set, so let's hide it from screen readers
    return (
      <img 
        className="card__image" 
        src={ src }
        alt=""
        aria-hidden="true"
      /> 
    );
  };
  
  return (
    <div className="card">
      { cardImage(attributes.imageUrl, attributes.imageAlt) }
      <div className="card__content">
        <h3 className="card__title">{ attributes.title }</h3>
        <div className="card__body">
          { attributes.body }
        </div>
      </div>
    </div>
  );
}

Выглядит очень похоже на функцию edit.

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

Затем у нас есть другая вспомогательная функция изображения, которая сначала определяет, есть ли изображение, и возвращает его, и возвращает nullесли его нет. Помните: мы возвращаем null в JSX, если хотим, чтобы он ничего не отображал. Следующая вещь, которую делает этот помощник, это визуализация слегка измененного тега <img />, если есть альтернативный текст или нет. Для последнего он скрывает его от программы чтения с экрана, добавляя aria-hidden="true"и устанавливая пустой атрибут alt.

И, наконец, наш returnвозвращает блок .card с чистой, управляемой БЭМ разметкой, которая будет загружаться в начало нашей темы.

Добавим стиль

Откройте editor.scss, который живет в том же каталоге, что и block.js. Добавьте следующее:

@import '../common';

.gutenberg { // This may need to be .wp-block in WordPress 5+
    
  .container {
    border: 1px solid $gray;
    padding: 1rem;
  }

  .button-container {
    text-align: center;
    padding: 22% 0;
    background: $off-white;
    border: 1px solid $gray;
    border-radius: 2px;
    margin: 0 0 1.2rem 0;
  }

  .heading {
    font-size: 1.5rem;
    font-weight: 600;
  }

  .image {
    height: 15.7rem;
    width: 100%;
    object-fit: cover;
  }
}

Это немного CSS, чтобы придать нашему блоку карточный стиль. Все это вложено в класс .gutenberg. Это для борьбы со спецификой некоторых основных стилей. Внутри редактора есть обертка <div class="gutenberg"области редактора блока, поэтому мы можем быть уверены, что затрагиваем только те элементы этой вложенности. Вы, вероятно, также заметите, что мы импортируем другой файл Sass, так что давайте заполним его.

/*
 * Common SCSS can contain your common variables, helpers and mixins
 * that are shared between all of your blocks. 
 */

// Colors
$gray: #cccccc;
$off-white: #f1f1f1;

Вот как должен выглядеть весь файл block.js:

const { RichText, MediaUpload, PlainText } = wp.editor;
const { registerBlockType } = wp.blocks;
const { Button } = wp.components;

// Import our CSS files
import './style.scss';
import './editor.scss';

registerBlockType('card-block/main', {   
  title: 'Card',
  icon: 'heart',
  category: 'common',
  attributes: {
    title: {
      source: 'text',
      selector: '.card__title'
    },
    body: {
      type: 'array',
      source: 'children',
      selector: '.card__body'
    },
    imageAlt: {
      attribute: 'alt',
      selector: '.card__image'
    },
    imageUrl: {
      attribute: 'src',
      selector: '.card__image'
    }
  },
  edit({ attributes, className, setAttributes }) {

    const getImageButton = (openEvent) => {
      if(attributes.imageUrl) {
        return (
          <img 
            src={ attributes.imageUrl }
            onClick={ openEvent }
            className="image"
          />
        );
      }
      else {
        return (
          <div className="button-container">
            <Button 
              onClick={ openEvent }
              className="button button-large"
            >
              Pick an image
            </Button>
          </div>
        );
      }
    };

    return (
      <div className="container">
        <MediaUpload
          onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }
          type="image"
          value={ attributes.imageID }
          render={ ({ open }) => getImageButton(open) }
        />
        <PlainText
          onChange={ content => setAttributes({ title: content }) }
          value={ attributes.title }
          placeholder="Your card title"
          className="heading"
        />
        <RichText
          onChange={ content => setAttributes({ body: content }) }
          value={ attributes.body }
          multiline="p"
          placeholder="Your card text"
          formattingControls={ ['bold', 'italic', 'underline'] }
          isSelected={ attributes.isSelected }
        />
      </div>
    );

  },

  save({ attributes }) {

    const cardImage = (src, alt) => {
      if(!src) return null;

      if(alt) {
        return (
          <img 
            className="card__image" 
            src={ src }
            alt={ alt }
          /> 
        );
      }
      
      // No alt set, so let's hide it from screen readers
      return (
        <img 
          className="card__image" 
          src={ src }
          alt=""
          aria-hidden="true"
        /> 
      );
    };
    
    return (
      <div className="card">
        { cardImage(attributes.imageUrl, attributes.imageAlt) }
        <div className="card__content">
          <h3 className="card__title">{ attributes.title }</h3>
          <div className="card__body">
            { attributes.body }
          </div>
        </div>
      </div>
    );
  }
});

Запустим webpack. Находясь в текущей директории плагинов в терминале, запустите это:

npx webpack --watch

Это немного отличается от предыдущей части серии, потому что мы добавили --watchаргумент. Он в основном следит за вашими jsфайлами и перезапускает webpackесли они меняются.

Откроем редактор

Давайте загрузим редактор Gutenberg, загрузив сообщение в бэкэнд WordPress. В редакторе Gutenberg, нажмите на маленький значок плюса и посмотрите на вкладку «блоки», и вот он, наш новый блок карточки:

Добавьте туда немного контента. 

Вот короткое видео:

Давайте перечислим то, что вы узнали в этой серии:

  • Вы изучили некоторые основы современного JavaScript, освоив ES6
  • Вы узнали основы JSX
  • Вы взяли эти новые знания и создали компонент React .
  • Затем вы узнали, как работает webpack, и написали масштабируемый конфигурационный файл для разработки блоков Gutenberg.
  • Наконец, вы создали пользовательский блок карточки Gutenberg.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Scroll to top