WordPress Core-Blocks erweitern

Hey, hey! Du möchtest also einem Core-Block eine zusätzliche Option verpassen? Kein Problem! Mit einem selbstgeschriebenen Plugin ist das kinderleicht. Wenn du Docker bereits auf deinem System installiert hast, geht das auch noch super schnell! Falls du Docker noch nicht installiert hast, dann mach dir keine Sorgen. Hier findest du eine einfache Anleitung, wie das geht.

Wir wollten dem Core-Block Überschrift eine Option mitgeben, um diese bei Bedarf zu animieren. Die Option sollte in der Admin-Sidebar wählbar sein, verschiedene Einstellungen ermöglichen und im Frontend dem Element die entsprechenden CSS-Klassen hinzufügen.

Digitaler Auftritt für Kultur und Wirtschaft | Webdesign | Schwyz | Zug | Luzern | Altdorf | ulrich.digital
Die Option Animation V1 mit drei Einstellungen

Um den eigentlichen Block zu erstellen, haben wir das Tool Create-Guten-Block von Ahmad Awais benutzt, es kann hier heruntergeladen werden. Wie dieses Tool eingesetzt wird, kann auf Github direkt, oder in unserer Beschreibung hier nachgelesen werden.

Zuerst werden die Komponenten definiert, welche die Option enthalten soll.

import classnames from 'classnames';

const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { InspectorAdvancedControls } = wp.blockEditor;
const { createHigherOrderComponent } = wp.compose;
const { ToggleControl } = wp.components;
const { RadioControl } = wp.components;
const { useState } = wp.element;

Dann haben definiert, bei welchen Blöcken unsere Option vorhanden sein soll:

const allowedBlocks = [ 'core/paragraph', 'core/heading' ];

Die Funktion addAttributes wird zwei Attribute festlegen. Wir prüfen sicherheitshalber die Abwärtskompatibilität mit älteren Gutenberg-Versionen und ob es sich um die vorhin festgelegten Block-Types handelt.

Wenn dem so ist, werden die beiden Attribute isAnimated und radioAnimation definiert. isAnimated wird dann für den Toggle-Button gebraucht ist ist vom Typ Boolean, radioAnimation wird einen String als Wert besitzen können.

function addAttributes( settings ) {
	if( typeof settings.attributes !== 'undefined' && allowedBlocks.includes( settings.name ) ){
		settings.attributes = Object.assign( settings.attributes, {
			isAnimated:{ 
				type: 'boolean',
				default: false,
			},
			radioAnimation:{
				type: 'string',
				default: 'once',
			}
		});
	}
	return settings;
}

Nun werden die Kontroll-Buttons im Advanced Block Panel angehängt. Die eigentliche Ausgabe in der Sidebar wird im <Fragment>-Tag definiert und sollte mehr oder weniger selbsterklärend sein.

const withAdvancedControls = createHigherOrderComponent( ( BlockEdit ) => {
	return ( props ) => {

		const {
			name,
			attributes,
			setAttributes,
			isSelected,
		} = props;

		const {
			isAnimated,
			radioAnimation,
		} = attributes;
			
		return (
			<Fragment>
				<BlockEdit {...props} />
				{ isSelected && allowedBlocks.includes( name ) &&
					<InspectorAdvancedControls>
						<ToggleControl
							label={ __( 'Animation V1' ) }
							checked={ !! isAnimated }
							onChange={ () => setAttributes( {  isAnimated: ! isAnimated } ) }
							help={ !! isAnimated ? __( 'Ist animiert.' ) : __( 'Ist nicht animiert.' ) }
						/>
                        
                        { isAnimated && (
                            <RadioControl
                                label="Verhalten"
                                selected={ radioAnimation }
                                options={ [
                                    { label: 'Nur einmal', value: 'once' },
                                    { label: 'Bei jedem Scroll-In', value: 'scroll_in_out' },
                                    { label: 'Loop', value: 'loop' },
                                ] }
                                onChange={ ( option ) => { setAttributes( { radioAnimation: option } ) } }
                                help="Wann soll der Text animiert werden?"
                            />
                        )}
                        
					</InspectorAdvancedControls>
				}
			</Fragment>
		);
	};
}, 'withAdvancedControls');

Die Funktion applyExtraClass hängt – je nach gewählter Option – dem Block die entsprechenden CSS-Klassen an.

function applyExtraClass( extraProps, blockType, attributes ) {

	const { isAnimated } = attributes;
 	const { radioAnimation } = attributes;

	if ( typeof isAnimated !== 'undefined' && isAnimated && allowedBlocks.includes( blockType.name ) ) {
		extraProps.className = classnames( extraProps.className, 'animation_v1' );
        
        if ( typeof radioAnimation !== 'undefined' && radioAnimation && allowedBlocks.includes( blockType.name ) ) {
            extraProps.className = classnames( extraProps.className, radioAnimation );
	       }
    }    

	return extraProps;
}

Nun werden drei Filter angehängt. Bei der Block-Registration werden unsere Attribute hinzugefügt, über den Aufruf unserer Funktion addAttributes. Beim Block editieren werden unsere Kontroll-Buttons aufgerufen, und schliesslich beim Speichern unsere Funktion applyExtraClass, welche die entsprechenden Klassen dem Block anhängt.

addFilter(
	'blocks.registerBlockType',
	'block-custom-block-ulrich-digital/custom-attributes',
	addAttributes
);

addFilter(
	'editor.BlockEdit',
	'block-custom-block-ulrich-digital/custom-advanced-control',
	withAdvancedControls
);

addFilter(
	'blocks.getSaveContent.extraProps',
	'block-custom-block-ulrich-digital/applyExtraClass',
	applyExtraClass
);

Hier der gesamte Code zusammengefasst und auskommentiert:

/**
 * External Dependencies
 */
import classnames from 'classnames';

/**
 * WordPress Dependencies
 */
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment }	= wp.element;
const { InspectorAdvancedControls } = wp.blockEditor;
const { createHigherOrderComponent } = wp.compose;
const { ToggleControl } = wp.components;
const { RadioControl } = wp.components;
const { useState } = wp.element;

//restrict to specific block names
const allowedBlocks = [ 'core/paragraph', 'core/heading' ];


/**
 * Add custom attributes
 * @param {Object} settings Settings for the block.
 * @return {Object} settings Modified settings.
 */
 
function addAttributes( settings ) {
	
	//check if object exists for old Gutenberg version compatibility
	//add allowedBlocks restriction
	if( typeof settings.attributes !== 'undefined' && allowedBlocks.includes( settings.name ) ){
	
		settings.attributes = Object.assign( settings.attributes, {
			isAnimated:{ 
				type: 'boolean',
				default: false,
			},
            radioAnimation:{
                type: 'string',
                default: 'once',
            }
		});
	}
	return settings;
}



/**
 * Add animation controls on Advanced Block Panel.
 *
 * @param {function} BlockEdit Block edit component.
 *
 * @return {function} BlockEdit Modified block edit component.
 */
const withAdvancedControls = createHigherOrderComponent( ( BlockEdit ) => {
	return ( props ) => {

		const {
			name,
			attributes,
			setAttributes,
			isSelected,
		} = props;

		const {
			isAnimated,
            radioAnimation,
		} = attributes;
		
		
		return (
			<Fragment>
				<BlockEdit {...props} />
				{ isSelected && allowedBlocks.includes( name ) &&
					<InspectorAdvancedControls>
						<ToggleControl
							label={ __( 'Animation V1' ) }
							checked={ !! isAnimated }
							onChange={ () => setAttributes( {  isAnimated: ! isAnimated } ) }
							help={ !! isAnimated ? __( 'Ist animiert.' ) : __( 'Ist nicht animiert.' ) }
						/>
                        
                        { isAnimated && (
                            <RadioControl
                                label="Verhalten"
                                selected={ radioAnimation }
                                options={ [
                                    { label: 'Nur einmal', value: 'once' },
                                    { label: 'Bei jedem Scroll-In', value: 'scroll_in_out' },
                                    { label: 'Loop', value: 'loop' },
                                ] }
                                onChange={ ( option ) => { setAttributes( { radioAnimation: option } ) } }
                                help="Wann soll der Text animiert werden?"
                            />
                        )}
                        
					</InspectorAdvancedControls>
				}
			</Fragment>
		);
	};
}, 'withAdvancedControls');

/**
 * Add custom element class in save element.
 *
 * @param {Object} extraProps     Block element.
 * @param {Object} blockType      Blocks object.
 * @param {Object} attributes     Blocks attributes.
 *
 * @return {Object} extraProps Modified block element.
 */
function applyExtraClass( extraProps, blockType, attributes ) {

	const { isAnimated } = attributes;
    const { radioAnimation } = attributes;

	if ( typeof isAnimated !== 'undefined' && isAnimated && allowedBlocks.includes( blockType.name ) ) {
        // add Class animation_v1, if selected
		extraProps.className = classnames( extraProps.className, 'animation_v1' );
        
        // add String, if isAnimated is true
        if ( typeof radioAnimation !== 'undefined' && radioAnimation && allowedBlocks.includes( blockType.name ) ) {
            extraProps.className = classnames( extraProps.className, radioAnimation );
	       }
    }    

	return extraProps;
}

//add filters

addFilter(
	'blocks.registerBlockType',
	'block-custom-block-ulrich-digital/custom-attributes',
	addAttributes
);

addFilter(
	'editor.BlockEdit',
	'block-custom-block-ulrich-digital/custom-advanced-control',
	withAdvancedControls
);

addFilter(
	'blocks.getSaveContent.extraProps',
	'block-custom-block-ulrich-digital/applyExtraClass',
	applyExtraClass
);
1 Star2 Stars3 Stars4 Stars5 Stars (0)

Wenn du denkst, dass dieser Beitrag ein Meisterwerk der Webentwicklung ist, dann hinterlasse uns einen Kommentar. Wenn nicht, kannst du trotzdem kommentieren - wir akzeptieren auch konstruktive Kritik!

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert