Ein Gutenberg Block Plugin erstellen

Bildschirmfoto Wordpress Gutenberg Block

Ich musste kurzfristig das Frontend für einen Wordpress-Website erstellen, da die Pages ausschließlich über Gutenberg erstellt werden und ich ein Design vorliegen hatte, das nicht in einem "Bezahl-Theme" abzubilden war, musste ich mich in den Layout-Builder einarbeiten. Anbei meine Erfahrungen und der ES5 Code, der dabei rausgefallen ist. Da ich kein Wordpress-Entwickler bin, empfehle ich den Code nur als erste Orientierung zu verwenden, tiefer einarbeiten könnt ihr euch hier:

developer.wordpress.org/block-editor/

Um das neue Plugin bei Wordpress zu registieren, in dem z.B. "custom-gutenberg-blocks" genannten Verzeichnis eine Datei erstellen, die nach dem Verzeichnis-Namen benannt wird (unter wp-content/plugins).

<?php
/**
 *
 * Plugin Name: Custom Gutenberg Blocks
 * Plugin URI: 
 * Description: Expanding Gutenberg Editor with theme specific blocks
 * Author: gnuschichten
 * Version: 1.0-dev
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * BLOCK: Bootstrap - Container and Row
 */
require_once('blocks/row-block/index.php');

/**
 * BLOCK:  Cards
 */
require_once('blocks/card-block/index.php');

Zuerst lege ich einen Block für die Bootstrap Row an. In dem Verzeichnis custom-gutenberg-blocks/row-block erstelle ich eine index.php Datei, die das Plugin um den Block erweitert.

<?php
/**
 * BLOCK: Row and Container block
 * 
 * Container block to be placed inside section block containing content blocks.
 *
 */

function row_block() {

    wp_register_script(
		'row-block-script', // Handle.
		plugins_url( 'block.js', __FILE__ ), // Block.js: We register the block here.
		array( 'wp-blocks', 'wp-element', 'wp-components', 'wp-editor' ) // Dependencies, defined above.
	);

	// Styles.
	wp_register_style(
		'row-block-editor-style', // Handle.
		plugins_url( 'editor.css', __FILE__ ), // Block editor CSS.
		array( 'wp-edit-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
	);
	wp_register_style(
		'row-block-style', // Handle.
		plugins_url( 'style.css', __FILE__ ), // Block editor CSS.
		array( 'wp-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
	);

	// Here we actually register the block with WP, again using our namespacing
	// We also specify the editor script to be used in the Gutenberg interface
	register_block_type( 'custom-gutenberg-blocks/row-block', array(
		'editor_script' => 'row-block-script',
		'editor_style' => 'row-block-editor-style',
		'style' => 'row-block-style',
	) );

}
add_action( 'init', 'row_block' );

In dem File werden übrigens auch zwei CSS-Files eingebunden, einmal für die Backend und ein weiteres für die Frontend Styles. Die Frontend Styles habe ich allerdings in meinem Projekt in das Theme ausgelagert.

In dem gleichen Verzeichnis wird nun die oben mit wp_register_script registrierte Datei "block.js" angelegt.

wp.blocks.registerBlockType( 'custom-gutenberg-blocks/row-block', {
    title: 'Bootstrap Row-Container',

    icon: 'grid-view',

    category: 'layout',

	attributes: {
		backgroundcolor: {
			type: 'string',
			default: '',
		}
	},

	// Backend Darstellung
    edit: function( props ) {

		var backgroundcolor = props.attributes.backgroundcolor;
		
		// Function für die Anpassung der Hintergrundfarbe
		function onChangeBackgroundColor( backgroundcolor ) {
			props.setAttributes( { backgroundcolor: backgroundcolor } );
		}

		return	wp.element.createElement(
			wp.element.Fragment,
			null,
			
			// Das Gutenberg Editor Element in der Seitenleiste
			// wird gerendert, hier mit der Möglichkeit die
			// Hintergrundfarbe im Backend anzupassen
			wp.element.createElement(
				wp.editor.InspectorControls,
				null,
				wp.element.createElement(
					'label',
					{
						className: 'inspectorcontrols-label'
					},
					'Background color'
				),
				wp.element.createElement(
					wp.editor.ColorPalette,
					{
						label: 'Background color',
						onChange: onChangeBackgroundColor,
						value: backgroundcolor,
					}
				),
			),
				
				// Der Darstellung im Gutenberg Layoutbuilder wird gerendert 
				wp.element.createElement( 'div', { className: props.className + ' row-container', style: { backgroundColor: backgroundcolor } }, [
					wp.element.createElement(
						'div',
						{
							className: 'container',
						},
						wp.element.createElement(
							'div',
							{
								className: 'row',
							},
							wp.element.createElement( wp.editor.InnerBlocks, { } ),
						),
					),
				]),

		);
	},
	
	save: function( props ) {

		var backgroundcolor = props.attributes.backgroundcolor;
		
		// Das Frontend wird gerendert
		return wp.element.createElement( 'div', { className: props.className + ' row-container', style: { backgroundColor: backgroundcolor } }, [
			wp.element.createElement(
				'div',
				{
					className: 'container',
				},
				wp.element.createElement(
					'div',
					{
						className: 'row',
					},
					wp.element.createElement( wp.editor.InnerBlocks.Content, { } ),
				),
			),
		]);
	},
} );

In diesem Block können später Bootstrap-Columns aus einem weiteren Block platziert werden, der im Folgenden angelegt wird. Dafür kann man einfach die Strukturen oben kopieren und nur jeweils den Namen des Blocks anpassen, zum Beispiel: custom-gutenberg-blocks/card-block. Wichtig, den Block-Namen "card-block", bzw. "card_block" in der index.php anpassen. Zuletzt wird im card-block Verzeichnis wieder eine Datei namens block.js angelegt.

wp.blocks.registerBlockType( 'gutenberg-block-extension/cards-block', {
    title: 'Vidone Cards',

    icon: 'welcome-widgets-menus',

    category: 'layout',

    attributes: {
        cardimage: {
            type: 'string',
            default: null,
        },
        cardtitle: {
            type: 'string',
            source: 'html',
            selector: 'h3',
        },
        cardintro: {
            type: 'string',
            source: 'html',
            selector: 'p',
        },
        cardurl: {
            type: 'string',
            default: '',
        },
        cardurlcontent: {
            type: 'string',
            source: 'html',
            selector: 'a',
        },
        cardclass: {
            type: 'string',
            default: 'card__container--bright',
        }
    },

    edit: function( props ) {

        var cardimage = props.attributes.cardimage;
        var cardtitle = props.attributes.cardtitle;
        var cardintro = props.attributes.cardintro;
        var cardurl = props.attributes.cardurl;
        var cardurlcontent = props.attributes.cardurlcontent;
        var cardclass = props.attributes.cardclass;

        function onImageSelect(imageObject) {
            props.setAttributes( {cardimage: imageObject.sizes.full.url} )
        }

        function onChangeTitle( cardtitle ) {
            props.setAttributes( { cardtitle: cardtitle } );
        }

        function onChangeIntro( cardintro ) {
            props.setAttributes( { cardintro: cardintro } );
        }
        
        function onChangeUrl( cardurl ) {
            props.setAttributes( { cardurl: cardurl } );
        }

        function onChangeUrlContent( cardurlcontent ) {
            props.setAttributes( { cardurlcontent: cardurlcontent } );
        }

        function onChangeClass( cardclass ) {
            props.setAttributes( { cardclass: cardclass } );
        }

        const getFirstImageButton = (openEvent) => {
            if (cardimage) {
                return (
                    wp.element.createElement(
                        'img',
                        {
                            src: cardimage,
                            onClick: openEvent,
                            className: 'cards-card__card',
                        }
                    )
                );
            }
            else {
                return (
                    wp.element.createElement(
                        'div',
                        {
                            className: 'button-container',
                        },
                        wp.element.createElement(
                            'button',
                            {
                                onClick: openEvent,
                                className: 'button button-large',
                                label: 'Upload media'
                            },
                            'Upload media'
                        ),
                    )
                );
            }
        };

        return (
            wp.element.createElement(
                wp.element.Fragment,
                null,
                wp.element.createElement(
                    wp.editor.InspectorControls,
                    null,
                    wp.element.createElement(
                        wp.editor.MediaUpload,
                        {
                            type: 'image',
                            value: cardimage,
                            onSelect: onImageSelect,
                            render: ({ open }) => getFirstImageButton(open),
                        }
                    ),
                    wp.element.createElement(
                        wp.components.TextControl,
                        {
                            label: 'Card 1: Link-URL',
                            onChange: onChangeUrl,
                            value: cardurl,
                        }
                    ),
                    wp.element.createElement(
                        wp.components.SelectControl,
                        {
                            label: 'Colors card',
                            onChange: onChangeClass,
                            value: cardclass,
                            options: [
                                { value: 'card__container--dark', label: 'Dark card' },
                                { value: 'card__container--bright', label: 'White card' },
                            ],
                        }
                    ),
                ),

                wp.element.createElement(
                    'div',
                    {
                        className: 'col-12 col-md-4 d-flex align-items-stretch',
                    },
                    wp.element.createElement(
                        'div',
                        {
                            className: 'card__container ' + cardclass,
                        },
                        wp.element.createElement(
                            'div',
                            {
                                className: 'card__img',
                                style: {
                                    backgroundImage: 'url("' + cardimage + '")',
                                    backgroundSize: 'contain'
                                }
                            },
                        ),
                        wp.element.createElement(
                            'div',
                            {
                                className: 'card__headline',
                            },
                            wp.element.createElement(
                                wp.editor.RichText,
                                {
                                    tagName: 'h3',
                                    onChange: onChangeTitle,
                                    value: cardtitle,
                                    placeholder: 'Title card'
                                }
                            ),
                        ),
                        wp.element.createElement(
                            'div',
                            {
                                className: 'card__intro',
                            },
                            wp.element.createElement(
                                wp.editor.RichText,
                                {
                                    tagName: 'p',
                                    onChange: onChangeIntro,
                                    value: cardintro,
                                    placeholder: 'Text Card'
                                }
                            ),
                        ),
                        wp.element.createElement(
                            'div',
                            {
                                className: 'card__link',
                            },
                            wp.element.createElement(
                                wp.editor.RichText,
                                {
                                    tagName: 'a',
                                    className: 'card-link',
                                    onChange: onChangeUrlContent,
                                    value: cardurlcontent,
                                    formattingControls: [],
                                    href: cardurl,
                                    placeholder: 'Link-Text card'
                                }
                            ),
                        ),
                    ),
                ),
            )
        );
    },

    save: function( props ) {

        var cardimage = props.attributes.cardimage;
        var cardtitle = props.attributes.cardtitle;
        var cardintro = props.attributes.cardintro;
        var cardurl = props.attributes.cardurl;
        var cardurlcontent = props.attributes.cardurlcontent;
        var cardclass = props.attributes.cardclass;

        return wp.element.createElement(
            'div',
            {
                className: 'col-12 col-md-4 d-flex align-items-stretch',
            },
            wp.element.createElement(
                'div',
                {
                    className: 'card__container ' + cardclass,
                },
                wp.element.createElement(
                    'div',
                    {
                        className: 'card__img',
                        style: {
                            backgroundImage: 'url("' + cardimage + '")',
                            backgroundSize: 'contain'
                        }
                    },
                ),
                wp.element.createElement(
                    'div',
                    {
                        className: 'card__headline',
                    },
                    wp.element.createElement(
                        wp.editor.RichText.Content,
                        {
                            tagName: 'h3',
                            value: cardtitle
                        }
                    ),
                ),
                wp.element.createElement(
                    'div',
                    {
                        className: 'card__intro',
                    },
                    wp.element.createElement(
                        wp.editor.RichText.Content,
                        {
                            tagName: 'p',
                            value: cardintro
                        }
                    ),
                ),
                wp.element.createElement(
                    wp.editor.RichText.Content,
                    {
                        tagName: 'a',
                        className: 'card-link',
                        value: cardurlcontent,
                        href: cardurl,
                        target: '_self',
                        rel: 'noopener noreferrer'
                    }
                ),
            ),
        );
    },
} );

Der Block bietet die Möglichkeit Titel, Content, Url und ein Bild zu pflegen.