Реализация Gutenberg RichText onSplit / onReplace

Вопрос или проблема

Мне нужен неупорядоченный список, но с опциями, выбранными для каждого элемента списка. Для этого я создал пользовательский блок под названием icon-list, который представляет собой элемент ul с InnerBlocks, который позволяет только моему пользовательскому блоку icon-list-item быть дочерним. Мой блок icon-list-item — это просто элемент RichText с tagName: li и некоторыми управляющими элементами. Все это работает хорошо, но в настоящее время нажатие клавиши Enter добавляет разрыв строки в текущий блок icon-list-item — я хотел бы, чтобы нажатие клавиши Enter вставляло новый блок icon-list-item. Аналогично, нажатие клавиши Backspace в начале блока icon-list-item должно удалять этот блок — точная такая же функциональность, как в блоке core/list.

Я покопался в документации и исходниках Gutenberg и обнаружил, что мне нужно реализовать свойства onSplit и onReplace для моего элемента RichText, но мне не ясно, как именно это сделать. На данный момент у меня есть:

el(RichText, {
    tagName     : 'li',
    className   : 'icon--' + props.attributes.icon,
    value       : props.attributes.content,
    placeholder : props.attributes.placeholder,
    onChange    : function(value) {
        props.setAttributes({ content: value })
    },
    onSplit     : function(value) {
        if (!value) {
            return createBlock('tutti/icon-list-item');
        }

        return createBlock('tutti/icon-list-item', {
            ...props.attributes,
            content: value,
        }
    },
    onReplace   : function() {
        // что-то
    }
})

… очевидно, что я абсолютно потерян, когда речь идет о функции onReplace. Любые подсказки будут очень appreciated!

Не тестировалось. Я просто просматривал, ища что-то другое. Но я бы попытался сделать что-то подобное:

onReplace: function() {
   var myID    = props.clientId;
   wp.data.dispatch('core/block-editor').removeBlock(myID);
}

Вот как я реализовал элемент, похожий на список, который ведет себя как нативный элемент списка. Я сделал это из-за уведомления о снятии с поддержки в компоненте RichText. Свойство multiline, которое можно было использовать, чтобы заставить такое поведение работать, будет удалено в версии 6.3, и рекомендуется использовать InnerBlocks вместо этого.

const blockSettings = {
  /**
    * @param attributes Объект атрибутов блока A
    * @param attributesToMerge Объект атрибутов блока B
    *
    * @returns {} Объект атрибутов после объединения
    */
  merge: (attributes, attributesToMerge) => {
    return {
      content: (attributes.content || "") +
               (attributesToMerge.content || ""),
    }
  },
  edit: (props) => {
    const blockProps = useBlockProps({
      // Определите свойства блога
    })

    return el(RichText, {
      ...blockProps, // Свойства блока не нужны для этого примера
      identifier: "content", // Это нужно, чтобы предотвратить сообщение об ошибке
      onSplit: (value, isOriginal) => {
        let newAttributes = {}

        if (isOriginal || value) {
          newAttributes = {
            ...props.attributes,
            content: value,
          }
        }

        const block = createBlock("custom/my-list--item", newAttributes)

        // Установите clientId первого элемента в оригинальный clientId, чтобы сохранить его
        if (isOriginal) {
          block.clientId = props.clientId
        }

        return block
      },
      // Функции, предоставленные в blockProps с помощью useBlockProps()
      onReplace: props.onReplace,
      onMerge: props.mergeBlocks,
      onRemove: props.onRemove,
    })
  }
}

Код заимствован из собственного блока core/paragraph Gutenberg (/packages/block-library/src/paragraph).

Объект props, который вы получаете из функции useBlockProps, импортируемой из wp.editor в вашей функции edit, уже содержит функции для onReplace, onRemove и mergeBlocks для события onMerge.
Источник: /packages/block-library/src/paragraph/edit.js строка 152

Кроме функции edit, вам нужно объявить функцию merge в настройках вашего блока. Она получит атрибуты обоих блоков и вернет атрибуты объединенного блока. Если вы не объявите merge, объединение завершится неудачей без сообщения об ошибке.
Источник: /packages/block-library/src/paragraph/index.js строка 43

RichText также нуждается в свойстве identifier. Без него возникает ошибка.
Источник: /packages/block-library/src/paragraph/edit.js строка 127

Функция onSplit устанавливает clientId оригинального элемента списка в значение props.clientId, поскольку onSplit заменяет оригинальный блок и поэтому генерирует новый id.

// Псевдокод
originalBlockA = {
  clientId: "A",
}

const { blockA, blockB } = onSplit(originalBlockA)

blockA.clientId == "B"
blockB.clientId == "C"

Источник: /packages/block-library/src/paragraph/edit.js строка 134

Сам функционал в некоторой степени задокументирован в README.md, но ключевая часть, свойство identifier и функция merge отсутствуют.

Необходимость функции merge можно найти в хранилище данных core/block-editor: /packages/block-editor/src/store/actions.js строка 1025

Необходимость свойства identifier более или менее задокументирована в /packages/block-editor/src/store/selectors.js. Оно используется в /packages/block-editor/src/store/actions.js на строке 1057, и attributeKey проверяется на строке 1073.

Ответ или решение

Внедрение Gutenberg RichText: onSplit и onReplace для пользовательского блока иконок

Создание пользовательских блоков в редакторе Gutenberg предоставляет огромные возможности для настройки контента, и одной из задач может быть создание элементов списка с функциями, аналогичными ядру блока списка. В вашем случае это требование распространяется на блок icon-list, который включает icon-list-item, представленный в виде элемента <li>. Рассмотрим, как реализовать функциональности onSplit и onReplace для эффективного управления вводом и удалением блоков.

Структура вашего блока

Ваш icon-list представляет собой ненумерованный список, содержащий вложенные блоки icon-list-item. Блок icon-list-item является элементом RichText и позволяет вводить текст отступами как элемент списка. Важно установить поведение, которое позволяет:

  1. Создавать новый элемент списка при нажатии клавиши Enter.
  2. Удалять текущий элемент списка, когда курсор находится в начале блока и нажата клавиша Backspace.

Давайте разберем, как достичь этого с помощью методов onSplit и onReplace.

Реализация onSplit и onReplace

el(RichText, {
    tagName: 'li',
    className: 'icon--' + props.attributes.icon,
    value: props.attributes.content,
    placeholder: props.attributes.placeholder,
    onChange: function(value) {
        props.setAttributes({ content: value });
    },
    onSplit: function(value, isOriginal) {
        // Если значение не указано, создаем новый блок для элемента списка
        const newAttributes = isOriginal ?
            { ...props.attributes, content: value } :
            { ...props.attributes, content: '' };

        const newBlock = createBlock('tutti/icon-list-item', newAttributes);

        // Если это оригинальный блок, устанавливаем идентификатор
        if (isOriginal) {
            newBlock.clientId = props.clientId;
        }

        return newBlock;
    },
    onReplace: function() {
        // Удаляем текущий блок при нажатии клавиши Backspace на пустом элементе списка
        const myID = props.clientId; 
        wp.data.dispatch('core/block-editor').removeBlock(myID);
    }
});

Подробности реализации

  1. onSplit:

    • Этот метод вызывается, когда пользователь нажимает клавишу Enter. Он позволяет разбить текущий блок на два, создавая новый элемент списка.
    • Если isOriginal истинно, это означает, что мы уже находимся в оригинальном блоке, и мы можем передать текущее содержимое в новый блок. В противном случае мы просто создаем новый блок с пустым содержимым.
    • Необходимо сохранить clientId оригинального блока в новом блоке, чтобы связать их при объединении.
  2. onReplace:

    • Этот метод вызывается, когда пользователь нажимает Backspace в начале блока. Он отвечает за удаление блока.
    • Мы получаем clientId текущего блока и запускаем действие для его удаления через dispatch.

Дополнительные рекомендации

  • Убедитесь, что вы также обработали функцию merge, если планируете объединять элементы списка.
  • Поддерживайте соответствие свойств блока с помощью создания функции merge для обработки слияния атрибутов между блоками.

Заключение

Эта реализация позволяет вам создать интерактивный и удобный пользовательский блок icon-list, который реагирует на действия пользователя аналогично стандартным блокам, таким как core/list. Убедитесь, что вы протестировали функциональность вашего блока на различных сценариях использования для достижения наилучшего пользовательского опыта.

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

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

Капча загружается...