diff --git a/packages/block-editor/src/components/block-switcher/block-styles-menu.js b/packages/block-editor/src/components/block-switcher/block-styles-menu.js
new file mode 100644
index 00000000000000..01d47a66e462f1
--- /dev/null
+++ b/packages/block-editor/src/components/block-switcher/block-styles-menu.js
@@ -0,0 +1,36 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { MenuGroup } from '@wordpress/components';
+import { useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import BlockStyles from '../block-styles';
+import PreviewBlockPopover from './preview-block-popover';
+
+const BlockStylesMenu = ( { hoveredBlock, onSwitch } ) => {
+ const [ hoveredClassName, setHoveredClassName ] = useState();
+ return (
+
+ { hoveredClassName && (
+
+ ) }
+
+
+ );
+};
+export default BlockStylesMenu;
diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js
index e1444dcde54689..6694ac1795092f 100644
--- a/packages/block-editor/src/components/block-switcher/index.js
+++ b/packages/block-editor/src/components/block-switcher/index.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { castArray, filter, mapKeys, orderBy, uniq, map } from 'lodash';
+import { castArray, filter, mapKeys, orderBy, uniq } from 'lodash';
/**
* WordPress dependencies
@@ -12,247 +12,155 @@ import {
ToolbarButton,
ToolbarGroup,
ToolbarItem,
- MenuGroup,
- Popover,
} from '@wordpress/components';
import {
- getBlockType,
getPossibleBlockTransformations,
switchToBlockType,
- cloneBlock,
- getBlockFromExample,
store as blocksStore,
} from '@wordpress/blocks';
-import { Component } from '@wordpress/element';
-import { withSelect, withDispatch } from '@wordpress/data';
-import { compose } from '@wordpress/compose';
+import { useSelect, useDispatch } from '@wordpress/data';
import { stack } from '@wordpress/icons';
/**
* Internal dependencies
*/
import BlockIcon from '../block-icon';
-import BlockStyles from '../block-styles';
-import BlockPreview from '../block-preview';
import BlockTransformationsMenu from './block-transformations-menu';
+import BlockStylesMenu from './block-styles-menu';
+import useMatchingVariationInformation from '../use-matching-variation-information';
-function PreviewBlockPopover( { hoveredBlock, hoveredClassName } ) {
- const hoveredBlockType = getBlockType( hoveredBlock.name );
- return (
-
-
-
-
-
- { __( 'Preview' ) }
-
-
-
-
-
-
+const BlockSwitcher = ( { clientIds } ) => {
+ const blocks = useSelect(
+ ( select ) =>
+ select( 'core/block-editor' ).getBlocksByClientId( clientIds ),
+ [ clientIds ]
);
-}
-
-export class BlockSwitcher extends Component {
- constructor() {
- super( ...arguments );
- this.state = {
- hoveredClassName: null,
- };
- this.onHoverClassName = this.onHoverClassName.bind( this );
- }
-
- onHoverClassName( className ) {
- this.setState( { hoveredClassName: className } );
- }
-
- render() {
- const {
- blocks,
- onTransform,
- inserterItems,
- hasBlockStyles,
- } = this.props;
- const { hoveredClassName } = this.state;
-
- if ( ! Array.isArray( blocks ) || ! blocks.length ) {
- return null;
- }
+ return blocks?.length ? (
+
+ ) : null;
+};
- const [ hoveredBlock ] = blocks;
- const itemsByName = mapKeys( inserterItems, ( { name } ) => name );
- const possibleBlockTransformations = orderBy(
- filter(
- getPossibleBlockTransformations( blocks ),
- ( block ) => block && !! itemsByName[ block.name ]
- ),
- ( block ) => itemsByName[ block.name ].frecency,
- 'desc'
- );
-
- // When selection consists of blocks of multiple types, display an
- // appropriate icon to communicate the non-uniformity.
- const isSelectionOfSameType =
- uniq( map( blocks, 'name' ) ).length === 1;
-
- const blockIconProps = isSelectionOfSameType
- ? { clientId: hoveredBlock.clientId }
- : { icon: stack };
-
- const hasPossibleBlockTransformations = !! possibleBlockTransformations.length;
-
- if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) {
- return (
-
- }
- />
-
+const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
+ const { replaceBlocks } = useDispatch( 'core/block-editor' );
+ const { inserterItems, hasBlockStyles } = useSelect(
+ ( select ) => {
+ const { getBlockRootClientId, getInserterItems } = select(
+ 'core/block-editor'
+ );
+ const { getBlockStyles } = select( blocksStore );
+ const rootClientId = getBlockRootClientId(
+ castArray( clientIds )[ 0 ]
);
- }
+ const firstBlock = blocks.length === 1 ? blocks[ 0 ] : null;
+ const styles = firstBlock && getBlockStyles( firstBlock.name );
+ return {
+ inserterItems: getInserterItems( rootClientId ),
+ hasBlockStyles: !! styles?.length,
+ };
+ },
+ [ clientIds, blocks ]
+ );
+ const [ hoveredBlock ] = blocks;
+ const blockInformation = useMatchingVariationInformation(
+ hoveredBlock.clientId
+ );
+ const onTransform = ( name ) =>
+ replaceBlocks( clientIds, switchToBlockType( blocks, name ) );
- const blockSwitcherLabel =
- 1 === blocks.length
- ? __( 'Change block type or style' )
- : sprintf(
- /* translators: %s: number of blocks. */
- _n(
- 'Change type of %d block',
- 'Change type of %d blocks',
- blocks.length
- ),
- blocks.length
- );
+ // When selection consists of blocks of multiple types, display an
+ // appropriate icon to communicate the non-uniformity.
+ const isSelectionOfSameType =
+ uniq( blocks.map( ( { name } ) => name ) ).length === 1;
+ const icon = isSelectionOfSameType ? blockInformation.icon : stack;
+ const itemsByName = mapKeys( inserterItems, ( { name } ) => name );
+ const possibleBlockTransformations = orderBy(
+ filter(
+ getPossibleBlockTransformations( blocks ),
+ ( block ) => block && !! itemsByName[ block.name ]
+ ),
+ ( block ) => itemsByName[ block.name ].frecency,
+ 'desc'
+ );
+ const hasPossibleBlockTransformations = !! possibleBlockTransformations.length;
+ if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) {
return (
-
- { ( toggleProps ) => (
-
- }
- toggleProps={ toggleProps }
- menuProps={ { orientation: 'both' } }
- >
- { ( { onClose } ) =>
- ( hasBlockStyles ||
- hasPossibleBlockTransformations ) && (
-
- { hasPossibleBlockTransformations && (
-
{
- onTransform( blocks, name );
- onClose();
- } }
- />
- ) }
- { hasBlockStyles && (
-
- { hoveredClassName !== null && (
-
- ) }
-
-
- ) }
-
- )
- }
-
- ) }
-
+ }
+ />
);
}
-}
-export default compose(
- withSelect( ( select, { clientIds } ) => {
- const {
- getBlocksByClientId,
- getBlockRootClientId,
- getInserterItems,
- } = select( 'core/block-editor' );
- const { getBlockStyles } = select( blocksStore );
- const rootClientId = getBlockRootClientId(
- castArray( clientIds )[ 0 ]
- );
- const blocks = getBlocksByClientId( clientIds );
- const firstBlock = blocks && blocks.length === 1 ? blocks[ 0 ] : null;
- const styles = firstBlock && getBlockStyles( firstBlock.name );
- return {
- blocks,
- inserterItems: getInserterItems( rootClientId ),
- hasBlockStyles: styles && styles.length > 0,
- };
- } ),
- withDispatch( ( dispatch, ownProps ) => ( {
- onTransform( blocks, name ) {
- dispatch( 'core/block-editor' ).replaceBlocks(
- ownProps.clientIds,
- switchToBlockType( blocks, name )
- );
- },
- } ) )
-)( BlockSwitcher );
+ const blockSwitcherLabel =
+ 1 === blocks.length
+ ? __( 'Change block type or style' )
+ : sprintf(
+ /* translators: %s: number of blocks. */
+ _n(
+ 'Change type of %d block',
+ 'Change type of %d blocks',
+ blocks.length
+ ),
+ blocks.length
+ );
+
+ return (
+
+
+ { ( toggleProps ) => (
+
+ }
+ toggleProps={ toggleProps }
+ menuProps={ { orientation: 'both' } }
+ >
+ { ( { onClose } ) =>
+ ( hasBlockStyles ||
+ hasPossibleBlockTransformations ) && (
+
+ { hasPossibleBlockTransformations && (
+ {
+ onTransform( name );
+ onClose();
+ } }
+ />
+ ) }
+ { hasBlockStyles && (
+
+ ) }
+
+ )
+ }
+
+ ) }
+
+
+ );
+};
+
+export default BlockSwitcher;
diff --git a/packages/block-editor/src/components/block-switcher/preview-block-popover.js b/packages/block-editor/src/components/block-switcher/preview-block-popover.js
new file mode 100644
index 00000000000000..4e8e0cd0b66842
--- /dev/null
+++ b/packages/block-editor/src/components/block-switcher/preview-block-popover.js
@@ -0,0 +1,57 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Popover } from '@wordpress/components';
+import {
+ getBlockType,
+ cloneBlock,
+ getBlockFromExample,
+} from '@wordpress/blocks';
+/**
+ * Internal dependencies
+ */
+import BlockPreview from '../block-preview';
+
+export default function PreviewBlockPopover( {
+ hoveredBlock,
+ hoveredClassName,
+} ) {
+ const hoveredBlockType = getBlockType( hoveredBlock.name );
+ return (
+
+
+
+
+
+ { __( 'Preview' ) }
+
+
+
+
+
+
+ );
+}