Skip to content

Custom block alignment

Mark Howells-Mead edited this page Nov 27, 2023 · 1 revision
var {
    __,
} = wp.i18n;

var {
    BlockControls,
    useBlockProps,
} = wp.blockEditor;

var {
    ToolbarDropdownMenu,
    ToolbarGroup,
    DropdownMenu,
    MenuGroup,
    MenuItem,
} = wp.components;

var {
    createHigherOrderComponent,
} = wp.compose;

var {
    getBlockSupport,
} = wp.blocks;

var {
    Fragment,
    createElement,
} = wp.element;

var el = createElement;

/*
* Disable default "align" to hide the core block alignment toolbar.
* Clone the block specific "align" supports values and store these in "alignCustom" instead.
*/

function modifyBlockAlignmentSupport( settings, name ) {
    
    if ( ! name.includes('acf/') ) {
        // Only modify our acf blocks
        return settings;
    }
    
    const hasAlignSupport = getBlockSupport( name, 'align', false );        
        
    if( ! hasAlignSupport ) {
        return settings;
    }   
    
    // Get the original align values from the block supports settings
    let originalAlignSupport = settings.supports.align;
    
    // Modify the default block supports settings
    newSettings = {
        ...settings,
        supports: {
            ...settings.supports,
            align: false, // <-- This will disable the default align to hide the core block alignment toolbar
            alignCustom: originalAlignSupport, // <-- We clone the default align and store it here instead
        }
    };
    
    return newSettings;
    
}

wp.hooks.addFilter( 'blocks.registerBlockType', 'plugin-name/modify-block-align-support', modifyBlockAlignmentSupport );

/*
* Function to handle the block alignment icons
*/

function blockAlignIcon( name ) {
    
    var icons = {
        'none': 'M5 15h14V9H5v6zm0 4.8h14v-1.5H5v1.5zM5 4.2v1.5h14V4.2H5z',
        'full': 'M5 4v11h14V4H5zm3 15.8h8v-1.5H8v1.5z',
        'wide': 'M5 9v6h14V9H5zm11-4.8H8v1.5h8V4.2zM8 19.8h8v-1.5H8v1.5z',
        'narrow': 'M5 9v6h14V9H5zm11-4.8H8v1.5h8V4.2zM8 19.8h8v-1.5H8v1.5z',
        'left': 'M4 9v6h14V9H4zm8-4.8H4v1.5h8V4.2zM4 19.8h8v-1.5H4v1.5z',
        'center': 'M7 9v6h10V9H7zM5 19.8h14v-1.5H5v1.5zM5 4.3v1.5h14V4.3H5z',
        'right': 'M6 15h14V9H6v6zm6-10.8v1.5h8V4.2h-8zm0 15.6h8v-1.5h-8v1.5z',
    }
    
    //var path = icons.name ? icons.name : icons.none;
    var path = icons[name];
    
    return el('svg', { width: 24, height: 24 },
        el('path', { d: path } )
    );
        
}

/*
* Filter to add custom block alignment toolbar controls
*/

var customBlockAlignmentControls = createHigherOrderComponent( function( BlockEdit ) {
    
    return function( props ) {
        
        const blockName = props.name;
        const currentAlign = props.attributes.align;
        const originalEdit = el( BlockEdit, props );        
        
        // Check for block align support        
        const blockAlignSupport = getBlockSupport( blockName, 'alignCustom', false );            
        
        // We add this custom controls to our acf blocks only
        if( ! blockName.includes('acf/') ) {
            // Return unmodified block edit
            return originalEdit;
        }

        // Do not add this custom controls if the block type has no align support
        if( ! blockAlignSupport ) {
            // Return unmodified block edit
            return originalEdit;
        }   
       
        // Get the current ToolbarDropdownMenu icon, depending on the selected align
        let currentIcon = currentAlign ? currentAlign : 'none';     
        currentIcon = blockAlignIcon( currentIcon );
        
        /*
        * Define a function to set the "align" attribute, after selecting a specific block alignment
        */
        
        function onChangeAlignment( newAlignment ) {
            
            let iconName = newAlignment;
            
            if( newAlignment === 'none' ) {
                // Because we don't want a "alignnone" classname in our block
                newAlignment = false;
            }

            // Change the block align attribute
            props.setAttributes( {
                align: newAlignment === undefined ? 'none' : newAlignment,
            } );
            
            // Change the current ToolbarDropdownMenu icon if the block align has been changed
            const alignToolbarButton = document.querySelector('[aria-label="Align"]');
            if( alignToolbarButton ) {
                let iconPath = blockAlignIcon( iconName );
                iconPath = iconPath.props.children.props.d;
                alignToolbarButton.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="' + iconPath + '"/></svg>';
            }

        }        
        
        /*
        * List all possible block alignments
        */
     
        const alignControls = {
            none: __('None', 'textdomain'),
            full: __('Full width', 'textdomain'),
            wide: __('Wide width', 'textdomain'),
            narrow: __('Narrow width', 'textdomain'),
            left: __('Align left', 'textdomain'),
            center: __('Align center', 'textdomain'),
            right: __('Align right', 'textdomain'),
        }
        
        /*
        * Build the toolbar block alignment controls depening on the align support of the block type
        */
            
        const allowedAlignControls = [];
        
        for( let key in alignControls ) {

            if( ! blockAlignSupport.includes( key ) && key !== 'none' ) {
                // [None] should be there all the time to reset other selected alignments
                // Only add the current align control if it's supported
                continue;
            }

            let controlTitle = alignControls[key];

            let newControl = { 
                title: controlTitle,
                icon: blockAlignIcon( key ),
                onClick: () => onChangeAlignment( key ),
            };

            allowedAlignControls.push(newControl);
            
        }
        
        /*
        * Re-Build the block toolbar and edit
        */
        
        return el(
            Fragment,
            {},
            el( BlockControls, { 
                    key: 'controls',
                    group: 'default',
                },
                el( ToolbarGroup, null,
                    el( ToolbarDropdownMenu, {
                        label: 'Align',
                        icon: currentIcon,
                        controls: allowedAlignControls,
                    } ),
                ),
            ), el( BlockEdit, props )
        ); 
        
    };
    
}, 'withInspectorControls' );

wp.hooks.addFilter( 'editor.BlockEdit', 'plugin-name/with-inspector-controls', customBlockAlignmentControls );
Clone this wiki locally