UNPKG

@gechiui/block-editor

Version:
235 lines (222 loc) 6.54 kB
/** * External dependencies */ import { castArray, uniq } from 'lodash'; /** * GeChiUI dependencies */ import { __, _n, sprintf } from '@gechiui/i18n'; import { DropdownMenu, ToolbarButton, ToolbarGroup, ToolbarItem, } from '@gechiui/components'; import { switchToBlockType, store as blocksStore, isReusableBlock, isTemplatePart, } from '@gechiui/blocks'; import { useSelect, useDispatch } from '@gechiui/data'; import { stack } from '@gechiui/icons'; /** * Internal dependencies */ import { store as blockEditorStore } from '../../store'; import useBlockDisplayInformation from '../use-block-display-information'; import BlockIcon from '../block-icon'; import BlockTitle from '../block-title'; import BlockTransformationsMenu from './block-transformations-menu'; import BlockStylesMenu from './block-styles-menu'; import PatternTransformationsMenu from './pattern-transformations-menu'; export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { const { replaceBlocks } = useDispatch( blockEditorStore ); const blockInformation = useBlockDisplayInformation( blocks[ 0 ].clientId ); const { possibleBlockTransformations, canRemove, hasBlockStyles, icon, blockTitle, patterns, } = useSelect( ( select ) => { const { getBlockRootClientId, getBlockTransformItems, __experimentalGetPatternTransformItems, } = select( blockEditorStore ); const { getBlockStyles, getBlockType } = select( blocksStore ); const { canRemoveBlocks } = select( blockEditorStore ); const rootClientId = getBlockRootClientId( castArray( clientIds )[ 0 ] ); 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 = uniq( blocks.map( ( { name } ) => name ) ).length === 1; // When selection consists of blocks of multiple types, display an // appropriate icon to communicate the non-uniformity. _icon = isSelectionOfSameType ? getBlockType( firstBlockName )?.icon : stack; } return { possibleBlockTransformations: getBlockTransformItems( blocks, rootClientId ), canRemove: canRemoveBlocks( clientIds, rootClientId ), hasBlockStyles: !! styles?.length, icon: _icon, blockTitle: getBlockType( firstBlockName )?.title, patterns: __experimentalGetPatternTransformItems( blocks, rootClientId ), }; }, [ clientIds, blocks, blockInformation?.icon ] ); const isReusable = blocks.length === 1 && isReusableBlock( blocks[ 0 ] ); const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] ); // Simple block tranformation based on the `Block Transforms` API. const onBlockTransform = ( name ) => replaceBlocks( clientIds, switchToBlockType( blocks, name ) ); // Pattern transformation through the `Patterns` API. const onPatternTransform = ( transformedBlocks ) => replaceBlocks( clientIds, transformedBlocks ); const hasPossibleBlockTransformations = !! possibleBlockTransformations.length && canRemove; const hasPatternTransformation = !! patterns?.length && canRemove; if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) { return ( <ToolbarGroup> <ToolbarButton disabled className="block-editor-block-switcher__no-switcher-icon" title={ blockTitle } icon={ <BlockIcon icon={ icon } showColors /> } /> </ToolbarGroup> ); } const blockSwitcherLabel = blockTitle; const blockSwitcherDescription = 1 === blocks.length ? sprintf( /* translators: %s: block title. */ __( '%s:更改区块类型或样式' ), blockTitle ) : sprintf( /* translators: %d: number of blocks. */ _n( '修改%d个区块的类型', 'Change type of %d blocks', blocks.length ), blocks.length ); const showDropDown = hasBlockStyles || hasPossibleBlockTransformations || hasPatternTransformation; return ( <ToolbarGroup> <ToolbarItem> { ( toggleProps ) => ( <DropdownMenu className="block-editor-block-switcher" label={ blockSwitcherLabel } popoverProps={ { position: 'bottom right', isAlternate: true, className: 'block-editor-block-switcher__popover', } } icon={ <> <BlockIcon icon={ icon } className="block-editor-block-switcher__toggle" showColors /> { ( isReusable || isTemplate ) && ( <span className="block-editor-block-switcher__toggle-text"> <BlockTitle clientId={ clientIds } /> </span> ) } </> } toggleProps={ { describedBy: blockSwitcherDescription, ...toggleProps, } } menuProps={ { orientation: 'both' } } > { ( { onClose } ) => showDropDown && ( <div className="block-editor-block-switcher__container"> { hasPatternTransformation && ( <PatternTransformationsMenu blocks={ blocks } patterns={ patterns } onSelect={ ( transformedBlocks ) => { onPatternTransform( transformedBlocks ); onClose(); } } /> ) } { hasPossibleBlockTransformations && ( <BlockTransformationsMenu className="block-editor-block-switcher__transforms__menugroup" possibleBlockTransformations={ possibleBlockTransformations } blocks={ blocks } onSelect={ ( name ) => { onBlockTransform( name ); onClose(); } } /> ) } { hasBlockStyles && ( <BlockStylesMenu hoveredBlock={ blocks[ 0 ] } onSwitch={ onClose } /> ) } </div> ) } </DropdownMenu> ) } </ToolbarItem> </ToolbarGroup> ); }; export const BlockSwitcher = ( { clientIds } ) => { const blocks = useSelect( ( select ) => select( blockEditorStore ).getBlocksByClientId( clientIds ), [ clientIds ] ); if ( ! blocks.length || blocks.some( ( block ) => ! block ) ) { return null; } return ( <BlockSwitcherDropdownMenu clientIds={ clientIds } blocks={ blocks } /> ); }; export default BlockSwitcher;