diff --git a/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js b/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js
new file mode 100644
index 0000000000000..f541f31e74a82
--- /dev/null
+++ b/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js
@@ -0,0 +1,114 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { MenuGroup, MenuItem } from '@wordpress/components';
+import {
+ getBlockMenuDefaultClassName,
+ cloneBlock,
+ store as blocksStore,
+} from '@wordpress/blocks';
+import { useSelect } from '@wordpress/data';
+import { useState, useMemo } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { store as blockEditorStore } from '../../store';
+import BlockIcon from '../block-icon';
+import PreviewBlockPopover from './preview-block-popover';
+
+export function useBlockVariationTransforms( { clientIds, blocks } ) {
+ const { activeBlockVariation, blockVariationTransformations } = useSelect(
+ ( select ) => {
+ const {
+ getBlockRootClientId,
+ getBlockAttributes,
+ canRemoveBlocks,
+ } = select( blockEditorStore );
+ const { getActiveBlockVariation, getBlockVariations } =
+ select( blocksStore );
+ const rootClientId = getBlockRootClientId(
+ Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
+ );
+ const canRemove = canRemoveBlocks( clientIds, rootClientId );
+ // Only handle single selected blocks for now.
+ if ( blocks.length !== 1 || ! canRemove ) {
+ return;
+ }
+ const [ firstBlock ] = blocks;
+ return {
+ blockVariationTransformations: getBlockVariations(
+ firstBlock.name,
+ 'transform'
+ ),
+ activeBlockVariation: getActiveBlockVariation(
+ firstBlock.name,
+ getBlockAttributes( firstBlock.clientId )
+ ),
+ };
+ },
+ [ clientIds, blocks ]
+ );
+ const transformations = useMemo( () => {
+ return blockVariationTransformations?.filter(
+ ( { name } ) => name !== activeBlockVariation?.name
+ );
+ }, [ blockVariationTransformations, activeBlockVariation ] );
+ return transformations;
+}
+
+const BlockVariationTransformationsMenu = ( {
+ transformations,
+ onSelect,
+ blocks,
+} ) => {
+ const [ hoveredTransformItemName, setHoveredTransformItemName ] =
+ useState();
+ return (
+
+ { hoveredTransformItemName && (
+ name === hoveredTransformItemName
+ ).attributes
+ ) }
+ />
+ ) }
+ { transformations?.map( ( item ) => (
+
+ ) ) }
+
+ );
+};
+
+function BlockVariationTranformationItem( {
+ item,
+ onSelect,
+ setHoveredTransformItemName,
+} ) {
+ const { name, icon, title } = item;
+ return (
+
+ );
+}
+
+export default BlockVariationTransformationsMenu;
diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js
index 696b097757d33..a397d7c87c01a 100644
--- a/packages/block-editor/src/components/block-switcher/index.js
+++ b/packages/block-editor/src/components/block-switcher/index.js
@@ -24,12 +24,17 @@ import { store as blockEditorStore } from '../../store';
import useBlockDisplayInformation from '../use-block-display-information';
import BlockIcon from '../block-icon';
import BlockTransformationsMenu from './block-transformations-menu';
+import {
+ useBlockVariationTransforms,
+ default as BlockVariationTransformationsMenu,
+} from './block-variation-transformations-menu';
import BlockStylesMenu from './block-styles-menu';
import PatternTransformationsMenu from './pattern-transformations-menu';
import useBlockDisplayTitle from '../block-title/use-block-display-title';
export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
- const { replaceBlocks, multiSelect } = useDispatch( blockEditorStore );
+ const { replaceBlocks, multiSelect, updateBlockAttributes } =
+ useDispatch( blockEditorStore );
const blockInformation = useBlockDisplayInformation( blocks[ 0 ].clientId );
const {
possibleBlockTransformations,
@@ -43,9 +48,9 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
getBlockRootClientId,
getBlockTransformItems,
__experimentalGetPatternTransformItems,
+ canRemoveBlocks,
} = select( blockEditorStore );
const { getBlockStyles, getBlockType } = select( blocksStore );
- const { canRemoveBlocks } = select( blockEditorStore );
const rootClientId = getBlockRootClientId(
Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
);
@@ -82,6 +87,11 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
[ clientIds, blocks, blockInformation?.icon ]
);
+ const blockVariationTransformations = useBlockVariationTransforms( {
+ clientIds,
+ blocks,
+ } );
+
const blockTitle = useBlockDisplayTitle( {
clientId: Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds,
maximumLength: 35,
@@ -105,6 +115,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
selectForMultipleBlocks( newBlocks );
}
+ function onBlockVariationTransform( name ) {
+ updateBlockAttributes( blocks[ 0 ].clientId, {
+ ...blockVariationTransformations.find(
+ ( { name: variationName } ) => variationName === name
+ ).attributes,
+ } );
+ }
+
// Pattern transformation through the `Patterns` API.
function onPatternTransform( transformedBlocks ) {
replaceBlocks( clientIds, transformedBlocks );
@@ -118,8 +136,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
*/
const hasPossibleBlockTransformations =
!! possibleBlockTransformations.length && canRemove && ! isTemplate;
+ const hasPossibleBlockVariationTransformations =
+ !! blockVariationTransformations?.length;
const hasPatternTransformation = !! patterns?.length && canRemove;
- if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) {
+ if (
+ ! hasBlockStyles &&
+ ! hasPossibleBlockTransformations &&
+ ! hasPossibleBlockVariationTransformations
+ ) {
return (
{
const showDropDown =
hasBlockStyles ||
hasPossibleBlockTransformations ||
+ hasPossibleBlockVariationTransformations ||
hasPatternTransformation;
return (
@@ -226,6 +251,20 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
} }
/>
) }
+ { hasPossibleBlockVariationTransformations && (
+ {
+ onBlockVariationTransform(
+ name
+ );
+ onClose();
+ } }
+ />
+ ) }
{ hasBlockStyles && (