diff --git a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js index 0c2e3bfc821339..acfd59b8db8c5c 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js @@ -19,6 +19,7 @@ import { rawHandler, createBlock, isUnmodifiedDefaultBlock, + isReusableBlock, } from '@wordpress/blocks'; import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; @@ -26,6 +27,9 @@ import { withInstanceId, compose } from '@wordpress/compose'; import { moreHorizontalMobile } from '@wordpress/icons'; import { useRef, useState } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; +import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; +import { store as coreStore } from '@wordpress/core-data'; + /** * Internal dependencies */ @@ -41,11 +45,14 @@ const BlockActionsMenu = ( { isEmptyDefaultBlock, isFirst, isLast, + isReusableBlockType, + reusableBlock, rootClientId, selectedBlockClientId, selectedBlockPossibleTransformations, // Dispatch createSuccessNotice, + convertToRegularBlocks, duplicateBlock, onMoveDown, onMoveUp, @@ -175,6 +182,21 @@ const BlockActionsMenu = ( { ); }, }, + convertToRegularBlocks: { + id: 'convertToRegularBlocksOption', + label: __( 'Convert to regular blocks' ), + value: 'convertToRegularBlocksOption', + onSelect: () => { + createSuccessNotice( + sprintf( + /* translators: %s: name of the reusable block */ + __( '%s converted to regular blocks' ), + reusableBlock?.title?.raw || blockTitle + ) + ); + convertToRegularBlocks(); + }, + }, }; const options = compact( [ @@ -187,6 +209,7 @@ const BlockActionsMenu = ( { allOptions.cutButton, isPasteEnabled && allOptions.pasteButton, allOptions.duplicateButton, + isReusableBlockType && allOptions.convertToRegularBlocks, allOptions.delete, ] ); @@ -297,6 +320,15 @@ export default compose( ? getBlockTransformItems( [ selectedBlock ], rootClientId ) : []; + const isReusableBlockType = block ? isReusableBlock( block ) : false; + const reusableBlock = isReusableBlockType + ? select( coreStore ).getEntityRecord( + 'postType', + 'wp_block', + block?.attributes.ref + ) + : undefined; + return { blockTitle, canInsertBlockType, @@ -305,13 +337,19 @@ export default compose( isEmptyDefaultBlock, isFirst: firstIndex === 0, isLast: lastIndex === blockOrder.length - 1, + isReusableBlockType, + reusableBlock, rootClientId, selectedBlockClientId, selectedBlockPossibleTransformations, }; } ), withDispatch( - ( dispatch, { clientIds, rootClientId, currentIndex }, { select } ) => { + ( + dispatch, + { clientIds, rootClientId, currentIndex, selectedBlockClientId }, + { select } + ) => { const { moveBlocksDown, moveBlocksUp, @@ -319,6 +357,7 @@ export default compose( removeBlocks, insertBlock, replaceBlocks, + clearSelectedBlock, } = dispatch( blockEditorStore ); const { openGeneralSidebar } = dispatch( 'core/edit-post' ); const { getBlockSelectionEnd, getBlock } = select( @@ -326,8 +365,20 @@ export default compose( ); const { createSuccessNotice } = dispatch( noticesStore ); + const { + __experimentalConvertBlockToStatic: convertBlockToStatic, + } = dispatch( reusableBlocksStore ); + return { createSuccessNotice, + convertToRegularBlocks() { + clearSelectedBlock(); + // Convert action is executed at the end of the current JavaScript execution block + // to prevent issues related to undo/redo actions. + setImmediate( () => + convertBlockToStatic( selectedBlockClientId ) + ); + }, duplicateBlock() { return duplicateBlocks( clientIds ); },