: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @return {?string} Last block client ID in the multi-selection set.
function getLastMultiSelectedBlockClientId(state) {
const selectedClientIds = getMultiSelectedBlockClientIds(state);
return selectedClientIds[selectedClientIds.length - 1] || null;
* Returns true if a multi-selection exists, and the block corresponding to the
* specified client ID is the first block of the multi-selection set, or false
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {boolean} Whether block is first in multi-selection.
function isFirstMultiSelectedBlock(state, clientId) {
return getFirstMultiSelectedBlockClientId(state) === clientId;
* Returns true if the client ID occurs within the block multi-selection, or
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {boolean} Whether block is in multi-selection set.
function isBlockMultiSelected(state, clientId) {
return getMultiSelectedBlockClientIds(state).indexOf(clientId) !== -1;
* Returns true if an ancestor of the block is multi-selected, or false
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {boolean} Whether an ancestor of the block is in multi-selection
const isAncestorMultiSelected = (0,external_wp_data_namespaceObject.createSelector)((state, clientId) => {
let ancestorClientId = clientId;
let isMultiSelected = false;
while (ancestorClientId && !isMultiSelected) {
ancestorClientId = getBlockRootClientId(state, ancestorClientId);
isMultiSelected = isBlockMultiSelected(state, ancestorClientId);
}, state => [state.blocks.order, state.selection.selectionStart.clientId, state.selection.selectionEnd.clientId]);
* Returns the client ID of the block which begins the multi-selection set, or
* null if there is no multi-selection.
* This is not necessarily the first client ID in the selection.
* @see getFirstMultiSelectedBlockClientId
* @param {Object} state Editor state.
* @return {?string} Client ID of block beginning multi-selection.
function getMultiSelectedBlocksStartClientId(state) {
if (selectionStart.clientId === selectionEnd.clientId) {
return selectionStart.clientId || null;
* Returns the client ID of the block which ends the multi-selection set, or
* null if there is no multi-selection.
* This is not necessarily the last client ID in the selection.
* @see getLastMultiSelectedBlockClientId
* @param {Object} state Editor state.
* @return {?string} Client ID of block ending multi-selection.
function getMultiSelectedBlocksEndClientId(state) {
if (selectionStart.clientId === selectionEnd.clientId) {
return selectionEnd.clientId || null;
* Returns true if the selection is not partial.
* @param {Object} state Editor state.
* @return {boolean} Whether the selection is mergeable.
function __unstableIsFullySelected(state) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
return !selectionAnchor.attributeKey && !selectionFocus.attributeKey && typeof selectionAnchor.offset === 'undefined' && typeof selectionFocus.offset === 'undefined';
* Returns true if the selection is collapsed.
* @param {Object} state Editor state.
* @return {boolean} Whether the selection is collapsed.
function __unstableIsSelectionCollapsed(state) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
return !!selectionAnchor && !!selectionFocus && selectionAnchor.clientId === selectionFocus.clientId && selectionAnchor.attributeKey === selectionFocus.attributeKey && selectionAnchor.offset === selectionFocus.offset;
function __unstableSelectionHasUnmergeableBlock(state) {
return getSelectedBlockClientIds(state).some(clientId => {
const blockName = getBlockName(state, clientId);
const blockType = (0,external_wp_blocks_namespaceObject.getBlockType)(blockName);
* Check whether the selection is mergeable.
* @param {Object} state Editor state.
* @param {boolean} isForward Whether to merge forwards.
* @return {boolean} Whether the selection is mergeable.
function __unstableIsSelectionMergeable(state, isForward) {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
// It's not mergeable if the start and end are within the same block.
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 = getBlockRootClientId(state, selectionAnchor.clientId);
const focusRootClientId = getBlockRootClientId(state, 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 = getBlockOrder(state, 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 targetBlockClientId = isForward ? selectionEnd.clientId : selectionStart.clientId;
const blockToMergeClientId = isForward ? selectionStart.clientId : selectionEnd.clientId;
const targetBlockName = getBlockName(state, targetBlockClientId);
const targetBlockType = (0,external_wp_blocks_namespaceObject.getBlockType)(targetBlockName);
if (!targetBlockType.merge) {
const blockToMerge = getBlock(state, blockToMergeClientId);
// It's mergeable if the blocks are of the same type.
if (blockToMerge.name === targetBlockName) {
// If the blocks are of a different type, try to transform the block being
// merged into the same type of block.
const blocksToMerge = (0,external_wp_blocks_namespaceObject.switchToBlockType)(blockToMerge, targetBlockName);
return blocksToMerge && blocksToMerge.length;
* Get partial selected blocks with their content updated
* based on the selection.
* @param {Object} state Editor state.
* @return {Object[]} Updated partial selected blocks.
const __unstableGetSelectedBlocksWithPartialSelection = state => {
const selectionAnchor = getSelectionStart(state);
const selectionFocus = getSelectionEnd(state);
if (selectionAnchor.clientId === selectionFocus.clientId) {
return selectors_EMPTY_ARRAY;
// Can't split if the selection is not set.
if (!selectionAnchor.attributeKey || !selectionFocus.attributeKey || typeof selectionAnchor.offset === 'undefined' || typeof selectionFocus.offset === 'undefined') {
return selectors_EMPTY_ARRAY;
const anchorRootClientId = getBlockRootClientId(state, selectionAnchor.clientId);
const focusRootClientId = getBlockRootClientId(state, 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) {
return selectors_EMPTY_ARRAY;
const blockOrder = getBlockOrder(state, anchorRootClientId);
const anchorIndex = blockOrder.indexOf(selectionAnchor.clientId);
const focusIndex = blockOrder.indexOf(selectionFocus.clientId);
// Reassign selection start and end based on order.
const [selectionStart, selectionEnd] = anchorIndex > focusIndex ? [selectionFocus, selectionAnchor] : [selectionAnchor, selectionFocus];
const blockA = getBlock(state, selectionStart.clientId);
const blockB = getBlock(state, selectionEnd.clientId);
const htmlA = blockA.attributes[selectionStart.attributeKey];
const htmlB = blockB.attributes[selectionEnd.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, 0, selectionStart.offset);
valueB = (0,external_wp_richText_namespaceObject.remove)(valueB, selectionEnd.offset, valueB.text.length);
[selectionStart.attributeKey]: (0,external_wp_richText_namespaceObject.toHTMLString)({
[selectionEnd.attributeKey]: (0,external_wp_richText_namespaceObject.toHTMLString)({
* Returns an array containing all block client IDs in the editor in the order
* they appear. Optionally accepts a root client ID of the block list for which
* the order should be returned, defaulting to the top-level block order.
* @param {Object} state Editor state.
* @param {?string} rootClientId Optional root client ID of block list.
* @return {Array} Ordered client IDs of editor blocks.
function getBlockOrder(state, rootClientId) {
return state.blocks.order.get(rootClientId || '') || selectors_EMPTY_ARRAY;
* Returns the index at which the block corresponding to the specified client
* ID occurs within the block order, or `-1` if the block does not exist.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {number} Index at which block exists in order.
function getBlockIndex(state, clientId) {
const rootClientId = getBlockRootClientId(state, clientId);
return getBlockOrder(state, rootClientId).indexOf(clientId);
* Returns true if the block corresponding to the specified client ID is
* currently selected and no multi-selection exists, or false otherwise.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {boolean} Whether block is selected and multi-selection exists.
function isBlockSelected(state, clientId) {
if (selectionStart.clientId !== selectionEnd.clientId) {
return selectionStart.clientId === clientId;
* Returns true if one of the block's inner blocks is selected.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @param {boolean} deep Perform a deep check.
* @return {boolean} Whether the block has an inner block selected
function hasSelectedInnerBlock(state, clientId, deep = false) {
const selectedBlockClientIds = getSelectedBlockClientIds(state);
if (!selectedBlockClientIds.length) {
return selectedBlockClientIds.some(id =>
// Pass true because we don't care about order and it's more
getBlockParents(state, id, true).includes(clientId));
return selectedBlockClientIds.some(id => getBlockRootClientId(state, id) === clientId);
* Returns true if one of the block's inner blocks is dragged.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @param {boolean} deep Perform a deep check.
* @return {boolean} Whether the block has an inner block dragged
function hasDraggedInnerBlock(state, clientId, deep = false) {
return getBlockOrder(state, clientId).some(innerClientId => isBlockBeingDragged(state, innerClientId) || deep && hasDraggedInnerBlock(state, innerClientId, deep));
* Returns true if the block corresponding to the specified client ID is
* currently selected but isn't the last of the selected blocks. Here "last"
* refers to the block sequence in the document, _not_ the sequence of
* multi-selection, which is why `state.selectionEnd` isn't used.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {boolean} Whether block is selected and not the last in the
function isBlockWithinSelection(state, clientId) {
const clientIds = getMultiSelectedBlockClientIds(state);
const index = clientIds.indexOf(clientId);
return index > -1 && index < clientIds.length - 1;
* Returns true if a multi-selection has been made, or false otherwise.
* @param {Object} state Editor state.
* @return {boolean} Whether multi-selection has been made.
function hasMultiSelection(state) {
return selectionStart.clientId !== selectionEnd.clientId;
* Whether in the process of multi-selecting or not. This flag is only true
* while the multi-selection is being selected (by mouse move), and is false
* once the multi-selection has been settled.
* @param {Object} state Global application state.
* @return {boolean} True if multi-selecting, false if not.
function selectors_isMultiSelecting(state) {
return state.isMultiSelecting;
* Selector that returns if multi-selection is enabled or not.
* @param {Object} state Global application state.
* @return {boolean} True if it should be possible to multi-select blocks, false if multi-selection is disabled.
function selectors_isSelectionEnabled(state) {
return state.isSelectionEnabled;
* Returns the block's editing mode, defaulting to "visual" if not explicitly
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @return {Object} Block editing mode.
function getBlockMode(state, clientId) {
return state.blocksMode[clientId] || 'visual';
* Returns true if the user is typing, or false otherwise.
* @param {Object} state Global application state.
* @return {boolean} Whether user is typing.
function selectors_isTyping(state) {
* Returns true if the user is dragging blocks, or false otherwise.
* @param {Object} state Global application state.
* @return {boolean} Whether user is dragging blocks.
function isDraggingBlocks(state) {
return !!state.draggedBlocks.length;
* Returns the client ids of any blocks being directly dragged.
* This does not include children of a parent being dragged.
* @param {Object} state Global application state.
* @return {string[]} Array of dragged block client ids.
function getDraggedBlockClientIds(state) {
return state.draggedBlocks;
* Returns whether the block is being dragged.
* Only returns true if the block is being directly dragged,
* not if the block is a child of a parent being dragged.
* See `isAncestorBeingDragged` for child blocks.
* @param {Object} state Global application state.
* @param {string} clientId Client id for block to check.
* @return {boolean} Whether the block is being dragged.
function isBlockBeingDragged(state, clientId) {
return state.draggedBlocks.includes(clientId);
* Returns whether a parent/ancestor of the block is being dragged.
* @param {Object} state Global application state.
* @param {string} clientId Client id for block to check.
* @return {boolean} Whether the block's ancestor is being dragged.
function isAncestorBeingDragged(state, clientId) {
// Return early if no blocks are being dragged rather than
// the more expensive check for parents.
if (!isDraggingBlocks(state)) {
const parents = getBlockParents(state, clientId);
return parents.some(parentClientId => isBlockBeingDragged(state, parentClientId));
* Returns true if the caret is within formatted text, or false otherwise.
* @return {boolean} Whether the caret is within formatted text.
function isCaretWithinFormattedText() {
external_wp_deprecated_default()('wp.data.select( "core/block-editor" ).isCaretWithinFormattedText', {