From 1710facd57ad89595c6b0dedc5f30cd0f4e694d3 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Wed, 4 Oct 2023 17:42:43 +0300 Subject: [PATCH] Writing flow: absorb clipboard handler (#55006) --- packages/block-editor/CHANGELOG.md | 2 + packages/block-editor/README.md | 6 +- .../src/components/block-actions/index.js | 2 +- .../src/components/block-canvas/index.js | 8 +- .../src/components/copy-handler/README.md | 2 + .../src/components/copy-handler/index.js | 312 ++---------------- packages/block-editor/src/components/index.js | 2 +- .../src/components/writing-flow/index.js | 2 + .../writing-flow/use-clipboard-handler.js | 242 ++++++++++++++ .../block-editor/src/utils/use-notify-copy.js | 63 ++++ .../index.js | 7 +- 11 files changed, 341 insertions(+), 307 deletions(-) create mode 100644 packages/block-editor/src/components/writing-flow/use-clipboard-handler.js create mode 100644 packages/block-editor/src/utils/use-notify-copy.js diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index cba698b8689c2..40ff46fca50b3 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Deprecated `CopyHandler`, absorbed into `WritingFlow`. + ## 12.10.0 (2023-09-20) - The Deprecated multiline prop on RichText will now fall back to using multiple diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 837a8c35aacfa..063a96384708c 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -334,9 +334,11 @@ _Related_ ### CopyHandler -_Related_ +> **Deprecated** -- +_Parameters_ + +- _props_ `Object`: ### createCustomColorsHOC diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js index b672bee63fc5b..5d942d2b25c70 100644 --- a/packages/block-editor/src/components/block-actions/index.js +++ b/packages/block-editor/src/components/block-actions/index.js @@ -11,7 +11,7 @@ import { /** * Internal dependencies */ -import { useNotifyCopy } from '../copy-handler'; +import { useNotifyCopy } from '../../utils/use-notify-copy'; import usePasteStyles from '../use-paste-styles'; import { store as blockEditorStore } from '../../store'; diff --git a/packages/block-editor/src/components/block-canvas/index.js b/packages/block-editor/src/components/block-canvas/index.js index 435655de3cc26..97aec461df7d8 100644 --- a/packages/block-editor/src/components/block-canvas/index.js +++ b/packages/block-editor/src/components/block-canvas/index.js @@ -11,7 +11,6 @@ import EditorStyles from '../editor-styles'; import Iframe from '../iframe'; import WritingFlow from '../writing-flow'; import { useMouseMoveTypingReset } from '../observe-typing'; -import { useClipboardHandler } from '../copy-handler'; import { useBlockSelectionClearer } from '../block-selection-clearer'; export function ExperimentalBlockCanvas( { @@ -23,13 +22,8 @@ export function ExperimentalBlockCanvas( { iframeProps, } ) { const resetTypingRef = useMouseMoveTypingReset(); - const copyHandler = useClipboardHandler(); const clearerRef = useBlockSelectionClearer(); - const contentRef = useMergeRefs( [ - copyHandler, - contentRefProp, - clearerRef, - ] ); + const contentRef = useMergeRefs( [ contentRefProp, clearerRef ] ); if ( ! shouldIframe ) { return ( diff --git a/packages/block-editor/src/components/copy-handler/README.md b/packages/block-editor/src/components/copy-handler/README.md index 089d1ce0b73f1..b70c841ce0ea5 100644 --- a/packages/block-editor/src/components/copy-handler/README.md +++ b/packages/block-editor/src/components/copy-handler/README.md @@ -1,5 +1,7 @@ # Copy Handler +**Deprecated** + The `CopyHandler` component handles the copy/cut and paste events of its children blocks. It handles multiple selection of blocks as well. Concretely, it handles the display of success messages and takes care of copying the block to the clipboard. It uses for that the [serialize function](https://github.com/WordPress/gutenberg/blob/HEAD/packages/blocks/src/api/serializer.js), which outputs HTML augmented with the HTML-comment demarcations to denote blocks. diff --git a/packages/block-editor/src/components/copy-handler/index.js b/packages/block-editor/src/components/copy-handler/index.js index c6d41cf0be3fe..0b0318c387fab 100644 --- a/packages/block-editor/src/components/copy-handler/index.js +++ b/packages/block-editor/src/components/copy-handler/index.js @@ -1,304 +1,34 @@ /** * WordPress dependencies */ -import { useCallback } from '@wordpress/element'; -import { - serialize, - pasteHandler, - store as blocksStore, - createBlock, - findTransform, - getBlockTransforms, -} from '@wordpress/blocks'; -import { - documentHasSelection, - documentHasUncollapsedSelection, - __unstableStripHTML as stripHTML, -} from '@wordpress/dom'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { __, _n, sprintf } from '@wordpress/i18n'; -import { store as noticesStore } from '@wordpress/notices'; -import { useRefEffect } from '@wordpress/compose'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies */ -import { getPasteEventData } from '../../utils/pasting'; -import { store as blockEditorStore } from '../../store'; - -export function useNotifyCopy() { - const { getBlockName } = useSelect( blockEditorStore ); - const { getBlockType } = useSelect( blocksStore ); - const { createSuccessNotice } = useDispatch( noticesStore ); - - return useCallback( ( eventType, selectedBlockClientIds ) => { - let notice = ''; - if ( selectedBlockClientIds.length === 1 ) { - const clientId = selectedBlockClientIds[ 0 ]; - const title = getBlockType( getBlockName( clientId ) )?.title; - notice = - eventType === 'copy' - ? sprintf( - // Translators: Name of the block being copied, e.g. "Paragraph". - __( 'Copied "%s" to clipboard.' ), - title - ) - : sprintf( - // Translators: Name of the block being cut, e.g. "Paragraph". - __( 'Moved "%s" to clipboard.' ), - title - ); - } else { - notice = - eventType === 'copy' - ? sprintf( - // Translators: %d: Number of blocks being copied. - _n( - 'Copied %d block to clipboard.', - 'Copied %d blocks to clipboard.', - selectedBlockClientIds.length - ), - selectedBlockClientIds.length - ) - : sprintf( - // Translators: %d: Number of blocks being cut. - _n( - 'Moved %d block to clipboard.', - 'Moved %d blocks to clipboard.', - selectedBlockClientIds.length - ), - selectedBlockClientIds.length - ); - } - createSuccessNotice( notice, { - type: 'snackbar', - } ); - }, [] ); -} - -export function useClipboardHandler() { - const { - getBlocksByClientId, - getSelectedBlockClientIds, - hasMultiSelection, - getSettings, - __unstableIsFullySelected, - __unstableIsSelectionCollapsed, - __unstableIsSelectionMergeable, - __unstableGetSelectedBlocksWithPartialSelection, - canInsertBlockType, - } = useSelect( blockEditorStore ); - const { - flashBlock, - removeBlocks, - replaceBlocks, - __unstableDeleteSelection, - __unstableExpandSelection, - insertBlocks, - } = useDispatch( blockEditorStore ); - const notifyCopy = useNotifyCopy(); - - return useRefEffect( ( node ) => { - function handler( event ) { - if ( event.defaultPrevented ) { - // This was likely already handled in rich-text/use-paste-handler.js. - return; - } - - const selectedBlockClientIds = getSelectedBlockClientIds(); - - if ( selectedBlockClientIds.length === 0 ) { - return; - } - - // Always handle multiple selected blocks. - if ( ! hasMultiSelection() ) { - const { target } = event; - const { ownerDocument } = target; - // If copying, only consider actual text selection as selection. - // Otherwise, any focus on an input field is considered. - const hasSelection = - event.type === 'copy' || event.type === 'cut' - ? documentHasUncollapsedSelection( ownerDocument ) - : documentHasSelection( ownerDocument ); - - // Let native copy behaviour take over in input fields. - if ( hasSelection ) { - return; - } - } - - if ( ! node.contains( event.target.ownerDocument.activeElement ) ) { - return; - } - - event.preventDefault(); - - const isSelectionMergeable = __unstableIsSelectionMergeable(); - const shouldHandleWholeBlocks = - __unstableIsSelectionCollapsed() || __unstableIsFullySelected(); - const expandSelectionIsNeeded = - ! shouldHandleWholeBlocks && ! isSelectionMergeable; - if ( event.type === 'copy' || event.type === 'cut' ) { - if ( selectedBlockClientIds.length === 1 ) { - flashBlock( selectedBlockClientIds[ 0 ] ); - } - // If we have a partial selection that is not mergeable, just - // expand the selection to the whole blocks. - if ( expandSelectionIsNeeded ) { - __unstableExpandSelection(); - } else { - notifyCopy( event.type, selectedBlockClientIds ); - let blocks; - // Check if we have partial selection. - if ( shouldHandleWholeBlocks ) { - blocks = getBlocksByClientId( selectedBlockClientIds ); - } else { - const [ head, tail ] = - __unstableGetSelectedBlocksWithPartialSelection(); - const inBetweenBlocks = getBlocksByClientId( - selectedBlockClientIds.slice( - 1, - selectedBlockClientIds.length - 1 - ) - ); - blocks = [ head, ...inBetweenBlocks, tail ]; - } - - const wrapperBlockName = event.clipboardData.getData( - '__unstableWrapperBlockName' - ); - - if ( wrapperBlockName ) { - blocks = createBlock( - wrapperBlockName, - JSON.parse( - event.clipboardData.getData( - '__unstableWrapperBlockAttributes' - ) - ), - blocks - ); - } - - const serialized = serialize( blocks ); - - event.clipboardData.setData( - 'text/plain', - toPlainText( serialized ) - ); - event.clipboardData.setData( 'text/html', serialized ); - } - } - - if ( event.type === 'cut' ) { - // We need to also check if at the start we needed to - // expand the selection, as in this point we might have - // programmatically fully selected the blocks above. - if ( shouldHandleWholeBlocks && ! expandSelectionIsNeeded ) { - removeBlocks( selectedBlockClientIds ); - } else { - event.target.ownerDocument.activeElement.contentEditable = false; - __unstableDeleteSelection(); - } - } else if ( event.type === 'paste' ) { - const { - __experimentalCanUserUseUnfilteredHTML: - canUserUseUnfilteredHTML, - } = getSettings(); - const { plainText, html, files } = getPasteEventData( event ); - let blocks = []; - - if ( files.length ) { - const fromTransforms = getBlockTransforms( 'from' ); - blocks = files - .reduce( ( accumulator, file ) => { - const transformation = findTransform( - fromTransforms, - ( transform ) => - transform.type === 'files' && - transform.isMatch( [ file ] ) - ); - if ( transformation ) { - accumulator.push( - transformation.transform( [ file ] ) - ); - } - return accumulator; - }, [] ) - .flat(); - } else { - blocks = pasteHandler( { - HTML: html, - plainText, - mode: 'BLOCKS', - canUserUseUnfilteredHTML, - } ); - } - - if ( selectedBlockClientIds.length === 1 ) { - const [ selectedBlockClientId ] = selectedBlockClientIds; - - if ( - blocks.every( ( block ) => - canInsertBlockType( - block.name, - selectedBlockClientId - ) - ) - ) { - insertBlocks( - blocks, - undefined, - selectedBlockClientId - ); - return; - } - } - - replaceBlocks( - selectedBlockClientIds, - blocks, - blocks.length - 1, - -1 - ); - } - } - - node.ownerDocument.addEventListener( 'copy', handler ); - node.ownerDocument.addEventListener( 'cut', handler ); - node.ownerDocument.addEventListener( 'paste', handler ); - - return () => { - node.ownerDocument.removeEventListener( 'copy', handler ); - node.ownerDocument.removeEventListener( 'cut', handler ); - node.ownerDocument.removeEventListener( 'paste', handler ); - }; - }, [] ); -} - -function CopyHandler( { children } ) { - return
{ children }
; -} +import useClipboardHandler from '../writing-flow/use-clipboard-handler'; /** - * Given a string of HTML representing serialized blocks, returns the plain - * text extracted after stripping the HTML of any tags and fixing line breaks. - * - * @param {string} html Serialized blocks. - * @return {string} The plain-text content with any html removed. + * @deprecated */ -function toPlainText( html ) { - // Manually handle BR tags as line breaks prior to `stripHTML` call - html = html.replace( /
/g, '\n' ); - - const plainText = stripHTML( html ).trim(); - - // Merge any consecutive line breaks - return plainText.replace( /\n\n+/g, '\n\n' ); -} +export const __unstableUseClipboardHandler = () => { + deprecated( '__unstableUseClipboardHandler', { + alternative: 'BlockCanvas or WritingFlow', + since: '6.4', + version: '6.7', + } ); + return useClipboardHandler(); +}; /** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/copy-handler/README.md + * @deprecated + * @param {Object} props */ -export default CopyHandler; +export default function CopyHandler( props ) { + deprecated( 'CopyHandler', { + alternative: 'BlockCanvas or WritingFlow', + since: '6.4', + version: '6.7', + } ); + return
; +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 6edcd460e31ac..1622cecc150ed 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -128,7 +128,7 @@ export { default as BlockToolbar } from './block-toolbar'; export { default as BlockTools } from './block-tools'; export { default as CopyHandler, - useClipboardHandler as __unstableUseClipboardHandler, + __unstableUseClipboardHandler, } from './copy-handler'; export { default as DefaultBlockAppender } from './default-block-appender'; export { default as __unstableEditorStyles } from './editor-styles'; diff --git a/packages/block-editor/src/components/writing-flow/index.js b/packages/block-editor/src/components/writing-flow/index.js index 03d7e43f5285e..f15c1dac0267f 100644 --- a/packages/block-editor/src/components/writing-flow/index.js +++ b/packages/block-editor/src/components/writing-flow/index.js @@ -22,6 +22,7 @@ import useDragSelection from './use-drag-selection'; import useSelectionObserver from './use-selection-observer'; import useClickSelection from './use-click-selection'; import useInput from './use-input'; +import useClipboardHandler from './use-clipboard-handler'; import { store as blockEditorStore } from '../../store'; export function useWritingFlow() { @@ -35,6 +36,7 @@ export function useWritingFlow() { before, useMergeRefs( [ ref, + useClipboardHandler(), useInput(), useDragSelection(), useSelectionObserver(), diff --git a/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js b/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js new file mode 100644 index 0000000000000..5b78d2f8656b6 --- /dev/null +++ b/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js @@ -0,0 +1,242 @@ +/** + * WordPress dependencies + */ +import { + serialize, + pasteHandler, + createBlock, + findTransform, + getBlockTransforms, +} from '@wordpress/blocks'; +import { + documentHasSelection, + documentHasUncollapsedSelection, + __unstableStripHTML as stripHTML, +} from '@wordpress/dom'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useRefEffect } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { getPasteEventData } from '../../utils/pasting'; +import { store as blockEditorStore } from '../../store'; +import { useNotifyCopy } from '../../utils/use-notify-copy'; + +export default function useClipboardHandler() { + const { + getBlocksByClientId, + getSelectedBlockClientIds, + hasMultiSelection, + getSettings, + __unstableIsFullySelected, + __unstableIsSelectionCollapsed, + __unstableIsSelectionMergeable, + __unstableGetSelectedBlocksWithPartialSelection, + canInsertBlockType, + } = useSelect( blockEditorStore ); + const { + flashBlock, + removeBlocks, + replaceBlocks, + __unstableDeleteSelection, + __unstableExpandSelection, + insertBlocks, + } = useDispatch( blockEditorStore ); + const notifyCopy = useNotifyCopy(); + + return useRefEffect( ( node ) => { + function handler( event ) { + if ( event.defaultPrevented ) { + // This was likely already handled in rich-text/use-paste-handler.js. + return; + } + + const selectedBlockClientIds = getSelectedBlockClientIds(); + + if ( selectedBlockClientIds.length === 0 ) { + return; + } + + // Always handle multiple selected blocks. + if ( ! hasMultiSelection() ) { + const { target } = event; + const { ownerDocument } = target; + // If copying, only consider actual text selection as selection. + // Otherwise, any focus on an input field is considered. + const hasSelection = + event.type === 'copy' || event.type === 'cut' + ? documentHasUncollapsedSelection( ownerDocument ) + : documentHasSelection( ownerDocument ); + + // Let native copy behaviour take over in input fields. + if ( hasSelection ) { + return; + } + } + + if ( ! node.contains( event.target.ownerDocument.activeElement ) ) { + return; + } + + event.preventDefault(); + + const isSelectionMergeable = __unstableIsSelectionMergeable(); + const shouldHandleWholeBlocks = + __unstableIsSelectionCollapsed() || __unstableIsFullySelected(); + const expandSelectionIsNeeded = + ! shouldHandleWholeBlocks && ! isSelectionMergeable; + if ( event.type === 'copy' || event.type === 'cut' ) { + if ( selectedBlockClientIds.length === 1 ) { + flashBlock( selectedBlockClientIds[ 0 ] ); + } + // If we have a partial selection that is not mergeable, just + // expand the selection to the whole blocks. + if ( expandSelectionIsNeeded ) { + __unstableExpandSelection(); + } else { + notifyCopy( event.type, selectedBlockClientIds ); + let blocks; + // Check if we have partial selection. + if ( shouldHandleWholeBlocks ) { + blocks = getBlocksByClientId( selectedBlockClientIds ); + } else { + const [ head, tail ] = + __unstableGetSelectedBlocksWithPartialSelection(); + const inBetweenBlocks = getBlocksByClientId( + selectedBlockClientIds.slice( + 1, + selectedBlockClientIds.length - 1 + ) + ); + blocks = [ head, ...inBetweenBlocks, tail ]; + } + + const wrapperBlockName = event.clipboardData.getData( + '__unstableWrapperBlockName' + ); + + if ( wrapperBlockName ) { + blocks = createBlock( + wrapperBlockName, + JSON.parse( + event.clipboardData.getData( + '__unstableWrapperBlockAttributes' + ) + ), + blocks + ); + } + + const serialized = serialize( blocks ); + + event.clipboardData.setData( + 'text/plain', + toPlainText( serialized ) + ); + event.clipboardData.setData( 'text/html', serialized ); + } + } + + if ( event.type === 'cut' ) { + // We need to also check if at the start we needed to + // expand the selection, as in this point we might have + // programmatically fully selected the blocks above. + if ( shouldHandleWholeBlocks && ! expandSelectionIsNeeded ) { + removeBlocks( selectedBlockClientIds ); + } else { + event.target.ownerDocument.activeElement.contentEditable = false; + __unstableDeleteSelection(); + } + } else if ( event.type === 'paste' ) { + const { + __experimentalCanUserUseUnfilteredHTML: + canUserUseUnfilteredHTML, + } = getSettings(); + const { plainText, html, files } = getPasteEventData( event ); + let blocks = []; + + if ( files.length ) { + const fromTransforms = getBlockTransforms( 'from' ); + blocks = files + .reduce( ( accumulator, file ) => { + const transformation = findTransform( + fromTransforms, + ( transform ) => + transform.type === 'files' && + transform.isMatch( [ file ] ) + ); + if ( transformation ) { + accumulator.push( + transformation.transform( [ file ] ) + ); + } + return accumulator; + }, [] ) + .flat(); + } else { + blocks = pasteHandler( { + HTML: html, + plainText, + mode: 'BLOCKS', + canUserUseUnfilteredHTML, + } ); + } + + if ( selectedBlockClientIds.length === 1 ) { + const [ selectedBlockClientId ] = selectedBlockClientIds; + + if ( + blocks.every( ( block ) => + canInsertBlockType( + block.name, + selectedBlockClientId + ) + ) + ) { + insertBlocks( + blocks, + undefined, + selectedBlockClientId + ); + return; + } + } + + replaceBlocks( + selectedBlockClientIds, + blocks, + blocks.length - 1, + -1 + ); + } + } + + node.ownerDocument.addEventListener( 'copy', handler ); + node.ownerDocument.addEventListener( 'cut', handler ); + node.ownerDocument.addEventListener( 'paste', handler ); + + return () => { + node.ownerDocument.removeEventListener( 'copy', handler ); + node.ownerDocument.removeEventListener( 'cut', handler ); + node.ownerDocument.removeEventListener( 'paste', handler ); + }; + }, [] ); +} + +/** + * Given a string of HTML representing serialized blocks, returns the plain + * text extracted after stripping the HTML of any tags and fixing line breaks. + * + * @param {string} html Serialized blocks. + * @return {string} The plain-text content with any html removed. + */ +function toPlainText( html ) { + // Manually handle BR tags as line breaks prior to `stripHTML` call + html = html.replace( /
/g, '\n' ); + + const plainText = stripHTML( html ).trim(); + + // Merge any consecutive line breaks + return plainText.replace( /\n\n+/g, '\n\n' ); +} diff --git a/packages/block-editor/src/utils/use-notify-copy.js b/packages/block-editor/src/utils/use-notify-copy.js new file mode 100644 index 0000000000000..0f98577f11bf6 --- /dev/null +++ b/packages/block-editor/src/utils/use-notify-copy.js @@ -0,0 +1,63 @@ +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; +import { store as blocksStore } from '@wordpress/blocks'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __, _n, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../store'; + +export function useNotifyCopy() { + const { getBlockName } = useSelect( blockEditorStore ); + const { getBlockType } = useSelect( blocksStore ); + const { createSuccessNotice } = useDispatch( noticesStore ); + + return useCallback( ( eventType, selectedBlockClientIds ) => { + let notice = ''; + if ( selectedBlockClientIds.length === 1 ) { + const clientId = selectedBlockClientIds[ 0 ]; + const title = getBlockType( getBlockName( clientId ) )?.title; + notice = + eventType === 'copy' + ? sprintf( + // Translators: Name of the block being copied, e.g. "Paragraph". + __( 'Copied "%s" to clipboard.' ), + title + ) + : sprintf( + // Translators: Name of the block being cut, e.g. "Paragraph". + __( 'Moved "%s" to clipboard.' ), + title + ); + } else { + notice = + eventType === 'copy' + ? sprintf( + // Translators: %d: Number of blocks being copied. + _n( + 'Copied %d block to clipboard.', + 'Copied %d blocks to clipboard.', + selectedBlockClientIds.length + ), + selectedBlockClientIds.length + ) + : sprintf( + // Translators: %d: Number of blocks being cut. + _n( + 'Moved %d block to clipboard.', + 'Moved %d blocks to clipboard.', + selectedBlockClientIds.length + ), + selectedBlockClientIds.length + ); + } + createSuccessNotice( notice, { + type: 'snackbar', + } ); + }, [] ); +} diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index f3a7b84d5ded6..d538e37ad1ec8 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -10,10 +10,7 @@ import { useResourcePermissions, } from '@wordpress/core-data'; import { useMemo } from '@wordpress/element'; -import { - CopyHandler, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -107,7 +104,7 @@ export default function WidgetAreasBlockEditorProvider( { useSubRegistry={ false } { ...props } > - { children } + { children }