Вопрос или проблема
Мне нужен неупорядоченный список, но с опциями, выбранными для каждого элемента списка. Для этого я создал пользовательский блок под названием 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 и позволяет вводить текст отступами как элемент списка. Важно установить поведение, которое позволяет:
- Создавать новый элемент списка при нажатии клавиши Enter.
- Удалять текущий элемент списка, когда курсор находится в начале блока и нажата клавиша 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);
}
});
Подробности реализации
-
onSplit:
- Этот метод вызывается, когда пользователь нажимает клавишу Enter. Он позволяет разбить текущий блок на два, создавая новый элемент списка.
- Если
isOriginal
истинно, это означает, что мы уже находимся в оригинальном блоке, и мы можем передать текущее содержимое в новый блок. В противном случае мы просто создаем новый блок с пустым содержимым. - Необходимо сохранить
clientId
оригинального блока в новом блоке, чтобы связать их при объединении.
-
onReplace:
- Этот метод вызывается, когда пользователь нажимает Backspace в начале блока. Он отвечает за удаление блока.
- Мы получаем
clientId
текущего блока и запускаем действие для его удаления черезdispatch
.
Дополнительные рекомендации
- Убедитесь, что вы также обработали функцию
merge
, если планируете объединять элементы списка. - Поддерживайте соответствие свойств блока с помощью создания функции merge для обработки слияния атрибутов между блоками.
Заключение
Эта реализация позволяет вам создать интерактивный и удобный пользовательский блок icon-list
, который реагирует на действия пользователя аналогично стандартным блокам, таким как core/list
. Убедитесь, что вы протестировали функциональность вашего блока на различных сценариях использования для достижения наилучшего пользовательского опыта.
Вполне вероятно, что вы захотите продолжить исследовать документацию Gutenberg и примеры существующих блоков, таких как core/paragraph
, чтобы получить больше идей для улучшения функциональности вашего блока.