: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
type: 'START_MULTI_SELECT'
* Action that stops block multi-selection.
* @return {Object} Action object.
function stopMultiSelect() {
type: 'STOP_MULTI_SELECT'
* Action that changes block multi-selection.
* @param {string} start First block of the multi selection.
* @param {string} end Last block of the multiselection.
* @param {number|null} __experimentalInitialPosition Optional initial position. Pass as null to skip focus within editor canvas.
const multiSelect = (start, end, __experimentalInitialPosition = 0) => ({
const startBlockRootClientId = select.getBlockRootClientId(start);
const endBlockRootClientId = select.getBlockRootClientId(end);
// Only allow block multi-selections at the same level.
if (startBlockRootClientId !== endBlockRootClientId) {
initialPosition: __experimentalInitialPosition
const blockCount = select.getSelectedBlockCount();
(0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: number of selected blocks */
(0,external_wp_i18n_namespaceObject._n)('%s block selected.', '%s blocks selected.', blockCount), blockCount), 'assertive');
* Action that clears the block selection.
* @return {Object} Action object.
function clearSelectedBlock() {
type: 'CLEAR_SELECTED_BLOCK'
* Action that enables or disables block selection.
* @param {boolean} [isSelectionEnabled=true] Whether block selection should
* @return {Object} Action object.
function toggleSelection(isSelectionEnabled = true) {
type: 'TOGGLE_SELECTION',
/* eslint-disable jsdoc/valid-types */
* Action that replaces given blocks with one or more replacement blocks.
* @param {(string|string[])} clientIds Block client ID(s) to replace.
* @param {(Object|Object[])} blocks Replacement block(s).
* @param {number} indexToSelect Index of replacement block to select.
* @param {0|-1|null} initialPosition Index of caret after in the selected block after the operation.
* @param {?Object} meta Optional Meta values to be passed to the action object.
* @return {Object} Action object.
const replaceBlocks = (clientIds, blocks, indexToSelect, initialPosition = 0, meta) => ({
/* eslint-enable jsdoc/valid-types */
clientIds = actions_castArray(clientIds);
blocks = actions_castArray(blocks);
const rootClientId = select.getBlockRootClientId(clientIds[0]);
// Replace is valid if the new blocks can be inserted in the root block.
for (let index = 0; index < blocks.length; index++) {
const block = blocks[index];
const canInsertBlock = select.canInsertBlockType(block.name, rootClientId);
// We're batching these two actions because an extra `undo/redo` step can
// be created, based on whether we insert a default block or not.
// To avoid a focus loss when removing the last block, assure there is
// always a default block if the last of the blocks have been removed.
dispatch.ensureDefaultBlock();
* Action that replaces a single block with one or more replacement blocks.
* @param {(string|string[])} clientId Block client ID to replace.
* @param {(Object|Object[])} block Replacement block(s).
* @return {Object} Action object.
function replaceBlock(clientId, block) {
return replaceBlocks(clientId, block);
* Higher-order action creator which, given the action type to dispatch creates
* an action creator for managing block movement.
* @param {string} type Action type to dispatch.
* @return {Function} Action creator.
const createOnMove = type => (clientIds, rootClientId) => ({
// If one of the blocks is locked or the parent is locked, we cannot move any block.
const canMoveBlocks = select.canMoveBlocks(clientIds);
clientIds: actions_castArray(clientIds),
const moveBlocksDown = createOnMove('MOVE_BLOCKS_DOWN');
const moveBlocksUp = createOnMove('MOVE_BLOCKS_UP');
* Action that moves given blocks to a new position.
* @param {?string} clientIds The client IDs of the blocks.
* @param {?string} fromRootClientId Root client ID source.
* @param {?string} toRootClientId Root client ID destination.
* @param {number} index The index to move the blocks to.
const moveBlocksToPosition = (clientIds, fromRootClientId = '', toRootClientId = '', index) => ({
const canMoveBlocks = select.canMoveBlocks(clientIds);
// If one of the blocks is locked or the parent is locked, we cannot move any block.
// If moving inside the same root block the move is always possible.
if (fromRootClientId !== toRootClientId) {
const canRemoveBlocks = select.canRemoveBlocks(clientIds);
// If we're moving to another block, it means we're deleting blocks from
// the original block, so we need to check if removing is possible.
const canInsertBlocks = select.canInsertBlocks(clientIds, toRootClientId);
// If moving to other parent block, the move is possible if we can insert a block of the same type inside the new parent block.
type: 'MOVE_BLOCKS_TO_POSITION',
* Action that moves given block to a new position.
* @param {?string} clientId The client ID of the block.
* @param {?string} fromRootClientId Root client ID source.
* @param {?string} toRootClientId Root client ID destination.
* @param {number} index The index to move the block to.
function moveBlockToPosition(clientId, fromRootClientId = '', toRootClientId = '', index) {
return moveBlocksToPosition([clientId], fromRootClientId, toRootClientId, index);
* Action that inserts a single block, optionally at a specific index respective a root block list.
* Only allowed blocks are inserted. The action may fail silently for blocks that are not allowed or if
* a templateLock is active on the block list.
* @param {Object} block Block object to insert.
* @param {?number} index Index at which block should be inserted.
* @param {?string} rootClientId Optional root client ID of block list on which to insert.
* @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
* @param {?Object} meta Optional Meta values to be passed to the action object.
* @return {Object} Action object.
function insertBlock(block, index, rootClientId, updateSelection, meta) {
return insertBlocks([block], index, rootClientId, updateSelection, 0, meta);
/* eslint-disable jsdoc/valid-types */
* Action that inserts an array of blocks, optionally at a specific index respective a root block list.
* Only allowed blocks are inserted. The action may fail silently for blocks that are not allowed or if
* a templateLock is active on the block list.
* @param {Object[]} blocks Block objects to insert.
* @param {?number} index Index at which block should be inserted.
* @param {?string} rootClientId Optional root client ID of block list on which to insert.
* @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
* @param {0|-1|null} initialPosition Initial focus position. Setting it to null prevent focusing the inserted block.
* @param {?Object} meta Optional Meta values to be passed to the action object.
* @return {Object} Action object.
const insertBlocks = (blocks, index, rootClientId, updateSelection = true, initialPosition = 0, meta) => ({
/* eslint-enable jsdoc/valid-types */
if (initialPosition !== null && typeof initialPosition === 'object') {
external_wp_deprecated_default()("meta argument in wp.data.dispatch('core/block-editor')", {
hint: 'The meta argument is now the 6th argument of the function'
blocks = actions_castArray(blocks);
const allowedBlocks = [];
for (const block of blocks) {
const isValid = select.canInsertBlockType(block.name, rootClientId);
allowedBlocks.push(block);
if (allowedBlocks.length) {
initialPosition: updateSelection ? initialPosition : null,
* Action that shows the insertion point.
* @param {?string} rootClientId Optional root client ID of block list on
* @param {?number} index Index at which block should be inserted.
* @param {?Object} __unstableOptions Additional options.
* @property {boolean} __unstableWithInserter Whether or not to show an inserter button.
* @property {WPDropOperation} operation The operation to perform when applied,
* either 'insert' or 'replace' for now.
* @return {Object} Action object.
function showInsertionPoint(rootClientId, index, __unstableOptions = {}) {
type: 'SHOW_INSERTION_POINT',
* Action that hides the insertion point.
const hideInsertionPoint = () => ({
if (!select.isBlockInsertionPointVisible()) {
type: 'HIDE_INSERTION_POINT'
* Action that resets the template validity.
* @param {boolean} isValid template validity flag.
* @return {Object} Action object.
function setTemplateValidity(isValid) {
type: 'SET_TEMPLATE_VALIDITY',
* Action that synchronizes the template with the list of blocks.
* @return {Object} Action object.
const synchronizeTemplate = () => ({
type: 'SYNCHRONIZE_TEMPLATE'
const blocks = select.getBlocks();
const template = select.getTemplate();
const updatedBlockList = (0,external_wp_blocks_namespaceObject.synchronizeBlocksWithTemplate)(blocks, template);
dispatch.resetBlocks(updatedBlockList);
* Delete the current selection.
* @param {boolean} isForward
const __unstableDeleteSelection = isForward => ({
const selectionAnchor = select.getSelectionStart();
const selectionFocus = select.getSelectionEnd();
if (selectionAnchor.clientId === selectionFocus.clientId) {
// It's not mergeable if there's no rich text selection.
if (!selectionAnchor.attributeKey || !selectionFocus.attributeKey || typeof selectionAnchor.offset === 'undefined' || typeof selectionFocus.offset === 'undefined') {
const anchorRootClientId = select.getBlockRootClientId(selectionAnchor.clientId);
const focusRootClientId = select.getBlockRootClientId(selectionFocus.clientId);
// It's not mergeable if the selection doesn't start and end in the same
// block list. Maybe in the future it should be allowed.
if (anchorRootClientId !== focusRootClientId) {
const blockOrder = select.getBlockOrder(anchorRootClientId);
const anchorIndex = blockOrder.indexOf(selectionAnchor.clientId);
const focusIndex = blockOrder.indexOf(selectionFocus.clientId);
// Reassign selection start and end based on order.
let selectionStart, selectionEnd;
if (anchorIndex > focusIndex) {
selectionStart = selectionFocus;
selectionEnd = selectionAnchor;
selectionStart = selectionAnchor;
selectionEnd = selectionFocus;
const targetSelection = isForward ? selectionEnd : selectionStart;
const targetBlock = select.getBlock(targetSelection.clientId);
const targetBlockType = (0,external_wp_blocks_namespaceObject.getBlockType)(targetBlock.name);
if (!targetBlockType.merge) {
const selectionA = selectionStart;
const selectionB = selectionEnd;
const blockA = select.getBlock(selectionA.clientId);
const blockB = select.getBlock(selectionB.clientId);
const htmlA = blockA.attributes[selectionA.attributeKey];
const htmlB = blockB.attributes[selectionB.attributeKey];
let valueA = (0,external_wp_richText_namespaceObject.create)({
let valueB = (0,external_wp_richText_namespaceObject.create)({
valueA = (0,external_wp_richText_namespaceObject.remove)(valueA, selectionA.offset, valueA.text.length);
valueB = (0,external_wp_richText_namespaceObject.insert)(valueB, START_OF_SELECTED_AREA, 0, selectionB.offset);
// Clone the blocks so we don't manipulate the original.
const cloneA = (0,external_wp_blocks_namespaceObject.cloneBlock)(blockA, {
[selectionA.attributeKey]: (0,external_wp_richText_namespaceObject.toHTMLString)({
const cloneB = (0,external_wp_blocks_namespaceObject.cloneBlock)(blockB, {
[selectionB.attributeKey]: (0,external_wp_richText_namespaceObject.toHTMLString)({
const followingBlock = isForward ? cloneA : cloneB;
// We can only merge blocks with similar types
// thus, we transform the block to merge first
const blocksWithTheSameType = blockA.name === blockB.name ? [followingBlock] : (0,external_wp_blocks_namespaceObject.switchToBlockType)(followingBlock, targetBlockType.name);
// If the block types can not match, do nothing
if (!blocksWithTheSameType || !blocksWithTheSameType.length) {
const blockToMerge = blocksWithTheSameType.pop();
updatedAttributes = targetBlockType.merge(blockToMerge.attributes, cloneB.attributes);
const blockToMerge = blocksWithTheSameType.shift();
updatedAttributes = targetBlockType.merge(cloneA.attributes, blockToMerge.attributes);
const newAttributeKey = retrieveSelectedAttribute(updatedAttributes);
const convertedHtml = updatedAttributes[newAttributeKey];
const convertedValue = (0,external_wp_richText_namespaceObject.create)({
const newOffset = convertedValue.text.indexOf(START_OF_SELECTED_AREA);
const newValue = (0,external_wp_richText_namespaceObject.remove)(convertedValue, newOffset, newOffset + 1);
const newHtml = (0,external_wp_richText_namespaceObject.toHTMLString)({
updatedAttributes[newAttributeKey] = newHtml;
const selectedBlockClientIds = select.getSelectedBlockClientIds();
const replacement = [...(isForward ? blocksWithTheSameType : []), {
// Preserve the original client ID.
...targetBlock.attributes,
}, ...(isForward ? [] : blocksWithTheSameType)];
dispatch.selectionChange(targetBlock.clientId, newAttributeKey, newOffset, newOffset);
dispatch.replaceBlocks(selectedBlockClientIds, replacement, 0,
// If we don't pass the `indexToSelect` it will default to the last block.
select.getSelectedBlocksInitialCaretPosition());
* Split the current selection.
const __unstableSplitSelection = (blocks = []) => ({
const selectionAnchor = select.getSelectionStart();
const selectionFocus = select.getSelectionEnd();
const anchorRootClientId = select.getBlockRootClientId(selectionAnchor.clientId);
const focusRootClientId = select.getBlockRootClientId(selectionFocus.clientId);
// It's not splittable if the selection doesn't start and end in the same
// block list. Maybe in the future it should be allowed.
if (anchorRootClientId !== focusRootClientId) {
const blockOrder = select.getBlockOrder(anchorRootClientId);
const anchorIndex = blockOrder.indexOf(selectionAnchor.clientId);
const focusIndex = blockOrder.indexOf(selectionFocus.clientId);
// Reassign selection start and end based on order.
let selectionStart, selectionEnd;
if (anchorIndex > focusIndex) {