@wordpress/block-editor
Version:
215 lines (195 loc) • 7.76 kB
JavaScript
import { createElement, Fragment } from "@wordpress/element";
/**
* WordPress dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { DropdownMenu, ToolbarButton, ToolbarGroup, ToolbarItem } from '@wordpress/components';
import { switchToBlockType, store as blocksStore, isReusableBlock, isTemplatePart } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';
import { copy } from '@wordpress/icons';
/**
* Internal dependencies
*/
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 } from './block-variation-transformations';
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,
updateBlockAttributes
} = useDispatch(blockEditorStore);
const blockInformation = useBlockDisplayInformation(blocks[0].clientId);
const {
possibleBlockTransformations,
canRemove,
hasBlockStyles,
icon,
patterns
} = useSelect(select => {
const {
getBlockRootClientId,
getBlockTransformItems,
__experimentalGetPatternTransformItems,
canRemoveBlocks
} = select(blockEditorStore);
const {
getBlockStyles,
getBlockType
} = select(blocksStore);
const rootClientId = getBlockRootClientId(Array.isArray(clientIds) ? clientIds[0] : clientIds);
const [{
name: firstBlockName
}] = blocks;
const _isSingleBlockSelected = blocks.length === 1;
const styles = _isSingleBlockSelected && getBlockStyles(firstBlockName);
let _icon;
if (_isSingleBlockSelected) {
_icon = blockInformation?.icon; // Take into account active block variations.
} else {
const isSelectionOfSameType = new Set(blocks.map(({
name
}) => name)).size === 1; // When selection consists of blocks of multiple types, display an
// appropriate icon to communicate the non-uniformity.
_icon = isSelectionOfSameType ? getBlockType(firstBlockName)?.icon : copy;
}
return {
possibleBlockTransformations: getBlockTransformItems(blocks, rootClientId),
canRemove: canRemoveBlocks(clientIds, rootClientId),
hasBlockStyles: !!styles?.length,
icon: _icon,
patterns: __experimentalGetPatternTransformItems(blocks, rootClientId)
};
}, [clientIds, blocks, blockInformation?.icon]);
const blockVariationTransformations = useBlockVariationTransforms({
clientIds,
blocks
});
const blockTitle = useBlockDisplayTitle({
clientId: Array.isArray(clientIds) ? clientIds[0] : clientIds,
maximumLength: 35
});
const isReusable = blocks.length === 1 && isReusableBlock(blocks[0]);
const isTemplate = blocks.length === 1 && isTemplatePart(blocks[0]);
function selectForMultipleBlocks(insertedBlocks) {
if (insertedBlocks.length > 1) {
multiSelect(insertedBlocks[0].clientId, insertedBlocks[insertedBlocks.length - 1].clientId);
}
} // Simple block tranformation based on the `Block Transforms` API.
function onBlockTransform(name) {
const newBlocks = switchToBlockType(blocks, name);
replaceBlocks(clientIds, newBlocks);
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);
selectForMultipleBlocks(transformedBlocks);
}
/**
* The `isTemplate` check is a stopgap solution here.
* Ideally, the Transforms API should handle this
* by allowing to exclude blocks from wildcard transformations.
*/
const hasPossibleBlockTransformations = !!possibleBlockTransformations.length && canRemove && !isTemplate;
const hasPossibleBlockVariationTransformations = !!blockVariationTransformations?.length;
const hasPatternTransformation = !!patterns?.length && canRemove;
if (!hasBlockStyles && !hasPossibleBlockTransformations && !hasPossibleBlockVariationTransformations) {
return createElement(ToolbarGroup, null, createElement(ToolbarButton, {
disabled: true,
className: "block-editor-block-switcher__no-switcher-icon",
title: blockTitle,
icon: createElement(Fragment, null, createElement(BlockIcon, {
icon: icon,
showColors: true
}), (isReusable || isTemplate) && createElement("span", {
className: "block-editor-block-switcher__toggle-text"
}, blockTitle))
}));
}
const blockSwitcherLabel = blockTitle;
const blockSwitcherDescription = 1 === blocks.length ? sprintf(
/* translators: %s: block title. */
__('%s: Change block type or style'), blockTitle) : sprintf(
/* translators: %d: number of blocks. */
_n('Change type of %d block', 'Change type of %d blocks', blocks.length), blocks.length);
const hasBlockOrBlockVariationTransforms = hasPossibleBlockTransformations || hasPossibleBlockVariationTransformations;
const showDropDown = hasBlockStyles || hasBlockOrBlockVariationTransforms || hasPatternTransformation;
return createElement(ToolbarGroup, null, createElement(ToolbarItem, null, toggleProps => createElement(DropdownMenu, {
className: "block-editor-block-switcher",
label: blockSwitcherLabel,
popoverProps: {
position: 'bottom right',
variant: 'toolbar',
className: 'block-editor-block-switcher__popover'
},
icon: createElement(Fragment, null, createElement(BlockIcon, {
icon: icon,
className: "block-editor-block-switcher__toggle",
showColors: true
}), (isReusable || isTemplate) && createElement("span", {
className: "block-editor-block-switcher__toggle-text"
}, blockTitle)),
toggleProps: {
describedBy: blockSwitcherDescription,
...toggleProps
},
menuProps: {
orientation: 'both'
}
}, ({
onClose
}) => showDropDown && createElement("div", {
className: "block-editor-block-switcher__container"
}, hasPatternTransformation && createElement(PatternTransformationsMenu, {
blocks: blocks,
patterns: patterns,
onSelect: transformedBlocks => {
onPatternTransform(transformedBlocks);
onClose();
}
}), hasBlockOrBlockVariationTransforms && createElement(BlockTransformationsMenu, {
className: "block-editor-block-switcher__transforms__menugroup",
possibleBlockTransformations: possibleBlockTransformations,
possibleBlockVariationTransformations: blockVariationTransformations,
blocks: blocks,
onSelect: name => {
onBlockTransform(name);
onClose();
},
onSelectVariation: name => {
onBlockVariationTransform(name);
onClose();
}
}), hasBlockStyles && createElement(BlockStylesMenu, {
hoveredBlock: blocks[0],
onSwitch: onClose
})))));
};
export const BlockSwitcher = ({
clientIds
}) => {
const blocks = useSelect(select => select(blockEditorStore).getBlocksByClientId(clientIds), [clientIds]);
if (!blocks.length || blocks.some(block => !block)) {
return null;
}
return createElement(BlockSwitcherDropdownMenu, {
clientIds: clientIds,
blocks: blocks
});
};
export default BlockSwitcher;
//# sourceMappingURL=index.js.map