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.
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
);