: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
function useSelectAll() {
getSelectedBlockClientIds,
} = (0,external_wp_data_namespaceObject.useSelect)(store);
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
const isMatch = (0,external_wp_keyboardShortcuts_namespaceObject.__unstableUseShortcutEventMatch)();
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
function onKeyDown(event) {
if (!isMatch('core/block-editor/select-all', event)) {
const selectedClientIds = getSelectedBlockClientIds();
if (selectedClientIds.length < 2 && !(0,external_wp_dom_namespaceObject.isEntirelySelected)(event.target)) {
const [firstSelectedClientId] = selectedClientIds;
const rootClientId = getBlockRootClientId(firstSelectedClientId);
const blockClientIds = getBlockOrder(rootClientId);
// If we have selected all sibling nested blocks, try selecting up a
// level. See: https://github.com/WordPress/gutenberg/pull/31859/
if (selectedClientIds.length === blockClientIds.length) {
node.ownerDocument.defaultView.getSelection().removeAllRanges();
selectBlock(rootClientId);
multiSelect(blockClientIds[0], blockClientIds[blockClientIds.length - 1]);
node.addEventListener('keydown', onKeyDown);
node.removeEventListener('keydown', onKeyDown);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/use-drag-selection.js
* Sets the `contenteditable` wrapper element to `value`.
* @param {HTMLElement} node Block element.
* @param {boolean} value `contentEditable` value (true or false)
function setContentEditableWrapper(node, value) {
node.contentEditable = value;
// Firefox doesn't automatically move focus.
* Sets a multi-selection based on the native selection across blocks.
function useDragSelection() {
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
} = (0,external_wp_data_namespaceObject.useSelect)(store);
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
// Equivalent to attaching the listener once.
defaultView.removeEventListener('mouseup', onMouseUp);
// The browser selection won't have updated yet at this point,
// so wait until the next animation frame to get the browser
rafId = defaultView.requestAnimationFrame(() => {
if (!hasSelectedBlock()) {
// If the selection is complete (on mouse up), and no
// multiple blocks have been selected, set focus back to the
// anchor element. if the anchor element contains the
// selection. Additionally, the contentEditable wrapper can
// now be disabled again.
setContentEditableWrapper(node, false);
const selection = defaultView.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
const clonedRange = range.cloneRange();
if (anchorElement.contains(commonAncestorContainer)) {
selection.removeAllRanges();
selection.addRange(clonedRange);
// If we're moving into a child element, ignore. We're tracking
// the mouse leaving the element to a parent, no a child.
if (target.contains(relatedTarget)) {
// Avoid triggering a multi-selection if the user is already
if (isDraggingBlocks()) {
// The primary button must be pressed to initiate selection.
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
// Abort if we are already multi-selecting.
if (isMultiSelecting()) {
// Abort if selection is leaving writing flow.
// Check the attribute, not the contentEditable attribute. All
// child elements of the content editable wrapper are editable
// and return true for this property. We only want to start
// multi selecting when the mouse leaves the wrapper.
if (target.getAttribute('contenteditable') !== 'true') {
if (!isSelectionEnabled()) {
// Do not rely on the active element because it may change after
// the mouse leaves for the first time. See
// https://github.com/WordPress/gutenberg/issues/48747.
// `onSelectionStart` is called after `mousedown` and
// `mouseleave` (from a block). The selection ends when
// `mouseup` happens anywhere in the window.
defaultView.addEventListener('mouseup', onMouseUp);
// Allow cross contentEditable selection by temporarily making
// all content editable. We can't rely on using the store and
// React because re-rending happens too slowly. We need to be
// able to select across instances immediately.
setContentEditableWrapper(node, true);
node.addEventListener('mouseout', onMouseLeave);
node.removeEventListener('mouseout', onMouseLeave);
defaultView.removeEventListener('mouseup', onMouseUp);
defaultView.cancelAnimationFrame(rafId);
}, [startMultiSelect, stopMultiSelect, isSelectionEnabled, hasSelectedBlock]);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/use-selection-observer.js
* Extract the selection start node from the selection. When the anchor node is
* not a text node, the selection offset is the index of a child node.
* @param {Selection} selection The selection.
* @return {Element} The selection start node.
function extractSelectionStartNode(selection) {
if (anchorNode.nodeType === anchorNode.TEXT_NODE) {
if (anchorOffset === 0) {
return anchorNode.childNodes[anchorOffset - 1];
* Extract the selection end node from the selection. When the focus node is not
* a text node, the selection offset is the index of a child node. The selection
* reaches up to but excluding that child node.
* @param {Selection} selection The selection.
* @return {Element} The selection start node.
function extractSelectionEndNode(selection) {
if (focusNode.nodeType === focusNode.TEXT_NODE) {
if (focusOffset === focusNode.childNodes.length) {
return focusNode.childNodes[focusOffset];
function findDepth(a, b) {
while (a[depth] === b[depth]) {
* Sets the `contenteditable` wrapper element to `value`.
* @param {HTMLElement} node Block element.
* @param {boolean} value `contentEditable` value (true or false)
function use_selection_observer_setContentEditableWrapper(node, value) {
// Since we are calling this on every selection change, check if the value
// needs to be updated first because it trigger the browser to recalculate
if (node.contentEditable !== String(value)) {
node.contentEditable = value;
// Firefox doesn't automatically move focus.
function getRichTextElement(node) {
const element = node.nodeType === node.ELEMENT_NODE ? node : node.parentElement;
return element?.closest('[data-wp-block-attribute-key]');
* Sets a multi-selection based on the native selection across blocks.
function useSelectionObserver() {
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
} = (0,external_wp_data_namespaceObject.useSelect)(store);
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
function onSelectionChange(event) {
const selection = defaultView.getSelection();
if (!selection.rangeCount) {
const startNode = extractSelectionStartNode(selection);
const endNode = extractSelectionEndNode(selection);
if (!node.contains(startNode) || !node.contains(endNode)) {
// If selection is collapsed and we haven't used `shift+click`,
// end multi selection and disable the contentEditable wrapper.
// We have to check about `shift+click` case because elements
// that don't support text selection might be involved, and we might
// update the clientIds to multi-select blocks.
// For now we check if the event is a `mouse` event.
const isClickShift = event.shiftKey && event.type === 'mouseup';
if (selection.isCollapsed && !isClickShift) {
if (node.contentEditable === 'true' && !isMultiSelecting()) {
use_selection_observer_setContentEditableWrapper(node, false);
let element = startNode.nodeType === startNode.ELEMENT_NODE ? startNode : startNode.parentElement;
element = element?.closest('[contenteditable]');
let startClientId = getBlockClientId(startNode);
let endClientId = getBlockClientId(endNode);
// If the selection has changed and we had pressed `shift+click`,
// we need to check if in an element that doesn't support
// text selection has been clicked.
const selectedClientId = getBlockSelectionStart();
const clickedClientId = getBlockClientId(event.target);
// `endClientId` is not defined if we end the selection by clicking a non-selectable block.
// We need to check if there was already a selection with a non-selectable focusNode.
const focusNodeIsNonSelectable = clickedClientId !== endClientId;
if (startClientId === endClientId && selection.isCollapsed || !endClientId || focusNodeIsNonSelectable) {
endClientId = clickedClientId;
// Handle the case when we have a non-selectable block
// selected and click another one.
if (startClientId !== selectedClientId) {
startClientId = selectedClientId;
// If the selection did not involve a block, return.
if (startClientId === undefined && endClientId === undefined) {
use_selection_observer_setContentEditableWrapper(node, false);
const isSingularSelection = startClientId === endClientId;
if (isSingularSelection) {
if (!isMultiSelecting()) {
selectBlock(startClientId);
multiSelect(startClientId, startClientId);
const startPath = [...getBlockParents(startClientId), startClientId];
const endPath = [...getBlockParents(endClientId), endClientId];
const depth = findDepth(startPath, endPath);
if (startPath[depth] !== startClientId || endPath[depth] !== endClientId) {
multiSelect(startPath[depth], endPath[depth]);
const richTextElementStart = getRichTextElement(startNode);
const richTextElementEnd = getRichTextElement(endNode);
if (richTextElementStart && richTextElementEnd) {
var _richTextDataStart$st, _richTextDataEnd$star;
const range = selection.getRangeAt(0);
const richTextDataStart = (0,external_wp_richText_namespaceObject.create)({
element: richTextElementStart,
__unstableIsEditableTree: true
const richTextDataEnd = (0,external_wp_richText_namespaceObject.create)({
element: richTextElementEnd,
__unstableIsEditableTree: true
const startOffset = (_richTextDataStart$st = richTextDataStart.start) !== null && _richTextDataStart$st !== void 0 ? _richTextDataStart$st : richTextDataStart.end;
const endOffset = (_richTextDataEnd$star = richTextDataEnd.start) !== null && _richTextDataEnd$star !== void 0 ? _richTextDataEnd$star : richTextDataEnd.end;
attributeKey: richTextElementStart.dataset.wpBlockAttributeKey,
attributeKey: richTextElementEnd.dataset.wpBlockAttributeKey,
multiSelect(startClientId, endClientId);
ownerDocument.addEventListener('selectionchange', onSelectionChange);
defaultView.addEventListener('mouseup', onSelectionChange);
ownerDocument.removeEventListener('selectionchange', onSelectionChange);
defaultView.removeEventListener('mouseup', onSelectionChange);
}, [multiSelect, selectBlock, selectionChange, getBlockParents]);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/use-click-selection.js
function useClickSelection() {
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
} = (0,external_wp_data_namespaceObject.useSelect)(store);
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
function onMouseDown(event) {
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
if (!isSelectionEnabled() || event.button !== 0) {
const startClientId = getBlockSelectionStart();
const clickedClientId = getBlockClientId(event.target);
if (startClientId !== clickedClientId) {
node.contentEditable = true;
// Firefox doesn't automatically move focus.
} else if (hasMultiSelection()) {
// Allow user to escape out of a multi-selection to a
// singular selection of a block via click. This is handled
// here since focus handling excludes blocks when there is
// multiselection, as focus can be incurred by starting a
// multiselection (focus moved to first block's multi-
selectBlock(clickedClientId);
node.addEventListener('mousedown', onMouseDown);
node.removeEventListener('mousedown', onMouseDown);
}, [selectBlock, isSelectionEnabled, getBlockSelectionStart, hasMultiSelection]);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/use-input.js
* Handles input for selections across blocks.