: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Update the selection if the original selection has been removed.
const shouldUpdateSelection = selectedBlockClientIds.length > 0 && getSelectedBlockClientIds().length === 0;
// If there's no previous block nor parent block, focus the first block.
blockToFocus = getBlockOrder()[0];
updateFocusAndSelection(blockToFocus, shouldUpdateSelection);
} else if (isMatch('core/block-editor/duplicate', event)) {
const canDuplicate = getBlocksByClientId(blocksToUpdate).every(blockToUpdate => {
return !!blockToUpdate && (0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockToUpdate.name, 'multiple', true) && canInsertBlockType(blockToUpdate.name, firstBlockRootClientId);
const updatedBlocks = await duplicateBlocks(blocksToUpdate, false);
if (updatedBlocks?.length) {
// If blocks have been duplicated, focus the first duplicated block.
updateFocusAndSelection(updatedBlocks[0], false);
} else if (isMatch('core/block-editor/insert-before', event)) {
await insertBeforeBlock(blocksToUpdate[0]);
const newlySelectedBlocks = getSelectedBlockClientIds();
// Focus the first block of the newly inserted blocks, to keep focus within the list view.
setOpenedBlockSettingsMenu(undefined);
updateFocusAndSelection(newlySelectedBlocks[0], false);
} else if (isMatch('core/block-editor/insert-after', event)) {
await insertAfterBlock(blocksToUpdate.at(-1));
const newlySelectedBlocks = getSelectedBlockClientIds();
// Focus the first block of the newly inserted blocks, to keep focus within the list view.
setOpenedBlockSettingsMenu(undefined);
updateFocusAndSelection(newlySelectedBlocks[0], false);
} else if (isMatch('core/block-editor/select-all', event)) {
const blockClientIds = getBlockOrder(firstBlockRootClientId);
if (!blockClientIds.length) {
// If we have selected all sibling nested blocks, try selecting up a level.
// This is a similar implementation to that used by `useSelectAll`.
// `isShallowEqual` is used for the list view instead of a length check,
// as the array of siblings of the currently focused block may be a different
// set of blocks from the current block selection if the user is focused
// on a different part of the list view from the block selection.
if (external_wp_isShallowEqual_default()(selectedBlockClientIds, blockClientIds)) {
// Only select up a level if the first block is not the root block.
// This ensures that the block selection can't break out of the root block
// used by the list view, if the list view is only showing a partial hierarchy.
if (firstBlockRootClientId && firstBlockRootClientId !== rootClientId) {
updateFocusAndSelection(firstBlockRootClientId, true);
// Select all while passing `null` to skip focusing to the editor canvas,
// and retain focus within the list view.
multiSelect(blockClientIds[0], blockClientIds[blockClientIds.length - 1], null);
} else if (isMatch('core/block-editor/collapse-list-view', event)) {
const blockParents = getBlockParents(firstBlockClientId, false);
// Expand all parents of the current block.
} else if (isMatch('core/block-editor/group', event)) {
if (blocksToUpdate.length > 1 && isGroupable(blocksToUpdate)) {
const blocks = getBlocksByClientId(blocksToUpdate);
const groupingBlockName = getGroupingBlockName();
const newBlocks = (0,external_wp_blocks_namespaceObject.switchToBlockType)(blocks, groupingBlockName);
replaceBlocks(blocksToUpdate, newBlocks);
(0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.__)('Selected blocks are grouped.'));
const newlySelectedBlocks = getSelectedBlockClientIds();
// Focus the first block of the newly inserted blocks, to keep focus within the list view.
setOpenedBlockSettingsMenu(undefined);
updateFocusAndSelection(newlySelectedBlocks[0], false);
const onMouseEnter = (0,external_wp_element_namespaceObject.useCallback)(() => {
toggleBlockHighlight(clientId, true);
}, [clientId, setIsHovered, toggleBlockHighlight]);
const onMouseLeave = (0,external_wp_element_namespaceObject.useCallback)(() => {
toggleBlockHighlight(clientId, false);
}, [clientId, setIsHovered, toggleBlockHighlight]);
const selectEditorBlock = (0,external_wp_element_namespaceObject.useCallback)(event => {
selectBlock(event, clientId);
}, [clientId, selectBlock]);
const updateFocusAndSelection = (0,external_wp_element_namespaceObject.useCallback)((focusClientId, shouldSelectBlock) => {
selectBlock(undefined, focusClientId, null, null);
focusListItem(focusClientId, treeGridElementRef?.current);
}, [selectBlock, treeGridElementRef]);
const toggleExpanded = (0,external_wp_element_namespaceObject.useCallback)(event => {
// Prevent shift+click from opening link in a new window when toggling.
if (isExpanded === true) {
} else if (isExpanded === false) {
}, [clientId, expand, collapse, isExpanded]);
// Allow right-clicking an item in the List View to open up the block settings dropdown.
const onContextMenu = (0,external_wp_element_namespaceObject.useCallback)(event => {
if (showBlockActions && allowRightClickOverrides) {
settingsRef.current?.click();
// Ensure the position of the settings dropdown is at the cursor.
setSettingsAnchorRect(new window.DOMRect(event.clientX, event.clientY, 0, 0));
}, [allowRightClickOverrides, settingsRef, showBlockActions]);
const onMouseDown = (0,external_wp_element_namespaceObject.useCallback)(event => {
// Prevent right-click from focusing the block,
// because focus will be handled when opening the block settings dropdown.
if (allowRightClickOverrides && event.button === 2) {
}, [allowRightClickOverrides]);
const settingsPopoverAnchor = (0,external_wp_element_namespaceObject.useMemo)(() => {
} = rowRef?.current || {};
// If no custom position is set, the settings dropdown will be anchored to the
// DropdownMenu toggle button.
if (!settingsAnchorRect || !ownerDocument) {
// Position the settings dropdown at the cursor when right-clicking a block.
getBoundingClientRect() {
return settingsAnchorRect;
}, [settingsAnchorRect]);
const clearSettingsAnchorRect = (0,external_wp_element_namespaceObject.useCallback)(() => {
// Clear the custom position for the settings dropdown so that it is restored back
// to being anchored to the DropdownMenu toggle button.
setSettingsAnchorRect(undefined);
}, [setSettingsAnchorRect]);
// Pass in a ref to the row, so that it can be scrolled
// into view when selected. For long lists, the placeholder for the
// selected block is also observed, within ListViewLeafPlaceholder.
useListViewScrollIntoView({
// When switching between rendering modes (such as template preview and content only),
// it is possible for a block to temporarily be unavailable. In this case, we should not
// render the leaf, to avoid errors further down the tree.
const blockPositionDescription = getBlockPositionDescription(position, siblingBlockCount, level);
const blockPropertiesDescription = getBlockPropertiesDescription(isLocked);
const hasSiblings = siblingBlockCount > 0;
const hasRenderedMovers = showBlockMovers && hasSiblings;
const moverCellClassName = dist_clsx('block-editor-list-view-block__mover-cell', {
'is-visible': isHovered || isSelected
const listViewBlockSettingsClassName = dist_clsx('block-editor-list-view-block__menu-cell', {
'is-visible': isHovered || isFirstSelectedBlock
} else if (!showBlockActions) {
const classes = dist_clsx({
'is-selected': isSelected,
'is-first-selected': isFirstSelectedBlock,
'is-last-selected': isLastSelectedBlock,
'is-branch-selected': isBranchSelected,
'is-synced-branch': isSyncedBranch,
'is-dragging': isDragged,
'has-single-cell': !showBlockActions,
'is-synced': blockInformation?.isSynced,
'is-displacement-normal': displacement === 'normal',
'is-displacement-up': displacement === 'up',
'is-displacement-down': displacement === 'down',
'is-after-dragged-blocks': isAfterDraggedBlocks,
// Only include all selected blocks if the currently clicked on block
// is one of the selected blocks. This ensures that if a user attempts
// to alter a block that isn't part of the selection, they're still able
const dropdownClientIds = selectedClientIds.includes(clientId) ? selectedClientIds : [clientId];
// Detect if there is a block in the canvas currently being edited and multi-selection is not happening.
const currentlyEditingBlockInCanvas = isSelected && selectedClientIds.length === 1;
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(leaf, {
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
id: `list-view-${listViewInstanceId}-block-${clientId}`,
"data-expanded": canEdit ? isExpanded : undefined,
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.__experimentalTreeGridCell, {
className: "block-editor-list-view-block__contents-cell",
"aria-selected": !!isSelected,
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)("div", {
className: "block-editor-list-view-block__contents-container",
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(block_contents, {
onClick: selectEditorBlock,
onContextMenu: onContextMenu,
onMouseDown: onMouseDown,
onToggleExpanded: toggleExpanded,
siblingBlockCount: siblingBlockCount,
tabIndex: currentlyEditingBlockInCanvas ? 0 : tabIndex,
isExpanded: canEdit ? isExpanded : undefined,
selectedClientIds: selectedClientIds,
ariaDescribedBy: descriptionId
}), /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(AriaReferencedText, {
children: `${blockPositionDescription} ${blockPropertiesDescription}`
}), hasRenderedMovers && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_ReactJSXRuntime_namespaceObject.Fragment, {
children: /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_wp_components_namespaceObject.__experimentalTreeGridCell, {
className: moverCellClassName,
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.__experimentalTreeGridItem, {
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(BlockMoverUpButton, {
}), /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.__experimentalTreeGridItem, {
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(BlockMoverDownButton, {
}), showBlockActions && BlockSettingsMenu && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.__experimentalTreeGridCell, {
className: listViewBlockSettingsClassName,
"aria-selected": !!isSelected,
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(BlockSettingsMenu, {
clientIds: dropdownClientIds,
label: (0,external_wp_i18n_namespaceObject.__)('Options'),
anchor: settingsPopoverAnchor // Used to position the settings at the cursor on right-click.
className: 'block-editor-list-view-block__menu',
onClick: clearSettingsAnchorRect,
disableOpenOnArrowDown: true,
expandedState: expandedState,
setInsertedBlock: setInsertedBlock,
__experimentalSelectBlock: updateFocusAndSelection
/* harmony default export */ const list_view_block = ((0,external_wp_element_namespaceObject.memo)(ListViewBlock));
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/list-view/branch.js
* Given a block, returns the total number of blocks in that subtree. This is used to help determine
* the list position of a block.
* When a block is collapsed, we do not count their children as part of that total. In the current drag
* implementation dragged blocks and their children are not counted.
* @param {Object} block block tree
* @param {Object} expandedState state that notes which branches are collapsed
* @param {Array} draggedClientIds a list of dragged client ids
* @param {boolean} isExpandedByDefault flag to determine the default fallback expanded state.
* @return {number} block count
function countBlocks(block, expandedState, draggedClientIds, isExpandedByDefault) {
var _expandedState$block$;
const isDragged = draggedClientIds?.includes(block.clientId);
const isExpanded = (_expandedState$block$ = expandedState[block.clientId]) !== null && _expandedState$block$ !== void 0 ? _expandedState$block$ : isExpandedByDefault;
return 1 + block.innerBlocks.reduce(countReducer(expandedState, draggedClientIds, isExpandedByDefault), 0);
const countReducer = (expandedState, draggedClientIds, isExpandedByDefault) => (count, block) => {
var _expandedState$block$2;
const isDragged = draggedClientIds?.includes(block.clientId);
const isExpanded = (_expandedState$block$2 = expandedState[block.clientId]) !== null && _expandedState$block$2 !== void 0 ? _expandedState$block$2 : isExpandedByDefault;
if (isExpanded && block.innerBlocks.length > 0) {
return count + countBlocks(block, expandedState, draggedClientIds, isExpandedByDefault);
const branch_noop = () => {};
function ListViewBranch(props) {
selectBlock = branch_noop,
isBranchSelected = false,
shouldShowInnerBlocks = true,
showAppender: showAppenderProp = true
const parentBlockInformation = useBlockDisplayInformation(parentId);
const syncedBranch = isSyncedBranch || !!parentBlockInformation?.isSynced;
const canParentExpand = (0,external_wp_data_namespaceObject.useSelect)(select => {
return select(store).canEditBlock(parentId);
} = useListViewContext();
// Only show the appender at the first level.
const showAppender = showAppenderProp && level === 1;
const filteredBlocks = blocks.filter(Boolean);
const blockCount = filteredBlocks.length;
// The appender means an extra row in List View, so add 1 to the row count.
const rowCount = showAppender ? blockCount + 1 : blockCount;
let nextPosition = listPosition;
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_ReactJSXRuntime_namespaceObject.Fragment, {
children: [filteredBlocks.map((block, index) => {
var _expandedState$client;
nextPosition += countBlocks(filteredBlocks[index - 1], expandedState, draggedClientIds, isExpanded);
const isDragged = !!draggedClientIds?.includes(clientId);
// Determine the displacement of the block while dragging. This
// works out whether the current block should be displaced up or
// down, relative to the dragged blocks and the drop target.
} = getDragDisplacementValues({
const blockInView = itemInView(nextPosition);
const position = index + 1;
const updatedPath = path.length > 0 ? `${path}_${position}` : `${position}`;
const hasNestedBlocks = !!innerBlocks?.length;
const shouldExpand = hasNestedBlocks && shouldShowInnerBlocks ? (_expandedState$client = expandedState[clientId]) !== null && _expandedState$client !== void 0 ? _expandedState$client : isExpanded : undefined;
// Make updates to the selected or dragged blocks synchronous,
// but asynchronous for any other block.
const isSelected = isClientIdSelected(clientId, selectedClientIds);
const isSelectedBranch = isBranchSelected || isSelected && hasNestedBlocks;
// To avoid performance issues, we only render blocks that are in view,
// or blocks that are selected or dragged. If a block is selected,
// it is only counted if it is the first of the block selection.
// This prevents the entire tree from being rendered when a branch is
// selected, or a user selects all blocks, while still enabling scroll
// into view behavior when selecting a block or opening the list view.
// The first and last blocks of the list are always rendered, to ensure
// that Home and End keys work as expected.
const showBlock = isDragged || blockInView || isSelected && clientId === selectedClientIds[0] || index === 0 || index === blockCount - 1;
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_wp_data_namespaceObject.AsyncModeProvider, {
children: [showBlock && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(list_view_block, {